Digging deeper into Rails scopes

October 8th, 2015

Background

Recently I wrote a post about Rails scopes and lambdas in an attempt to clarify why it is so common to see the use of lambdas with Rails scopes. I now realise that I could have gone a bit further in my explanation.

A question that is sometimes asked is:

Why are lambdas used with Rails scopes? Why not procs or blocks?

To answer that question it is useful to first look at the implementation of the Rails scope method in ActiveRecord::Scoping::Named::ClassMethods.

Implementation Details

The implementation is as follows:

def scope(name, body, &block)
  unless body.respond_to?(:call)
    raise ArgumentError, 'The scope body needs to be callable.'
  end

  if dangerous_class_method?(name)
    raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
      "on the model \"#{self.name}\", but Active Record already defined " \
      "a class method with the same name."
  end

  extension = Module.new(&block) if block

  singleton_class.send(:define_method, name) do |*args|
    scope = all.scoping { body.call(*args) }
    scope = scope.extending(extension) if extension

    scope || all
  end
end

Now let’s consider this implementation in conjunction with the following scope:

class Article < ActiveRecord::Base
  scope :created_before, ->(time) { where("created_at < ?", time) }
end

In this example, :created_before is interpreted as name and ->(time) { where("created_at < ?", time) } as body.

Block, Proc or Lambda?

Notice that body must be callable. So that rules out a block, which is simply a syntactic structure. However, it does still allow body to be a lambda or a proc.

Whilst it is technically possible for a proc to be used in conjunction with a Rails scope, a lambda is more useful because of the constraint that it must, unlike a proc, have a specific arity. For example, in the example above, Article.created_before must be called with one argument.

Summing up

Hopefully, that explains why lambdas are used with Rails scopes.

Of course, you’ll notice that the implementation of the scope method uses metaprogramming via :define_method to create a class method that could have been programmed directly.

Tags: rails ruby scopes lambdas | Comments (0)

Rails scopes and lambdas

October 6th, 2015

Recently I was asked a searching question that left me pondering. The questioner mentioned that he had been following The Rails Way and was puzzled by it’s emphasis on the use of lambdas in conjunction with Rails scopes.

At the time I could only offer a vague answer along the lines of it being a common idiom. I was not satisfied with my reply so committed to following up with a more definitive response.

Background

Ruby Lambdas

Firstly, let’s recap and clarify exactly what a Ruby lambda is.

The lambda method belongs to the Kernel module. Since Ruby 1.9 introduced an alternative syntax, a lambda can also be identified with the characters ->. So, the following two code blocks are equivalent:

succ = ->(x) { x + 1 }
succ = lambda { |x| x + 1 }

Another thing to note about lambdas is their arity, or how many arguments are required when they are called. Every lambda has a precise arity, which can be tested using the arity method as follows:

->(x) { x + 1 }.arity # => 1

There is much more that can be said about lambdas but I’ll leave that to others. For example, Chapter 6, Methods, Procs, Lambdas, and Closures within The Ruby Programming Language by David Flanagan and Yukihiro Matsumoto gives a thorough treatment despite the age of the book.

As a taste, let me quote:

Blocks are syntactic structures in Ruby; they are not objects, and cannot be manipulated as objects. It is possible, however, to create an object that represents a block. Depending on how the object is created, it is called a proc or a lambda. Procs have block-like behavior and lambdas have method-like behavior. Both, however, are instances of class Proc.

Rails Scopes

At the time of writing, the latest Rails version is 4.2.4. On the subject of Rails scopes it is worth reading what is said about them in the Rails guides.

It is also worth keeping in mind that Rails has undergone many changes in it’s history. Currently the Rails guides illustrate that this:

class Article < ActiveRecord::Base
  scope :published, -> { where(published: true) }
end

is exactly the same as:

class Article < ActiveRecord::Base
  def self.published
    where(published: true)
  end
end

Furthermore, the Rails Guides section about passing in arguments advises:

Using a class method is the preferred way to accept arguments for scopes.

In other words, this:

class Article < ActiveRecord::Base
  def self.created_before(time)
    where("created_at < ?", time)
  end
end

is preferable to:

class Article < ActiveRecord::Base
  scope :created_before, ->(time) { where("created_at < ?", time) }
end

If you look carefully and test the two styles, you will realise that the important part of the code is the where clause. So long as the scope or class method returns an object that is an ActiveRecord::Relation, it can be chained together with other scopes or class methods.

In essence, with Rails 4.2 there is no reason to use scopes together with lambdas in preference to class methods. It is a matter of personal preference.

So, why the emphasis on using scopes with lambdas?

To answer this we need to consider the history of Rails. It was around the time of version 2 that named scopes were introduced. They allowed the convenience of chaining scope calls together. Later, if my memory serves me correctly, in version 3, Arel was introduced. This reduced the importance of named scopes when it came to specifying a variety of conditions to return a collection of model objects.

Fast forward to today and, as the Rails Guides state, there is no need to use scopes with lambdas instead of their equivalent class methods, which are arguably easier to read. However, given that Rails has been around for over ten years now, there are lots of legacy Rails codebases, many of which contain a plethora of scopes. Also, there are plenty of seasoned Rails programmers who have formed the habit of using Rails scopes.

In Summary

Hopefully this post has done a better job of clarifying the use of lambdas in Rails scopes than my original fumbling reply during a conversation at last week’s Melbourne Ruby meetup.

Tags: rails ruby scopes lambdas | Comments (0)

Rails and RSpec with Two Databases

June 18th, 2015

Maintaining Rails applications in the real world is not always straight-forward.

Recently I needed to enable a Rails 4.0.13 application which used a MySQL database as it’s default to also use a PostgreSQL database. Being a good TDD citizen, I also needed to ensure that the new tests would run in our CI environment.

Whilst I did find a helpful post, it didn’t tell me everything I needed to know. So I thought I’d share what I did.

Database Configuration

First of all, I configured environments for the PostgreSQL database within config/database.yml as follows:

# in addition to existing contents
postgres: &postgresql_defaults
  adapter: postgresql
  pool: 10
  timeout: 60000
  host: localhost
  template: template0

development_foo:
  database: foo_development
  <<: *postgresql_defaults

test_foo:
  database: foo_test
  <<: *postgresql_defaults

staging_foo:
  database: foo_staging
  <<: *postgresql_defaults

production_foo:
  database: foo_production
  <<: *postgresql_defaults

Establish Connection in Model Class

In my case, I only needed to use one model class backed by a table from the extra database. Let’s imaginatively call this class Bar. So the class would be defined like this:

class Bar < ActiveRecord::Base
  establish_connection "#{Rails.env}_foo"

# the rest of the class
end

Note that you probably want to introduce an abstract class which establishes the connection if you have more than one model class which needs it.

Augment rake db tasks

So far so good. The next step was to ensure that the CI script on Buildkite handled the extra database. As you would expect, the script prepares to run the tests by dropping, creating and loading the schema of the database. To do this it uses some code common to all our builds, which includes running bundle exec rake db:drop and bundle exec rake db:create db:schema:load.

I wanted to find a way for the setup of the extra database to piggy-back on these rake tasks and, in so doing, use the existing script unchanged. I discovered I could do so by using the Rake::Task#enhance method. This allowed the rake db tasks to be augmented as follows:

namespace :db do
  task drop_foo: :environment do
    ActiveRecord::Tasks::DatabaseTasks.drop(foo_db_config)
  end

  task create_foo: :environment do
    ActiveRecord::Tasks::DatabaseTasks.create(foo_db_config)
    reset_connection_to_mysql_db
  end

  namespace :structure do
    task load_foo: :environment do
      if File.exist?(foo_structure_filename)
        ActiveRecord::Tasks::DatabaseTasks.structure_load(foo_db_config, foo_structure_filename)
      end
    end
  end
end

def foo_structure_filename
  "#{Rails.root}/db/foo_structure.sql"
end

def foo_db_config
  ActiveRecord::Base.configurations["#{Rails.env}_foo"]
end

def default_db_config
  ActiveRecord::Base.configurations["#{Rails.env}"]
end

def reset_connection_to_mysql_db
  ActiveRecord::Base.establish_connection(default_db_config)
end

Rake::Task['db:drop'].enhance do
  Rake::Task['db:drop_foo'].invoke
end

Rake::Task['db:create'].enhance do
  Rake::Task['db:create_foo'].invoke
end

Rake::Task['db:schema:load'].enhance do
  Rake::Task['db:structure:load_foo'].invoke
end

There are a couple of things I should explain here.

Let’s assume FooApp is the name of application which has foo as it’s default database. FooApp uses SQL rather than Ruby to define it’s schema so I copied db/foo_structure.sql from db/structure.sql in FooApp.

Also worthy of close attention is the call to the reset_connection_to_mysql_db method after the create_foo task. Without this, rake db:schema:load will load the default MySQL schema into the PostgreSQL database!

Conclusion

So there you have it. How to add an extra database to your Rails application and not break CI in the process. Discovering how to use the Rake::Task#enhance method to facilitate this was my main motivation for sharing this.

I hope it helps someone else.

Tags: rspec rake databases rails | Comments (3)

Rails i18n Config Gotcha

January 20th, 2015

Today I battled a subtle problem which surfaced when our Rails application was updated from Rails 4.012 to 4.0.13.

Several tests started failing with I18n::InvalidLocale errors.

After some debugging I found that the following line of configuration config/application.rb was not being respected:

I18n.enforce_available_locales = false

This line was introduced when the application was upgraded to Rails 4.0.2 a year ago. At the time, as this stackoverflow question highlights, the following deprecation warning message advised to do this:

[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.

However, as I digged deeper into the Rails 4.0.13 changes, I found that they included this commit, which changed the behaviour of setting i18n configuration values. It now ignores I18n.enforce_available_locales = false.

The solution is to follow the relevant section of the Rails Guides and configure as follows:

config.i18n.enforce_available_locales = false

It’s a subtle change that caught us out.

Tags: i18n rails | Comments (0)

On Test Driven Development

September 14th, 2014

Recently I responded to a request for help with setting up a new machine on which to develop Rails applications.

A little while later I was pleased to hear that Rosie Williams had completed setting up her new Mac and was ready to continue developing the Rails app that Matt Allen had helped her get started with at a Rails Girls event.

As you can see from the Twitter exchange below, I also cheekily asked Rosie whether she had all her tests passing.

I didn’t expect that Rosie would have ventured down the TDD path yet but I guess I couldn’t resist asking the question. The value of automated testing is something I have long cared about.

Why are automated tests important?

Now I’m not one who always practices strict TDD. I don’t always start with a failing test and work towards enabling that test to pass. Sometimes I do but other times I find more value in experimenting with a solution in other ways, e.g. via a REPL or a browser, before writing an automated test.

And there are times that I don’t bother with automated tests at all. However, those situations are definitely in the minority.

If I am working on a production system that an organisation is dependent on for more than a brief period of time, I consider automated tests to be vital.

Why? The simple answer is that software needs to be malleable.

Developers need to be able to make changes to a software system to meet changing needs of business. To allow a software system to adapt to changing requirements, the design needs to be continually improved. Integral to such refactoring is a sufficiently comprehensive suite of automated tests. Without such tests there will be too great a risk of new defects being introduced.

An anecdote about refactoring

Recently I have been responding to various reports from users, all impacted by problems in a subsystem of a large Rails codebase.

It has been a classic case of defects caused by software that is far too complex for the essential job it was designed to do. To my eyes, it is begging to be refactored. To work with the code as it is now, it can take a disproportionate amount of time to debug a problem due to the challenge in deciphering what it is doing. It is clear that the internal design of this code needs to be improved so that it is easier for the next person to understand. So I intend to refactor this code in the near future.

Fighting software entropy

Like many things, software is prone to entropy over time. Without careful maintenance, it is likely to degrade as bandaid solutions are progressively applied.

Refactoring is a key part of enabling software to successfully adapt. Automated tests are crucial to refactoring.

Opinions about TDD

Unsurprisingly, there are differing views about TDD. Without going into depth here, I will allude to some interesting discussions between Martin Fowler, Kent Beck and David Heinemeier Hansson a few months ago, which I summarised in a talk I gave at a Sydney Ruby meetup in July.

Resources

I hope this post has been useful, particularly to newcomers to Rails development who may have been wondering what all the fuss is about TDD. There is much more to say about testing and refactoring but for now I’ll end this post by recommending:

Tags: testing tdd refactoring rails ruby | Comments (0)

Which OS for Rails?

August 21st, 2014

Why write about this topic? Isn’t the answer obvious?

Maybe for many.

However, thanks in large part to the initiatives of reInteractive and Ruby Australia, there is an increasing number of Australian newcomers to Rails. Many are predominantly familiar with Windows when it comes to the operating system on their own machine.

The Background to the Question

Who prompted this post? It was Rosie Williams when she posed the following question on the Rails Oceania mailing list:

I’ve begun learning RoR but want to know what operating system to continue with that is most in demand in the market? To date I’ve done most of my development on Windows (which dual boots to Linux which I’ve not really used). I am considering buying a new Mac for development but wondering whether I ought to be doing my RoR on Linux? Or does it not really matter?

Thanks to the generosity of the community, Rosie has received many helpful suggestions.

The Short Answer

Most Rails developers use OS X or Linux. The majority use OS X.

Assuming OS X, What Next?

Given that most Rails developers use OS X, I am one of them and Rosie now has a new Macbook Air, I will continue this post on the assumption that OS X has been chosen. What will the developer want to focus on next when preparing to start their first Rails application on OS X?

The Command Line

I was going to start with Homebrew but I think there’s an important step before that. Often newcomers to Rails who are used to Windows do not have familiarity with using the command line.

I think it is important to stress that if you want to dive into web development with Ruby on Rails in a serious way, you absolutely need to develop a sufficient level of comfort with using the command line. In OS X, that can be using bash via Terminal. Personally, I prefer iTerm2 to Terminal. And many developers prefer zsh to bash.

In any case, the important thing is this: embrace the command line. You are going to need to depend upon it!

Xcode Command Line Tools

As keen as I am to mention that you’ll need Homebrew, before then you are going to need to install Apple’s Xcode command line tools. Over time Apple has changed the method for installing these tool tools. At the time of writing, you need to register as an Apple developer and then sign into the Apple developer downloads site so that you can download and install the command line tools.

Update: An easier method of installing Command Line Tools, namely running xcode-select --install from the command line, has been known to work. It’s worth trying this first before resorting to registering and downloading as described above.

Homebrew

OK, next up is Homebrew, the package manager that is simply indispensible to developers using OS X. Throughout your journey as a Rails developer using a Mac, there will be many occasions when you will need to install software on which your application depends. It might be PostgreSQL. Or it may be imagemagick. Or git. In almost every case you will simply be able to run brew install <package-name> from the command line and Bob’s your uncle.

Notice I mentioned using the command line! Anyway, install Homebrew and let it do your bidding. And once you have, get into the habit of using brew update, brew outdated, brew upgrade, brew cleanup and brew doctor to keep your Homebrew packages in good shape.

Ruby

Now, obviously a Ruby on Rails developer needs Ruby on their machine. And, OS X does indeed come with Ruby installed. However, it is almost certainly an older version than you will need. At the time of writing, the latest version of Ruby is 2.1.2.

To install and manage Ruby versions, unless you are going to compile from source manually, you have three choices:

These are listed in chronological order. I have happily used rbenv for some time, having discarded RVM. Many Ruby developers whom I respect recommend chruby. Pick one and install the latest version of Ruby.

Update: If you would like to know more about each of these Ruby version managers, I recommend you listen to Episode 169 of the Ruby Rogues podcast.

RubyGems

Fortunately, RubyGems, the default package manager for Ruby, is part of the Ruby standard library, as of version 1.9. So, once you have installed Ruby, you will have RubyGems at your disposal to install software such as Ruby on Rails.

Rails

Opening your trusty command line (e.g. Terminal), run the command gem install rails, wait for a while, and Rails should be installed. Type rails -v to confirm the version that has just been installed for you, courtesy of RubyGems.

Other software

Having got this far, you should be ready to start experimenting with Rails development. However, before long you will likely need to install some other software that your application needs.

If it is software in the form of a RubyGem, the accepted practice these days is to add the gem to your application’s Gemfile and use bundler to install and update it. There are all sorts of gems available. The Ruby Toolbox is a good place to explore your options.

If the software you need is not built with Ruby, the chances are that you will be able to install it using Homebrew. For example you are definitely going to need git. At the command line, enter brew install git and you should be set.

Other considerations

Naturally, when dealing with computers, it is not uncommon to be presented with a surprise when you may have expected something to “just work”. For example, sometimes on UNIX-based systems, commands fail due to permissions problems and the error message doesn’t always make it clear that this is the source of the problem. In this case, get to know UNIX permissions and chmod.

Something else that I think is worth mentioning is the awesome combination of Dash and Alfred for quickly looking up API documentation.

And, of course, remember that there are always people online who are willing to help.

In Conclusion

Whilst there are many other aspects that could be covered within the topic of “what to do next having chosen OS X for Rails development”, I think I’ll leave it there as far as this post goes. And, Rosie Williams, if you’re reading, I wish you all the best with developing Rails applications on your new Macbook Air and I hope that I have been of some help.

Update: Credit where credit is due. Leonard Garvey has reminded me about his excellent Rails Install Fest Guide.

Tags: rails | Comments (0)

Twitter Bootstrap to the Rescue

January 28th, 2013

OK, I admit it. I have finally acknowledged that web design is something I should not waste my time on.

The design of this site has gone through several phases, all my own attempts and none that I was satisfied with. So yesterday I finally bit the bullet, jettisoned my old home grown styles and images and resorted to Twitter Bootstrap.

Whilst a quick glance reveals the obvious use of Twitter Bootstrap, I’m encouraged by the result. I can now rely on a popular, well-designed, well-documented tool to produce an acceptable looking site rather than struggling with my own incompetence. For me, Twitter Bootstrap hits the sweet spot. After all, it is aimed at programmers who don’t have good web design skills.

The Conversion

For those interested in what it took to convert a Rails site to use Twitter Bootstrap, here are some of the steps I followed:

  • installed the twitter-boostrap-rails gem
  • enabled the Twitter Bootstrap responsive styles
  • tailored my layouts to use Twitter Bootstrap
  • removed my old styles
  • converted forms to use the simple_form gem, which supports Twitter Bootstrap
  • made use of the various classes in Twitter Bootstrap to easily improve the appearance of forms, buttons and tables

Responsive Design

One aspect I’m delighted with is the fact that Twitter Bootstrap supports responsive design out of the box. I’ve found it interesting that one of the barriers to adding content to this site has been it’s poor display on mobile devices. Not only did the site look ugly, but it didn’t facilitate easy browsing on mobiles.

Not any more. As a by-product of using Twitter Bootstrap, my site is now responsive!

Conclusion

Having used Twitter Bootstrap before, it was a fairly painless exercise to convert the site. All it took was a rainy day on a long weekend to finally encourage me to bite the bullet.

Now I have one less excuse for writing blog posts!

Tags: twitter bootstrap web design rails | Comments (1)

Rails Camp 9

June 24th, 2011

Phew! I’ve just about recovered from Rails Camp 9, an event that may be described as an unconference. It took place over the June long weekend at Lake Ainsworth near Byron Bay on the NSW far north coast.

For this camp, it was my honour to be one of the organisers. Fortunately, Rails Camp now has a well-established pattern and there were many organisers of previous camps willing to help and advise. And I had a terrific team amongst whom to share the workload.

Nevertheless, for the benefit of future Rails Camp organisers, I thought I’d share a few recollections of how our team went about organising Rails Camp 9.

The Organising Crew

I present the wonderful bunch of Rails hackers who all played their part in ensuring that Rails Camp 9 was, to use an over-used adjective du jour, awesome: Jason Crane, Scott Harvey, Zubin Henner, Ben Hoskings, Tim McEwan, Elle Meredith, Keith Pitty and Jason Stirk. (Trivia note: half of this crew were at the inaugural Rails Camp at Somersby in 2007.)

The Venue: Lake Ainsworth

How did we choose the venue? Once we had settled on the idea of holding Rails Camp in the Byron Bay area, the locals in the organising crew – Zubin Henner, Elle Meredith and Jason Stirk – scoured the region looking for a suitable venue and came up with only one that could accommodate the numbers we were expecting.

In many respects Lake Ainsworth was a perfect venue. Nestled between the lake and the beach it had excellent facilities for our needs. The staff were very helpful and I can really only think of two negatives: we were obliged to deal with some unexpected government bureaucracy and we had to hold the camp on a long weekend.

How we organised Rails Camp 9

Settling on the venue was half the battle. Happily, as this photo shows, we were also able to organise the essential ingredients of Rails Camp.

Rails Camp 9 Ingredients

Thanks go to Zubin for organising the beer and Elle for organising the coffee machine. As for the coffee itself, Jason Crane ensured we had excellent beans and well trained baristas!

Speaking of essential ingredients, securing the services of Ben Hoskings to again set up the server and wifi was wonderful. Apart from one brief power outage during the weekend, everything ran as smooth as clockwork. So coffee, beer and wifi were sorted, ensuring happy campers!

Some of the other things we did to keep the organisation on track leading up to the weekend were:

  • Set up a Basecamp project, which may have been overkill but proved to be invaluable
  • Distributed responsibilities – e.g. finance, sponsorship, ticket sales, coffee, beer – to various members of the crew
  • Held regular virtual meetings via IRC, capturing the logs in Basecamp for reference
  • Used Eventbrite, Paypal and a joint bank account to manage ticket sales
  • Sent messages and encouraged communication via the Railscamp Google Group
  • Sent messages via the @railscamp_au Twitter account
  • Took advantage of the skills of Tim Lucas and Daniel Bogan to get the t-shirts designed and ordered
  • Created a simple Rails app that, amongst other things, enabled sending emails to participants via MailChimp and facilitated printing lanyard inserts
  • Organised buses to transport campers between Gold Coast airport and Lake Ainsworth
  • Created a Rails Camp 9 Twitter list
  • Organised a welcome BBQ (thanks Zubin) for those who arrived early enough on the Friday
  • Thanks to Elle and Gabe Hollombe, provided an Ideagora app for sharing interests, projects and talks

Running the event

One of the wonderful things about an unconference such as Rails Camp is that, once it starts, it is largely self-organising. The 146 participants of Rails Camp 9 decided how they would spend the weekend. Many chose projects to work on and talks to either present or listen to. Some simply appreciated the opportunity to socialise amongst like-minded people. And, of course, there were games of Werewolf to be played.

Rails Camp 9 Hacking

As an organiser, once the camp had started, I found my role was largely to be alert to the possibility of requests for assistance. Whilst this did keep me reasonably busy I did have the chance to catch up with and meet lots of Ruby people from around Austalia and New Zealand. I also managed, thanks to Phil Oye, to remember how to use Bananajour so that I could submit a couple of patches to Ideagora. And, there was one particularly epic game of Werewolf (thanks Carl) to remember.

Thanks to our Sponsors

A wrap of Rails Camp 9 would not be complete without thanking our sponsors. So, on behalf of the organising crew, I extend a big thank you to:

Future Australian Rails Camps

Another heartening thing about the Australian Rails Camp community is that by the end of one camp a volunteer invariably steps up to take responsibility for organising the next camp. Happily, Sebastian von Conrad did just that at Lake Ainsworth, agreeing to take possession of the virtual baton to ensure that Rails Camp 10 happens near Adelaide.

Not only that, there have already been discussions about the possibility of Rails Camp 11 in Queensland and Rails Camp 12 in Tasmania!

Tags: railscamp unconference rails | Comments (0)

Maintaining a Legacy Rails App

April 5th, 2011

Last night I deployed a new release of a Rails app.

Having deployed apps built with Rails 3 or at the very least Rails 2 apps using Bundler, the experience of deploying a Rails 2.3.5 app left me with a keen desire to at least bring this app up to Rails 2.3.11 with Bundler.

One of the delights of last night’s deployment was having to update RubyGems on the target machine. Usually this is as simple as:

gem update --system

That is, unless you are forced to upgrade to a specific, old release of RubyGems. Being at Rails 2.3.5 with this app, we were in this situation.

Speaking of “not exactly state of the art”, the version of Ruby on the target machine is Ruby Enterprise Edition 1.8.6.

Obviously, the preferred option would be to upgrade to Ruby 1.9.2 and Rails 3.0.5 forthwith.

However, it’s not my money that will be funding the upgrade. First steps first. I’m aiming for the following path:

  • Rails 2.3.11 with Bundler and latest compatible gems.
  • Ruby 1.9.2
  • Rails 3.0.5

Wish me luck!

Tags: legacy apps rubygems rails ruby | Comments (0)

Upgrading to Rails 3

February 27th, 2011

Introduction

It was long overdue.

Rails 3.0.5 is out and I’ve finally bitten the bullet and upgraded this site to Rails 3 and Ruby 1.9.2. The site had its genesis back in mid-2007 as I was preparing to start my own business. Since 2008 the codebase hasn’t received much love. Indeed, I have only published articles here in dribs and drabs over the last couple of years.

So, as I embarked on this upgrade, I was aware that it also represented an opportunity to clean up some of my code as well as update third party libraries.

What follows is a story of how the upgrade unfolded including some decisions I made along the way and some lessons I learned.

Ruby 1.9.2

My initial intention was to just upgrade the app to use Ruby 1.9.2 and attack the Rails 3 upgrade later. However, as I soon found out, getting the app to work on Ruby 1.9 wasn’t as easy as I had anticipated. Sure, using RVM it was easy to change to using Ruby 1.9.2 in development. I also chose to move to Bundler even though my intention was to stay at Rails 2.3 for now.

However, I quickly discovered that not all the plugins I was using had been upgraded to run on Ruby 1.9.

Syntax Highlighting Adventures

Back in 2008 when I implemented syntax code highlighting I chose to use the tm_syntax_highlighting plugin, which is based on Ultraviolet. Unfortunately, the original tm_syntax_highlighting plugin is not supported on Ruby 1.9. There is a gem derived from it that claims to support Ruby 1.9 but I failed to get this to work.

I had a look at the suggestions from Ryan Bates and, because I had been using a solution based on Ultraviolet, I considered using Harsh but soon found that it was not supported on Ruby 1.9.

At this point I decided to go the whole hog and upgrade to Rails 3. As for supporting syntax highlighting, I ended up switching to use CodeRay.

Rails 3 Here I Come

First point of call for anybody embarking on a Rails 3 upgrade has to be Jeremy McAnally’s Rails 3 Upgrade Handbook. This invaluable resource not only guides you through using the official “rails upgrade” tool but explains how to make the most of the improvements in Rails 3.

So I methodically worked my way through the upgrade to the point where I was able to start and use the Rails console. This was a real milestone but I still had much to do before I could consider my upgrade complete.

Time to jettison old plugins

When I attempted to get the app running via a browser it quickly became evident that it was high time I jettisoned some ancient plugins.

Chief of these was the acts_as_authenticated plugin. Yes, I did say ancient. I’m aware that Devise is a favoured authentication solution for Rails 3. However, having had success in other projects with Authlogic, I chose to use it in the interests of expediency.

Another old plugin that needed replacing was acts_as_taggable_on_steroids. The acts_as_taggable_on gem proved to be an effective replacement for this with minimal changes required.

The final old plugin to be jettisoned was akismetor, which I replaced with the Rakismet gem.

Some comments about testing

I haven’t said anything about automated tests to this point. That is quite simply because I haven’t touched them. I know I should but, since this is a relatively small app that really only affects me, I decided to bypass automated testing in favour of completing the upgrade quickly. Some may scold me for this decision but I couldn’t justify the effort to myself. I’ll come back and get the automated tests working again later.

Note: If this was a larger app for a customer I would be far less inclined to bypass the task of ensuring that the automated tests work before considering the upgrade complete.

Reflections

I’m glad I’ve taken the time to upgrade this app to Rails 3 and Ruby 1.9.2. It has given me the chance to:

  • replace old plugins with more recent gems;
  • take advantage of Bundler;
  • practice upgrading to Rails 3;
  • position the app for further code improvements courtesy of Rails 3 and Ruby 1.9.

And, of course, now I can say that I have upgraded an old Rails app to Rails 3!

Tags: rails ruby | Comments (1)

Rails Camp 5 and Sydney RORO Meetup

June 11th, 2009

I think it’s time I gave a brief update about a couple of professional events within the Australian Ruby community that I’ve recently enjoyed.

Rails Camp 5

Last month over an extended weekend the fifth Australian Rails Camp was held in the Queensland Gold Coast Hinterland. The venue was superb, much fun was had and a great deal of thanks is due to the Brisbane contingent who organised the event.

Having started the weekend working on my own to improve my iPhone development skills, particularly with respect to using ObjectiveResource to integrate an iPhone app with a Rails backend, I was actually very pleased that I didn’t continue on this path beyond Saturday afternoon. From Saturday evening onwards I teamed up with Martin Stannard and Michael Koukoullis to develop a Heroku-like tool called Bivouac. I thoroughly enjoyed our collaboration and learnt much from it. For anyone else considering attending a future Rails Camp I thoroughly recommend getting involved in a team project rather than working alone.

More on Bivouac in a later post. Meanwhile, you can find the source on github.

Sydney June RORO Meeting

Earlier this week the monthly Sydney RORO (Ruby on Rails Oceania) meeting featured a dozen lightning talks. In one of my talks I shared a technique for providing XML in a legacy format via REST, XML Builder and a presenter object. Whilst the example is contrived, the technique is one that I used to handle a requirement for a client recently.

Here are the slides:

In fairness, I must give credit to Obie Fernandez for his coverage of XML Builder in his book, The Rails Way, which gave me a head start.

Tags: railscamp legacy apps rorosyd xml rails | Comments (0)

ActionWebService with Rails 2.2

February 8th, 2009

I encountered a trick for young players the other day when providing XML-RPC web service support within a Rails application.

Since Rails 2.0 was introduced, RESTful web services via ActiveResource have been supported in preference to SOAP or XML-RPC web services. Indeed, ActionWebService was dropped from Rails when version 2.0 was released.

However, there are times when the need to provide an interface to another application mandates the use of SOAP or XML-RPC. Recently I had a need to provide an XML-RPC web service server within a Rails application. Fortunately, a version of ActionWebService has been kept up to date on GitHub together with instructions for implementing it.

As I was developing my support for XML-RPC calls, I found chapter 25 of Thomas and Hansson’s “Agile Web Development with Rails” (second edition) invaluable.

For simplicity’s sake, let’s say my controller was:

class BackendController < ApplicationController
  wsdl_service_name 'Backend'
  web_service_api BackendApi
  web_service_scaffold :invoke if Rails.env == 'development'

  def foo_bar(args)
    # method code goes here
  end
end

And here is an XML-RPC test client:

require 'xmlrpc/client'
require 'pp'

server = XMLRPC::Client.new2("http://example.com/backend/api")
result = server.call("FooBar", ["arg1", "arg2", "arg3"])

pp result

Notice that the call is to a method called “FooBar” whereas the method within the Rails controller is called “foo_bar”. If you’re like me you will have originally fallen into the trap of assuming that the remote call will have the same name as the controller method rather than the camel case equivalent.

Tags: rails | Comments (0)

Rails Camp in the Adelaide Hills

November 23rd, 2008

I think I’ve just about recovered from last weekend’s Rails Camp in the Adelaide Hills. Whether I was hacking or socialising, as the hour got later each evening it just seemed easier to ignore the fact that I was going to pay later for the lack of sleep.

It was worth it though. This was the best attended Rails Camp yet with about 70 Rails developers converging from different parts of Australia. One of the aspects I like about these events is their self-organising nature. The unconference style encourages learning within small groups for much of the event. In my case I was determined to practice using Cucumber, Webrat and Machinist and benefited from the fact that several others were also cutting their teeth on Cucumber.

There are also plenty of opportunities to share with the whole group. At this camp I was fortunate that Pete Yandell, the author of Machinist, was one of those who spoke. I also thoroughly enjoyed the lengthy discussion which was precipitated by Pat Allan’s talk about Freelancing.

Quite apart from the learning opportunities, I appreciated catching up with other Rails developers I hadn’t seen for a while and making new friendships. I’m looking forward to my next Rails Camp already. Hopefully the number of attendees doesn’t increase. I think it has reached its limit for a good unconference.

Tags: railscamp community rails | Comments (1)

Dynamic CSS with Rails

October 24th, 2008

I’ve sometimes wondered why CSS isn’t more dynamic.

Take, for example, the styles applied to the sub-menu on the services page of this site. To achieve the highlighting of the current service, I combine a Rails view and CSS as follows:

The Rails View

<div id="<%= @current_service.name %>">
  <div id="secondaryNav">
    <ul>
      <% @services.each do |service| %> 
        <li class="<%= service.name %>"><%= link_to service.heading, service_path(service) %></li>
      <% end %>
    </ul>        
  </div>
</div>

The CSS

#agileweb #secondaryNav .agileweb a,
#java #secondaryNav .java a,
#coaching #secondaryNav .coaching a,
#mentoring #secondaryNav .mentoring a,
#overview #secondaryNav .overview a,
#advice #secondaryNav .advice a {
  color: #99CC00;
  text-decoration: underline;
}

Handling a new service

That does the trick nicely until I decide to add a new service. Wouldn’t it be nice to have that CSS regenerated dynamically whenever I create, update or delete a service?

Enter a new Ruby module:

# lib/dynamic_css.rb
module DynamicCss
  def generate_services_nav_links_css
    return if RAILS_ENV == "test"
    FileUtils.cd File.expand_path("public/stylesheets", RAILS_ROOT)
    File.open("servicesnav.css", "w") do |out|
      service_names = []
      services = Service.find :all
      services.each { |s| service_names << s.name }
      service_names.each_with_index do |name, i|
        out.print "##{name} #secondaryNav .#{name} a"
        if i + 1 < service_names.size
          out.puts ","
        else
          out.puts " {"
        end
      end  
      out.puts "  color: #99CC00;"
      out.puts "  text-decoration: underline;"
      out.puts "}"
    end
  end
end

Then a small adjustment to invoke the css regeneration via a filter in my admin services controller:

class Admin::ServicesController < AdminLayoutController
  include DynamicCss
  
  after_filter :generate_services_nav_links_css, :only => [:create, :update, :destroy]

# remainder of controller

end

Lastly, to ensure that the servicesnav.css file exists by the time one of the public services pages is requested:

# config/initializers/services_nav_css.rb

include DynamicCss

generate_services_nav_links_css

Admittedly this is a specific case, but this example shows that it is relatively straightforward to dynamically generate CSS within a Rails app if required.

Tags: css rails | Comments (3)