Archive for the ‘Guides’ Category

Upgrading an Application to Rails 3 — Part 1

Almost a week ago I posted What I Will Do for Rails 3 in which I promised to “upgrade a Rails 2.3.5 application, publish an article on the process, and report any bugs I stumble upon”. This guide will walk you through the steps I took to upgrade a Rails 2.3.5 application of mine, called “The School Portal” (or “Skoleportalen” in Danish).

Re-generating the Application

The very first thing I did was to clone the Rails Git repository and use the rails executable to re-generate the application to get all files up-to-date with Rails 3.

git clone git://github.com/rails/rails.git
ruby rails/railties/bin/rails skoleportalen

The generator is smart enough to ask you for permission before overwriting the contents of a file. It will even let you see a diff of the current contents of the file and what Rails would like to insert. It’s important that you don’t let Rails delete code written by you. For example Rails wants to overwrite config/routes.rb but that would mean deleting your routes. So you should merge the two versions together to contain both your code and Rails’ updates to the file.

There is one exception, though: When Rails wants to delete the config.gem lines of config/environment.rb (and the configuration file for each environment) you can let it do so, given you have your application under version control (Of course you have, right?) so you can fetch the dependencies later and add them to the Gemfile.

When I was done I reviewed the changes, deleted public/index.html, and committed the current state of the process.

git diff
rm public/index.html
git add .
git commit -m "Rails 3 upgrade: Re-generated application"

You should consider doing the upgrade in a separate branch. In my case it was not necessary since my application was made as part of an assignment at school and therefore never went into production.

Booting the Application

The next step I took was to add the Rails Git repository that I just cloned to the Gemfile and bundle it.

gem 'rails', '3.0.pre', :git => '../rails/'

In my case, my clone of Rails and the application were both in my ~/code/ directory, but if you have arranged your directories differently you should update the reference to the Rails repository to reflect that.

gem bundle

Then it was time to try starting the server.

david@david-laptop:~/code/skoleportalen$ script/server
=> Booting Mongrel
=> Rails 3.0.pre application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
Exiting
/usr/lib/ruby/gems/1.8/gems/activesupport-3.0.pre/lib/active_support/dependencies.rb:456:in `load_missing_constant': uninitialized constant ActiveSupport::CoreExtensions (NameError)
...
	from /home/david/code/skoleportalen/config/initializers/date_formats.rb:1
...

In Active Support 3.0.pre all core extensions has been moved out of their respective modules and directly into the classes they extend. My config/initializers/date_formats.rb file looked like this:

ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(
  :danish => "d. %d/%m-%Y"
)

And what I changed it to, in order to accomplish the same thing, was this:

Time::DATE_FORMATS.merge!(
  :danish => "d. %d/%m-%Y"
)

Then I tried starting the server again.

/home/david/code/skoleportalen/config/initializers/new_rails_defaults.rb:14: undefined method `generate_best_match=' for ActionDispatch::Routing:Module (NoMethodError)

The file that causes this error was intended to change the behavior of Rails 2 applications to what would be default in Rails 3, so I removed that.

git rm config/initializers/new_rails_defaults.rb

Now the server started successfully for the first time, so I committed the state of the upgrade once again.

git add .
git commit -m "Rails 3 upgrade: Bundled Rails and made the application boot-able"

A Bundler Bug?

Then I navigated to http://localhost:3000/ and concluded it was time to add the application’s dependencies to the Gemfile.

gem 'formtastic',     '~> 0.9'
gem 'wysihat-engine', '~> 0.1'
gem 'friendly_id',    '~> 2.2'
gem 'authlogic',      '~> 2.1'
gem 'cancan',         '~> 0.2'
gem 'paperclip',      '~> 2.3'
gem 'will_paginate',  '~> 2.3'

With a bit of search and replace-fu it was a piece of cake. I ran gem bundle again, restarted the server, and refreshed. But weirdly I got the same error again telling me that even though I had bundled the application’s dependencies they didn’t seem to get loaded. I went digging: The config/boot.rb file requires vendor/gems/environment.rb which, in my case, requires vendor/gems/ruby/1.8/environment.rb.

Inside that a Bundler.require_env method is defined which creates an anonymous class with a gem method that requires a specified gem. It then evaluates the Gemfile within the context of that class. Just for fun I added a call to it just below the definition and restarted the server.

One of the gems raised an exception which means, on the bright side, it got required. Of course, as a good citizen of the open source community, I reported the bug. I did not provide a patch because in cases like this the decision of how the bug should be fixed is really up to the project owners. Adding a call to require_env under the definition might not be ideal. Also, I didn’t know if it was Bundler’s or actually Rails’ responsibility to call the method.

Conclusion

When the dependencies of my application have all been fixed to work with Rails 3 I will post part 2 of this guide. The following is a list of issues I posted to the respective gems’ issue trackers:

UPDATE As pointed out by José Valim in the comments: These issues can simply be solved by calling Bundler.require_env later on in the process, making sure the components of Rails have been loaded. At the end of config/environment.rb would be a good place.

Making Generators for Rails 3 with Thor

Rails 3 replaces the old built-in generator system with Thor which is “a scripting framework that replaces rake and sake”. Not only that, because during Google Summer of Code José Valim extended it with Thor::Actions, a module with methods useful for generators, and integrated it with Rails. Some things have changed, others haven’t. This article will walk you through the process of making a gem with a built-in generator for Rails 3.

First Things First

The gem we will make is very simple. It’s job will be to copy one JavaScript (specifically the ColorBox jQuery plugin) into the public/javascripts/ directory of an application. Start out by creating a directory for the gem wherever you prefer to store your code:

mkdir colorbox_rails/

Previously Rails would search for generators in the rails_generators/ or just generators/ directory of a gem or plugin, but the new Thor-based system changes that. Now the locations are lib/generators/ or alternatively lib/rails_generators/.

cd colorbox_rails/
mkdir lib/
mkdir lib/generators/

Inside lib/generators/ the rules are as follows: One directory per generator, each of which should contain a file of which the filename is simply the name of the generator suffixed with _generator.rb. So in our case the file of the generator will be named colorbox_generator.rb.

cd lib/generators/
mkdir colorbox/
cd colorbox/
touch colorbox_generator.rb

The Generator

Open the newly created file in your favorite text-editor. The first line should require the generator system.

require 'rails/generators'

This little piece of code actually introduces another change, because the module used to be called Rails::Generator. Next, we need a class which inherits from Rails::Generators::Base.

class ColorboxGenerator < Rails::Generators::Base
end

The name of a generator class should simply be the name of the generator in CamelCase suffixed with Generator, staying consistent with the filename. Rails::Generators::Base inherits from Thor::Group which works in a special way. When invoked it will call all instance methods, or "tasks" in Thor terminology, of the class automatically, allowing you to divide the code of your generator in related parts — and naming them, notably. In our case we only need one task, though.

class ColorboxGenerator < Rails::Generators::Base

  def install_colorbox
  end

end

Now for the actual copying of the JavaScript. This is where Thor::Actions comes in. It has a few submodules with methods we can use in our generator. (Please refer to the Thor documentation for an overview.) In this case, what we're interested in is the copy_file method.

def install_colorbox
  copy_file(
    'jquery.colorbox-min.js',
    'public/javascripts/jquery.colorbox-min.js'
  )
end

UPDATE As José Valim has kindly pointed out in the comments: Rails::Generators::Base also has a set of Rails-related actions available which live in Rails::Generators::Actions which basically contains the Rails templates API. That means that generators and templates are merged in Rails 3! Check out the source of Rails::Generators::Actions.

Go ahead and download the jQuery plugin and copy the minified version into templates/, which should be located in the same directory as your generator file.

Before we make the generator a gem, we need to add a class method to the generator. It's intended to set the source root (where the generator will look for templates) to the templates/ directory of our generator. I honestly don't know why this is necessary — perhaps it isn't. I've been browsing through the source of the Rails generator system, trying to find a clear definition of where your templates should be located in order to not have to specify it manually, but with no success. If you can help me out here, please feel free to share your knowledge in the comments.

def self.source_root
  File.join(File.dirname(__FILE__), 'templates')
end

Making the Generator a Gem

In order to use our generator inside a Rails application we want to install it as a gem. All that takes, really, is a .gemspec file, and since this is just an example we will keep it as minimalistic as possible. Create a file called colorbox_rails.gemspec in the root of your gem directory and paste in the following:

Gem::Specification.new do |gem|
  gem.name    = 'colorbox_rails'
  gem.version = '0.0.0'
  gem.summary = 'The ColorBox jQuery plugin on Rails.'

  gem.files = Dir['lib/**/*']
  gem.add_dependency 'rails', '3.0.pre'
end

Before we install our gem we need the 3.0.pre version of Rails. Since it hasn't been released officially as a gem we have to install it from source. Active Record depends on Arel 2.0.pre which also hasn't been released as a gem, so first we'll install that from source, too.

git clone git://github.com/rails/arel.git
cd arel/
gem build arel.gemspec
gem install arel-0.2.pre.gem

git://github.com/rails/rails.git
cd rails/
sudo rake install

With that done we are ready to install our own generator as a gem.

cd colorbox_rails/
gem build colorbox_rails.gemspec
gem install colorbox_rails-0.0.0.gem

Using the Generator

In order to use our new generator we need a Rails 3.0.pre application. Unfortunately, even though we installed 3.0.pre as a gem we can't use the rails executable. Go ahead and try and observe the following error:

/usr/local/lib/site_ruby/1.8/rubygems.rb:384:in `bin_path': can't find executable rails for rails-3.0.pre (Gem::Exception)
	from /usr/local/bin/rails:19

Right... Luckily, the binary is (naturally) to be found in the Rails Git repository which we just cloned, so that's not a problem.

ruby rails/railties/bin/rails colorbox_rails_test
cd colorbox_rails_test/

To enable the generator we need to add the gem to the Gemfile.

gem "colorbox_rails", "0.0.0"

Now we just need to bundle the gem, before we should be able to run our generator.


gem bundle
script/generate colorbox

Tada! We have successfully created a Rails 3-compatible generator with Thor, installed it as a gem, and used it in a 3.0.pre application. But as good Ruby programmers there is one more thing we should do.

Testing the Generator

Head back to the root directory of our gem and create a test/ directory. Inside that we want a test_helper.rb file, and a generators/colorbox/ directory. Inside the latter we want a colorbox_generator_test.rb file, staying consistent with the structure of the lib/ directory. Open test_helper.rb and paste in the following:

require 'test/unit'
require 'rubygems'

Inside colorbox_generator_test.rb we want to require test_helper.rb and the generator file.

require 'test_helper'
require 'generators/colorbox/colorbox_generator'

Beneath we define our test case.

class ColorboxGeneratorTest < Test::Unit::TestCase
end

Inside we want one test method which tests for the presence and content of the jQuery plugin.

class ColorboxGeneratorTest < Test::Unit::TestCase

  def test_install_colorbox
    assert File.exists?(
      File.join(@destination, 'public', 'javascripts', 'jquery.colorbox-min.js')
    )

    assert_equal(
      File.read(File.join(@source, 'jquery.colorbox-min.js')),
      File.read(File.join(@destination, 'public', 'javascripts', 'jquery.colorbox-min.js'))
    )
  end

end

Obviously, a couple of instance variables are missing.

def setup
  @destination = File.join('tmp', 'test_app')
  @source = ColorboxGenerator.source_root

  ColorboxGenerator.start('', :destination_root => @destination)
end

Let us also clean up after ourselves, shall we?

def teardown
  FileUtils.rm_rf(@destination)
end

To run our tests let us define a task in Rakefile.

require 'rake/testtask'

Rake::TestTask.new do |test|
  test.pattern = 'test/**/*_test.rb'
  test.libs << 'test'
end

task :default => :test

Run rake and watch it go green.

Conclusion

This walk-through barely scratched the surface of the new generator system, but it hopefully gave you an idea of what has changed and what hasn't — and if you have never written a generator for Rails before, you can now! Don't forget to checkout the documentation of Thor.

PostgreSQL & Rails on Ubuntu

I work as a Ruby on Rails contractor for Enlight Solutions, and recently I got involved in a new project for which we wanted to use PostgreSQL because we were going to use Heroku for staging the application. Since I never used PostgreSQL on a project before, I just wanted to share with you how I got it up and running on my Ubuntu 9.10 machine.

The first thing you need to do in order to get it working is to install the postgresql package:

sudo apt-get install postgresql

Now install the postgresql-client package so that you can interact with PostgreSQL in the terminal:

sudo apt-get install postgresql-client

Then you need to install the pg gem to get the Ruby bindings for PostgreSQL:

sudo gem install pg

Next, run the following command to enter the PostgreSQL console as superuser:

sudo -u postgres psql

postgres is the user of PostgreSQL on your machine. In the console, run this command:

CREATE USER #{your_unix_username}

Replace #{your_unix_username} with the username you use to log in to your machine, in my case it’s just “david”. We’re not specifying a password because it’s our development environment – in production you should of course strive to keep things secure. Now let’s test if it’s working. First generate a Rails project which uses PostgreSQL:

rails pg_test -d postgresql

That should work just fine no matter what. Even if you haven’t installed PostgreSQL on your machine. However, if you haven’t installed PostgreSQL properly this will not work:

rake db:create

Run it and see what happens. It should work if you have followed the instructions above. Please note that you do not have to edit config/database.yml with your username. By default Rails will put the name of the application there, because that’s how you would usually set it up in production environment: create a separate user for each application. In this case, however, it works even if you leave username and password blank because we named our PostgreSQL user after our UNIX user. To illustrate the process of authentication inside PostgreSQL:

  • Does “username” match a user on the machine?
    • Yes
      • Is that user logged in?
        • Yes
          • OK!
        • No
          • Fail!
    • No
      • Normal authentication procedure

Please leave feedback if there’s something you think should be corrected or if you have difficulties getting it to work. Hope you found it useful!

Getting Magento Running on PHP 5.3

A couple of weeks ago I was forced to getting Magento, an open source e-commerce platform written in PHP, up and running on 5.3 which Magento isn’t natively compatible with. After hours, and even days, of hard work I got it running with only minor issues, so for future reference I’m posting the steps I followed here. Now you may be wondering how this could ever belong on a blog about Ruby and Rails, but the guide is also intended to remind you not to return to PHP ever. Magento as a system and PHP as a language have been one of the most painful technical experiences of my entire life.

The setup

The easy part

After having installed XAMPP, extract magento-1.3.2.3.zip into C:\xampp\htdocs and start Apache and MySQL using the XAMPP Control Panel. Now head to http://localhost/magento and congratulations: You’re looking at your first obscure error:

Fatal error: Method Varien_Object::__tostring() cannot take arguments in C:\xampp\htdocs\magento\lib\Varien\Object.php on line 488

But have no worries, the fix is easy. Open C:\xampp\htdocs\magento\lib\Varien\Object.php in your favorite text-editor. On line 484 the definition of the function __toString() begins. Rename that function to __invoke() and go back to your browser and hit refresh. A new error will appear, but this time wrapped in a basic Magento layout. Progress.

Unknown error (8192): Function split() is deprecated  in C:\xampp\htdocs\magento\app\code\core\Mage\Core\Controller\Request\Http.php on line 274

The thing is that in PHP 5.3, split() was deprecated in favor of explode(). They used to be aliases so once again the fix is easy. Go to the file and line in question and replace split with explode. Now go back to the browser and hit the back button to get away from the error submission form. Then refresh.

The dirty part

You should see a welcome message and text area containing the terms and conditions of Magento. But before you agree to them and click continue, unpack magento-sample-data-1.2.0.zip that you downloaded previously to an arbitrary location. Navigate to http://localhost/phpmyadmin and create a database named “magento”. Click the import tab and select magento_sample_data_for_1.2.0.sql. After the 1487 SQL queries has executed successfully it’s time to do something dirty.

The thing is that Magento has it’s own sort of migrations, but they can’t run on our version of PHP. Since we just imported the sample data into our database, which includes all the tables Magento needs, all we need is a way to skip the migrations. Open C:\xampp\htdocs\magento\app\code\core\Mage\Core\Model\Resource\Setup.php and comment line 121 out using //.

The final part

With that done it’s time to continue. After adjusting the localization settings you may encounter another error:

PHP Extension "curl" must be loaded

Obviously we just have to enable the curl extension. Open C:\xampp\php\php.ini and remove the semicolon at the beginning of the line saying ;extension=php_curl.dll, then restart Apache using the Control Panel. Switch to your browser and click continue. What you see is the configuration screen of Magento. Change whatever you want to change (not the database name and table prefix, though). In specific, I recommend enabling the “Use Web Server (Apache) Rewrites” option for prettier URLs.

Conclusion

The next screen lets you create an admin account. When you’re done, click continue. You should see a message telling you that “you’re all set” and an encryption key. Let this be a word of warning: If you loose your encryption key you’re on your own. Copy it to a safe place and click “Go to Backend”. Log in and observe a familiar error; you should know how to fix it. You may encounter it again as you browse the admin section of Magento.

Congratulations. Magento is up and running with an incompatible version of PHP. I hope this has taught you a lesson. Stick with Rails. No seriously.