Archive for the 'ruby on rails' Category

Distributing Rails Applications, with migrations

Creating a executable file (.exe for windows for example) from a Ruby on Rails application isn’t something really new. There enough informations about that on the net, the best one can be found here.
Well, then why am I writing this blog? Well the manual from Erik Veenstra ist quite good, but what I didn’t find there anywhere was how to get the migrations done! I want to be able to give this .exe file to someone (or actually I was forced to do it this way) and they should be able to use the application, but when you keep updates in mind then you must have some way to also get migrations working. 

I will be repeating some steps that are already mentioned in the “Distributing Rails Applications” tutorial.
First add this to the top of your environment.rb, this is a bit different then mentioned in the tutorial. Basically it should be working as mentioned in the tutorial, but in the last two days I was trying to solve this problem until late in the night and can’t really remember why I changed it :)

module Rails
  class Configuration
    def database_configuration
      conf = YAML::load(ERB.new(IO.read(database_configuration_file)).result)
      if defined?(TAR2RUBYSCRIPT)
        conf.each do |k, v|
          if v["adapter"] =~ /^sqlite3/
            v["database"] = oldlocation(v["database"]) if v.include?("database")
            v["dbfile"]   = oldlocation(v["dbfile"])   if v.include?("dbfile")
          end
        end
      else
        YAML::load(ERB.new(IO.read(database_configuration_file)).result)
      end
    end
  end
end

Create a init.rn in the root of your application folder, you can remove the three “puts” lines, just added them for debug purposes.

at_exit do
  require "irb"
  require "drb/acl"
  require "sqlite3"
end
 
puts "Running as an RBA." if defined?(TAR2RUBYSCRIPT)
puts "Runing from: " + oldlocation
puts "Runing in: " + newlocation
 
load "mig.rb"
load "script/server"

As you can see there is a new “load” line which load mig.rb before starting the server. Here is where the magic happens.
Actually the mig.rb is nothing but a copy of the rake script with a extra line.

require 'rubygems'
require 'rake'
 
version = ">= 0"
 
ARGV<<"db:migrate"
 
gem 'rake', version
load 'rake'

So what happens is that tar2rubyscript executes init.rb on startup, then the mig.rb is called which always does a rake db:migrate. This is done but manually adding “db:migrate” to ARGV, this has to be done so the script can be started with “load”, starting the script with a system command won’t work!
This is because the environment set up by tar2rubyscript will be missing and your migrations just won’t work.

Thats all! Incredible that for this answer I spend 2 long long nights. But now it works as it should, when starting it without tar2rubyscript it work’s as usual and with tar2rubyscript it works also great and I can have migrations when I send out new software versions. 
The idea is that the same application is intended to run in two different environments, one being a usual ruby on rails environment on Linux with a MySQL Database and the other being sent out via CD and it must run on windows.
I could have made complete installer to install Ruby, Rubygems, SQLite, the Gems I need … and much more. This way it is much much easier and nobody has to install anything, just click the exe, open the browser and start working.

[HowTo] Installing Ruby Enterprise Edition and Passenger on Debian Etch

Install some stuff we will need

apt-get install build-essential apache2 ruby1.8 zlib1g-dev libssl-dev mysql-server mysql-common libmysqlclient15-dev libmysqlclient15off apache2-prefork-dev

Create a link for ruby else the installer won’t work

ln -s /usr/bin/ruby1.8 /usr/bin/ruby

get Ruby Enterprise edition (http://www.rubyenterpriseedition.com/download.html)

tar xzvf ruby-enterprise-X.X.X.tar.gz
./ruby-enterprise-X.X.X/installer

Delete the Ruby link and create new links

rm /usr/bin/ruby
ln -s /opt/ruby-enterprise-1.8.6-20081215/bin/rake /usr/bin/rake
ln -s /opt/ruby-enterprise-1.8.6-20081215/bin/gem /usr/bin/gem 
ln -s /opt/ruby-enterprise-1.8.6-20081215/bin/rails /usr/bin/rails
ln -s /opt/ruby-enterprise-1.8.6-20081215/bin/ruby /usr/bin/ruby

Install Passenger

/opt/ruby-enterprise-1.8.6-20081215/bin/passenger-install-apache2-module

if you have this error

cd /opt/ruby-enterprise-1.8.6-20081215/lib/ruby/gems/1.8/gems/passenger-2.0.6
/opt/ruby-enterprise-1.8.6-20081215/bin/ruby -S rake clean apache2
/opt/ruby-enterprise-1.8.6-20081215/bin/ruby: No such file or directory -- rake (LoadError)

then it looks like the Rake GEM was not installed, just install it now. and don’t forget to create the symlink in /usr/bin !

/opt/ruby-enterprise-1.8.6-20081215/bin/gem install rake

Add this to /etc/apache2/apache2.conf

LoadModule passenger_module /opt/ruby-enterprise-1.8.6-20081215/lib/ruby/gems/1.8/gems/passenger-2.0.6/ext/apache2/mod_passenger.so
PassengerRoot /opt/ruby-enterprise-1.8.6-20081215/lib/ruby/gems/1.8/gems/passenger-2.0.6
PassengerRuby /opt/ruby-enterprise-1.8.6-20081215/bin/ruby

That’s it ! Now you can configure your vhosts.

<VirtualHost 192.168.0.1:80>
  ServerName foo.com
  DocumentRoot /var/rails/
 
  RailsBaseURI /project1
  RailsBaseURI /project2
  RailsBaseURI /project3
 
  Alias /docs "/opt/ruby-enterprise-1.8.6-20081215/lib/ruby/gems/1.8/gems/passenger-2.0.6/doc/Users guide.html"
</VirtualHost>

An finaly, the capistrano config in deploy.rb

set :application, "project1"
set :repository,  "https://svn.foo.com/project1"
set :user, 'root'
set :use_sudo, false
# If you aren't deploying to /u/apps/#{application} on the target
# servers (which is the default), you can specify the actual location
# via the :deploy_to variable:
set :deploy_to, "/var/rails/#{application}"
 
after "deploy:update_code", "deploy:chown"
 
role :app, "foo.com"
role :web, "foo.com"
role :db,  "foo.com", :primary => true
 
namespace :deploy do
  desc "Change owner"
  task :chown, :roles => :app do
    run "chown www-data:www-data -R #{latest_release}"
  end
  desc "Restart Application"
  task :restart, :roles => :app do
    run "touch #{current_path}/tmp/restart.txt"
  end
end

in case you have this error:

script/console production
Loading production environment (Rails 2.1.0)
/opt/ruby-enterprise-1.8.6-20081215/lib/ruby/1.8/irb/completion.rb:10:in `require': no such file to load -- readline (LoadError)
        from /opt/ruby-enterprise-1.8.6-20081215/lib/ruby/1.8/irb/completion.rb:10
        from /opt/ruby-enterprise-1.8.6-20081215/lib/ruby/1.8/irb/init.rb:252:in `require'
        from /opt/ruby-enterprise-1.8.6-20081215/lib/ruby/1.8/irb/init.rb:252:in `load_modules'
        from /opt/ruby-enterprise-1.8.6-20081215/lib/ruby/1.8/irb/init.rb:250:in `each'
        from /opt/ruby-enterprise-1.8.6-20081215/lib/ruby/1.8/irb/init.rb:250:in `load_modules'
        from /opt/ruby-enterprise-1.8.6-20081215/lib/ruby/1.8/irb/init.rb:21:in `setup'
        from /opt/ruby-enterprise-1.8.6-20081215/lib/ruby/1.8/irb.rb:54:in `start'
        from /usr/bin/irb:13

Then some things where missing during install. Let’s install that and compile readling.

cd /root/ruby-enterprise-1.8.6-20081215/source/ext/readline
 
ruby extconf.rb
checking for tgetnum() in -lncurses... no
checking for tgetnum() in -ltermcap... no
checking for tgetnum() in -lcurses... no
checking for readline/readline.h... no
 
apt-get install libncurses5-dev libreadline5-dev

Check once again if everything is installed

ruby extconf.rb
.
.
creating Makefile

and now compile

make
make install

AJAX validation on Rails

I had a ajax form to add languages in the system in one of my projects. A pretty simple form with just one text field. The problem I had was with validations and display the error message if something goes wrong.
After search the net for a while I came across this blog from bigsmoke which could solve my issues, but after looking a bit it kind of looks a bit complicated and I wanted something more simple.
I took some ideas from bigsmoke and did my own thing.

So here is my implementation of Ajax validation on Rails:

My controller has a “new” action which looks like this

def new
  @language = Language.new(params[:language])
  if @language.save
    @language.reload
    return if request.xhr?
  else
    render :partial => 'error', :status => 444
  end
end

The view where the languages are displayed I have added a hidden div which will display the error message if present

Here is where the most stuff happens, in the _error.rjs

page.replace_html 'errors', "Language could not be created. Reason: #{@language.errors.full_messages}"
page.delay(5) do
  page.visual_effect :fade, 'errors'
end
page["errors"].toggle

This replaces the errors div with a message and the errors returned from the validation, it starts a fade with a delay so that the error message will disappear after a while and finally, the div is set to visible.

That’s it! It really is that simple.