Accessible Rails Applications – Let Cucumber help you test for Accessibility

Posted by Brian in Accessibility, Rails (August 27th, 2010)

Developers working with Rails are constantly looking for ways to use Cucumber to test their JavaScript. One thing I’ve come to love about Cucumber’s default setup is that it does not work with JavaScript at all, which actually helps me ensure that my applications work for uses without JavaScript enabled.

if I write a Cucumber story that tests to see if a delete works, and I’ve used the default “link_to” method for deletes made popular by the Rails scaffolding, my cucumber feature will fail. I’ll be shown the Show page instead of the “successfully deleted” message.

Keep that in mind as you work through your applications – See what stories break when you don’t have JavaScript enabled and reconsider your implementations.

Reordering Records with acts_as_list and Metaprogramming

Posted by Brian in Howto, Metaprogramming, Rails (May 28th, 2010)

Using acts_as_list, you can reorder items by adding a “position” column to your database and then easily sort records.

  class Project < ActiveRecord::Base
     acts_as_list
  end
 
  class Task < ActiveRecord::Base
     acts_as_list
  end

The acts_as_list plugin also provides you methods to manipulate the position.

  • @project.move_to_top
  • @project.move_to_bottom
  • @project.move_higher
  • @project.move_lower

In a current project, I have multiple items that need reordering, which means I’d be duplicating a lot of controller actions and other code across my controllers and views. I’ll show you how I used metaprogramming to significantly reduce that duplication.

The Route To Reordering

First, we need some routes. Add this to your routes.rb file:

  %w{projects tasks}.each do |controller|
    %w{move_higher move_lower move_to_top move_to_bottom}.each do |action|
      instance_eval <<-EOF
        map.#{action}_#{controller.singularize} "#{controller}/:id/#{action}", {:controller => "#{controller}", :action => "#{action}"}
      EOF
    end
  end

We use an array of controllers, then we have another array of the four actions, and we just make the named routes. This generates

    move_higher_project        /projects/:id/move_higher                            {:action=>"move_higher", :controller=>"projects"}
      move_lower_project       /projects/:id/move_lower                             {:action=>"move_lower", :controller=>"projects"}
     move_to_top_project       /projects/:id/move_to_top                            {:action=>"move_to_top", :controller=>"projects"}
  move_to_bottom_project       /projects/:id/move_to_bottom                         {:action=>"move_to_bottom", :controller=>"projects"}

        move_higher_task       /tasks/:id/move_higher                            {:action=>"move_higher", :controller=>"tasks"}
         move_lower_task       /tasks/:id/move_lower                             {:action=>"move_lower", :controller=>"tasks"}
        move_to_top_task       /tasks/:id/move_to_top                            {:action=>"move_to_top", :controller=>"tasks"}
     move_to_bottom_task       /tasks/:id/move_to_bottom                         {:action=>"move_to_bottom", :controller=>"tasks"}

Metaprogramming really cuts down on work when you use it correctly. This turns out to be a pretty nice way of generating route patterns that share common attributes.

Keep The Control Centralized

Now let’s step it up a notch. Each controller is going to need the four reordering methods. The logic will be identical except for the object they work on and the URL they redirect to when they’re finished.

Let’s create a module that adds controller methods to match these routes. We’ll add it into our controllers where we need it.

Create the file lib/reorder_controller_actions.rb:

  module ReorderControllerMethods
 
      %w{move_higher move_lower move_to_top move_to_bottom}.each do |action|
        define_method action do
          klass = self.class.name.split("::").last.gsub("Controller", "").downcase.singularize.camelize.constantize
          item = klass.find(params[:id])
          item.send(action)
          flash[:notice] = "The #{klass.to_s} was reordered."
          redirect_to :action => "index"
        end
      end
  end

That module defines four controller actions and calls the corresponding method on the model, then does a redirect back to the index action. It’ll be the same everywhere I use it, so I also gain consistency with metaprogramming.

We need to add this to the bottom of config/environment.rb so that our controllers can make use of it.

  require 'reorder_controller_methods'

Then, in each controller where we need this module, we mix it in with include

class ProjectsController < ApplicationController
  include ReorderControllerMethods
end
 
class TasksController < ApplicationController
  include ReorderControllerMethods
end

Stop and REST for a second

Is it appropriate for me to do it this way? I’m going to argue that while technically the position of something could be sent to the update action, I want specific redirection and notification to occur when I change the order of elements. The logic was getting too nasty in the update action, so I split each thing apart, so I went with this approach instead.

An Improved View

Next, we need some buttons for our index pages with arrows on them so the user can trigger the ordering. How about a simple helper that can generate all four buttons for us?

  def reorder_buttons(object)
    thing = object.class.name.camelize.downcase
    result = ""
    [
      {:action => "move_higher", :label => "&uarr;"},
      {:action => "move_lower", :label => "&darr;"},
      {:action => "move_to_top", :label => "&uarr;&uarr;"},
      {:action => "move_to_bottom", :label => "&darr;&darr;"}
    ].each do |item|    
      result << button_to("#{item[:label]}", send("#{item[:action]}_#{thing}_path", object) )
    end
    result
  end

We use an array hold the buttons. We use hashes within the array to map the action to the label of the button. We then iterate and generate buttons for each one, concatenating to a string that we then return.

Then in our index views, we just need to call this:

  <%=reorder_buttons @project %>

That generates exactly what I need – four buttons, one for each reordering action.

Wrapping Up

This solution easily allowed me to add reordering to a lot of controllers in only a few minutes. Hopefully it will help you do the same. You could improve this by storing the methods in a constant so that you didn’t have to duplicate the array all the time, and I’m sure there are other improvements that I could make as well. I’d love to hear from you.

Making “as_string” Attribute Readers for ActiveRecord

Posted by Brian in Howto, Metaprogramming, Rails, tips (March 15th, 2010)

Occasionally, I need to transform boolean model attributes like “active” to display “active” or “inactive” instead of “true” or “false” when making reports or views. A lot of times this means writing some kind of helper method like this:

def active_or_inactive(object, true_message, false_message)
  object.active ? true_message : false_message
end

and calling it like this:

  <%= active_or_inactive(@project, "Active", "Inactive" %>

That’s not a bad approach, and it helps keep the views slightly cleaner by keeping the logic out, but it ends up being more characters than simply using a ternary operator in the view. I’ve used a slightly different approach in some of my more recent projects and I thought I should share it with you.

Move It To The Model

That’s right, I’m advocating pushing that helper into the model itself. I can hear you now, yelling something about “this guy doesn’t know what he’s talking about! How dare he put display logic in his models!” But before you close your browser, allow me to explain.

It just so happens that I need this logic not only in my views, but in my text-based reports that I run outside of the web server. I could mix the module with the helpers in when I needed it, but there’s also something un-object-oriented that bugs me about helpers. They remind me of PHP a bit. I feel like I should be calling object.active_as_string("Active", "Inactive") instead. So that’s what I’m going to do.

First, a unit test, because we’re all good professionals that write tests first. I want to call a method called active_as_string which takes two parameters – the string to print when it’s true and the string to print when it’s
false. Here are my tests:

require 'test_helper'
 
class ProjectTest < ActiveSupport::TestCase
 
  test "should display 'Active' if active" do
    p = Project.new(:active => true)
    assert_equal p.active_as_string("Active", "Inactive"), "Active"
  end
 
  test "should display 'Inactive' if not active" do
    p = Project.new(:active => false)
    assert_equal p.active_as_string("Active", "Inactive"), "Inactive"
  end
end

Tests help me design the method’s use up front. With two failing tests as my guide, I can now take my first stab at making the method work:

class Project < ActiveRecord::Base
   def active_as_string(true_message, false_message)
      self.active ? true_message : false_message
   end
end

With that implemented, my tests pass. However, I also have a “closed” boolean I need to handle, and it would also be nice if I could display “No description” if a project’s description was blank. I could write my own _as_string methods like I’ve done already, but instead, I’ll do a little metaprogramming to generate what I need.

Let’s add four more test cases – to test the “closed” and the “description” fields.

  test "should display 'Closed' if closed" do
    p = Project.new(:closed => true)
    assert_equal p.closed_as_string("Closed", "Open"), "Closed"
  end
 
  test "should display 'Open ' if not closed" do
    p = Project.new(:active => false)
    assert_equal p.closed_as_string("Closed", "Open"), "Open"
  end
 
  test "should display 'No Description' if description is nil" do
    p = Project.new(:description => nil)
    assert_equal p.description_as_string("No Description"), "No Description"
  end
 
  test "should display the description if it exists" do
    p = Project.new(:description => "Hi there!")
    assert_equal p.description_as_string("No Description"), "Hi there!"
  end

Now, let’s build some methods!

ActiveRecord::Base.columns

Every ActiveRecord class has a class method called columns that returns a collection of column objects. The Column object describes each database column and lets you determine its type and its name. We can use that and class_eval to generate a whole bunch of methods at runtime.

 
class Project < ActiveRecord::Base
  self.columns.each do |column|
 
    if column.type == :boolean
 
      class_eval <<-EOF
 
        def #{column.name}_as_string(t,f)
          value = self.#{column.name}
          value ? t : f
        end
 
      EOF
 
    end
  end
end

In this example, we’re creating the _as_string method for each boolean column. It takes two parameters and is basically the same code we already used in our original method earlier. Notice how class_eval can do string interpolation using Ruby’s #{} syntax. That makes it easy to build up the method names.

We can use that same concept to do the same for any other methods – we’ll just cast them to strings and check to see if they are blank.

  class_eval <<-EOF
 
    def #{column.name}_as_string(default_value)
     value = self.#{column.name}.to_s
     value.blank? ? default_value : value
    end
 
  EOF

We throw that into the else block and our whole example looks like this:

  class Project < ActiveRecord::Base
 
    self.columns.each do |column|
 
      if column.type == :boolean
 
        class_eval <<-EOF
 
          def #{column.name}_as_string(t,f)
            value = self.#{column.name}
            value ? t : f
          end
 
        EOF
 
      else
 
      class_eval <<-EOF
 
          def #{column.name}_as_string(default_value)
           value = self.#{column.name}.to_s
           value.blank? ? default_value : value
          end
 
        EOF
 
      end
 
    end
  end

If you run your tests now, they all pass. But our work isn’t done – this isn’t very DRY. We may want to use this in another class too.

Modules!

Create a new module and mix the behavior into your models. Create the file lib/active_record/as_string_reader_methods.rb (create the active_recordfolder if it doesn’t exist already) and put this code in the file:

  module ActiveRecord
    module AsStringReaderMethods
     def self.included(base)
       create_string_readers(base)
     end
 
     def self.create_string_readers(base)
      base.columns.each do |column|
 
         if column.type == :boolean
 
           class_eval <<-EOF
 
             def #{column.name}_as_string(t,f)
               value = self.#{column.name}
               value ? t : f
             end
 
           EOF
         else
 
           class_eval <<-EOF
 
             def #{column.name}_as_string(default_value)
               value = self.#{column.name}.to_s
               value.blank? ? default_value : value
             end
 
           EOF
         end
       end
     end
    end
  end

It’s mostly the same code we had before, but in this case we’re using the self.included method to trigger the method creation on the model that includes the module.

Now, remove the code from your Project mode and replace it with

include AsStringReaderMethods

Run your tests, and everything should pass. You now have a module you can drop into your projects and you’ll have this functionality yourself. Now it’s up to you to expand upon this, and use this pattern in your own work if you find it useful.

Good luck!

Rails and SQL Server – “There is no text for object”

Posted by Brian in Howto, Rails, tips (February 23rd, 2010)

I recently moved a Rails application to a new SQL Server 2005 server on a recent project and everything seemed to go smoothly, but when I tried to fire up a connection to the database from my Rails application, I was greeted with

ActiveRecord::StatementInvalid: DBI::DatabaseError: 42000 (15197) [FreeTDS][SQL Server]There is no text for object 'people'.: EXEC sp_helptext people

The “people” table here is actually a view that gets used all over the place in multiple applications. The DBA had moved the databases from an older SQL Server 2000 database previously.

The solution was to ensure that the application’s user account had the “view definition” permission on the view in question as well as the “select” permission. On the view, in the SQL Server Management Studio, right click and choose “Properties”. Then choose Permissions select your user account, and then select the “View definition” permission. Checking the box under the “Grant” column was enough for me to make it work.

Interestingly enough, the production server (which was upgraded months ago from SQL Server 2000 to 2005), does not have the permission set, but still works fine.

Hopefully someone else finds this useful.

Why Rails?

Posted by Brian in News, Rails (February 16th, 2010)

NAPCS was a proud sponsor of the first Chippewa Valley Ruby Camp, a day-long Ruby training camp where 23 students learned how to build and deploy their first Rails application. I taught two of the three sessions and had a great time helping other developers get their hands on what I believe to be the best way to develop scalable, maintainable, and stable web applications today. That’s a pretty bold statement, but I believe in it, and it’s why NAPCS uses Rails on all new client projects. (In fact, every project since 2006 has been a Rails project.)

Rails projects are quick to launch

With Rails, we can build and launch a prototype application in an extremely short time. On average, we can have something simple in front of the client in less than a couple of days, which is much faster than our previous projects where we used ASP or PHP. And that project isn’t usually a throwaway project; we can tweak it and move forward, from prototype to production.

Rails applications are easily testable

Professionals write tests that prove the code works as it should, and since testing is built right in to the Rails framework. testing is an easy natural part of the process. Testing has always been possible regardless of the language used, but with Rails, it’s so easy to produce well-tested code that you’d be foolish not to test. For my customers, that means much better products, and less support calls.

It’s a standard framework

I occasionally pick up projects from other developers, and while I can’t always ensure that the quality of the code will be good, I at least already know my way around the project because, in a Rails application, conventions dictate where things go. This means the learning curve is lower when we transition an application, and the customer doesn’t get billed extra time for me to figure out what’s going on.

The community is incredible

We rely heavily on open-source projects to get stuff done, and Rails has an amazing community that is always pushing the limits of what Rails applications can do. There is a new solution to a new problem almost every day, and that keeps us all on our toes. Plus, we’re very proud to be sponsoring the Rails Mentors project, which helps other developers get better at Rails development. We’re always giving back to open source, too.

It gets out of the way.

This is the most important point of all; Rails lets me deliver features. Instead of spending hours wiring up database tables to web pages, I can do that in five minutes and spend more time focusing on user experience and new features. And since it isn’t difficult to build things incrementally, I don’t get boxed in. I can make changes without feeling that I’ll lose days of work. It allows me to respond flexibly to new feature requests.

Rails gives us a competitive advantage. We cannot always compete on price alone, but we can provide better-quality solutions than others because we embrace an open, agile framework that lets us deliver stable, scalable, well-tested, and maintainable web applications.

Want to learn how you can take advantage of Ruby on Rails?

Contact us for information on customized training and mentoring services. We offer affordable hourly rates for remote mentoring, as well as custom training classes upon request.

Autocomplete forms in Rails 2.3.3

Posted by Brian in Howto, Rails (August 28th, 2009)

When Rails 2 was released, the core team dropped some of the Javascript Ajax helpers like in-place editing and autocompleting, and released them as plugins. However, if you’ve ever tried to use them with a modern Rails application, you may have encountered problems with routing and authenticity tokens. In this brief post, I’ll walk you through a trivial example that shows how to make Autocomplete work, using the advantages that Rails 2.3 has, including format responses.

First, let’s get a basic Rails app running with a Projects table with a single field, the project’s name :

 rails autocomplete
 cd autocomplete
 ruby script/generate model project name:string
 ruby script/generate resource projects
 rake db:migrate

Next, let’s load some test data.

 ruby script/runner "
 ['The Ninja project', 'The Top Secret project', 'World Domination'].each do |p|
    Project.create(:name => p)
 end
 "

Grab the autocomplete plugin from Rails’ Github repository and install it as a plugin:

 ruby script/plugin install git://github.com/rails/auto_complete.git

Our basic app is installed, but we need to implement a controller action to display projects.

Open app/controllers/projects_controller.rb and add this method:

  def index
    @projects = Project.all :limit => 10
    respond_to do |format|
      format.html
    end
  end

When the user requests HTML, we’ll show the last 10 projects created. Good enough for now. Let’s implement the layout and the view.

Open app/views/layouts/application.html.erb and build a very simple layout:

<html>
  <head>
    <title>Autocomplete</title>
    <% =javascript_include_tag :defaults %>
  </head>
 
  <body>
    <%= yield %>
  </body>
</html>

This very basic layout loads the default Javascript fiels for Rails and yields the template.

Now open app/views/projects/index.html and add this code:

<div id="search">
  <% form_tag(projects_path(), {:method => :get, :class => "form"}) do %>    
    <%=text_field_tag "query"%>
    <%=submit_tag "Search"%>
  <% end -%>
</div>
<div id="projects">
  <% if @projects.any? %>
    <ul>
      <%=render @projects %>
    </ul>
  <% else %>
    <p>There are no projects to show.</p>
  <% end %>
</div>

Here we have a list of projects, and when we click the submit button, we’ll filter the results. We’re using render here, and when you pass the render helper a collection, it looks for a partial that it can use render each of the elements in the collection.

Create app/views/projects/_project.erb and add this line:

<li><%=h(project.name)%></li>

Now we have to do the filtering in the controller. Since the search box is simply applying a filter to the list of projects, it makes sense for us to use the index action for the request. This means changing the controller’s index action slightly.

def index
  keyword = params[:query]
  if keyword.present?
    keyword = "%#{keyword}%"
    @projects = Project.all :conditions => ["name like ?", keyword], :order => "name asc", :limit => 10
  else
    @projects = Project.all :limit => 10, :order => "created_at desc"
  end
 
  respond_to do |format|
    format.html
    format.js do
      render :inline => "<%= auto_complete_result(@projects, 'name') %>"
    end
  end
end

If the query parameter comes in, then we want to filter. We build up a LIKE query and get the results. If no query parameter is present, we just get the top ten recent projects.

However, this is really messy. Let’s get that search logic out of the controller.

Open app/models/project.rb. Move the search logic into a new method:

def self.find_all_by_keyword(keyword)
  if keyword.present?
    keyword = "%#{keyword}%"
    @projects = Project.all :conditions => ["name like ?", keyword], :order => "name asc", :limit => 10
  else
    @projects = Project.all :limit => 10, :order => "created_at desc"
  end
end

Now change app/controllers/projects_controller.rb to call the method instead of doing the search:

def index
  @projects = Project.find_by_keyword(params[:query])
  respond_to do |format|
    format.html
    format.js do
      render :inline => "<%= auto_complete_result(@projects, 'name') %>"
    end
  end
end

Much better. At this point you have a non-Javascript search for your projects in a very short time.

Let’s add the autocomplete functionality. First, in the view, change the text field to this:

<%= text_field_with_auto_complete :project, :name, 
    { :name => "query" },
    {:method => :get, :url => projects_path(:format => :js) } %>

This sets up the autocomplete field to send its queries to our index action, but specifies that we should use the “js” format, perfectly appropriate for this, as it’s a Javascript-only request, so we should provide our response with Javascript.

Open the controller and modify the controller’s code so it looks like this:

def index
  @projects = Project.find_all_by_keyword(params[:query])
  respond_to do |format|
    format.html
    format.js do
      render :inline => "<%= auto_complete_result(@projects, 'name') %>"
    end
  end
end

The only real change is adding the format.js block of code. Here, we’re using Rails’ inline renderer which renders ERb code. Within that ERB, we’re calling the auto_complete_response helper provided by the autocomplete plugin to render the specially formatted list that the auto compelte text field expects. We’re using inline rendering here, but you could use a view with the .js extension. Since it’s one line, I’ll save the file I/O.

With the responder in place, the autocomplete should start working. This is a pretty simple example, but it does illustrate how to make the autocomplete plugin work in a current Rails application, without working about

Saving without callbacks

Posted by Brian in Howto, Rails, tips (August 19th, 2009)

Occasionally, you need to do something to a model without invoking callbacks. This is especially necessary for instances where you need to modify the record you just saved in an after_save callback. For example, say you want to create a hash based on the record’s ID. You won’t have the ID until after it’s been saved, and if you call self.save in an after_save callback, you’re going to end up in an endless loop.

It turns out that ActiveRecord has two private methods.

create_without_callbacks

and

update_without_callbacks

Remember that private in Ruby is less about keeping you from calling the methods and more about letting you know that you shouldn’t depend on them. With Ruby’s send method, using either of these in your application is easy.

Note that in Ruby 1.9, send doesn’t allow calling private methods, but send! does.

  class Project < ActiveRecord::Base
    after_save :create_hash_from_name_and_id

    private
      def create_hash_from_name_and_id
        self.hash = Digest::SHA1.hexdigest("#{self.id}#{Time.now.to_s}#{self.name}")
        self.send :update_without_callbacks
      end
  end

It's my opinion that these methods should not be private. If I can skip validations with save(false), then I should be able to skip callbacks as well.

There are other patterns you can use to skip callbacks.

  class Project < ActiveRecord::Base
    attr_accessor :skip_callbacks

    with_options :unless => :skip_callbacks do |project|
      project.after_save :create_hash_from_name_and_id
      project.after_save :do_more_stuff
    end

    private
      def create_hash_from_name_and_id
        self.skip_callbacks = true
        self.hash = Digest::SHA1.hexdigest("#{self.id}#{Time.now.to_s}#{self.name}")
        self.save
      end
  end

So there you go. Go forth and skip callbacks at will.

Using a class method with or without named scopes

Posted by Brian in Howto, Rails (August 17th, 2009)

Here’s a quick one, building off of the previous post on Geokit and Named Scopes.

If you have defined a class method you want to run which operates on a collection of records, you can share it with named scopes.

class Project < ActiveRecord::Base
  validates_presence_of :name
  named_scope :completed, :conditions => {:completed => true}

  # returns array of names for projects
  def self.names
    scope = self.scoped({})
    scope.all.collect{|record| record.name}
  end
end

This allows you to do

@project_names = Project.names

Or

@project_names = Project.completed.names

If you return the scope, you can continue chaining. If you don’t return the scope, then you can’t. This comes in handy though for creating a method that returns other objects than the object you’re working with. Here’s a completely contrived example:

Say you want to find the actual hours you’ve spent on all your completed projects:

class Project < ActiveRecord::Base
  named_scope :completed, :conditions => {:completed => true}

  def self.tasks
    tasks = []
    self.scoped({}).all.each do |project|
      tasks += (project.tasks)
    end
    tasks
  end

  def self.actual_hours
    hours = 0
    tasks.each do |task|
      hours += task.hours
    end
    hours
  end

Grab the value like this:

actual_hours = Project.completed.actual_hours

Now, of course there are other ways to do this, and this particular method will result in a lot of queries. If you were doing this hours counting in a real app, you’d be better off using SQL. This example illustrates the point though. Now go build something more awesome.

Geokit and named_scope

Posted by Brian in Rails, tips (August 11th, 2009)

I discovered that the geokit_rails plugin doesn’t support named_scope. For those unfamiliar, named_scope methods can be chained to narrow down a query while only performing one query. Here’s an example:

class Business < ActiveRecord::Base
  acts_as_mappable
  named_scope :active, :conditions => {:active => true}
  named_scope :nonprofit, :conditions => {:nonprofit => true}
end

@active_businesses = Business.active
@nonprofits = Business.nonprofit
@active_nonprofits = Business.active.nonprofit

Geokit has a really nice syntax for locating businesses within a certain distance.

@businesses = Business.find :all, :origin => "742 Evergreen Terrace, Springfield IL", :within => 10

However, you simply cannot, out of the box, use this with named_scopes. So I cannot do

@businesses = Business.active :origin => "742 Evergreen Terrace, Springfield IL", :within => 10

The way Geokit exposes itself to ActiveRecord is just not compatible.

RailsCasts to the Rescue!

Ryan Bates, Railscasts producer and all-around awesome guy, has posted a solution in which you can build your own dynamic named scopes. We can create our own scope for Geokit, because the Geokit-Rails plugin provides a public method to retrieve the distance SQL fragment.

First, we add a new named scope to our model

   named_scope :map_conditions, lambda { |*args| {:conditions => args} }

Next, we add our own method to handle the scopes.

    def self.by_location(options ={})
      scope = self.scoped({})
      scope = scope.map_conditions "#{distance_sql(options[:origin])} <= #{options[:within]}"
      scope
    end

We use this method to append our own conditions to the anonymous scope. we use the distance_sql method which takes whatever we pass in via our :origin option, and we concatonate the equality. In this case, we want distance less than or equal to our distance.

We can now do exactly what we wanted, although with modified syntax.

@businesses = Business.active.nonprofit.by_location :origin => "742 Evergreen Terrace, Springfield IL", :within => 10

Pretty amazing stuff.

Use Modules, not Inheritence to share behavior in Ruby

Posted by Brian in Howto, Rails, snacks (August 10th, 2009)

When working in Rails, I often find that several classes have the same behavior. A common approach to share code between objects is the tried-and-true method of inheritance, wherein you create a base class and then extend your subclasses from the parent. The child classes get the behaviors from the inheritance. Basically, Object Oriented Programming 101.

Languages like Java and Ruby only let you inherit from one base class, though, so that isn’t gonna work. However, Ruby programmers can use modules to “mix in” behavior.

Mixing it up and Mixing it in

Let’s say we have a Person class.

class Person
  def sleep
    puts "zzz"
  end
end

Now let’s say we need to define a Ninja. We could create a Ninja class and inherit from Person, but that’s not really practical. Ruby developers really don’t like changing the type of a class just because its behavior changes.

Instead, let’s create a Ninja module, and put the behaviors in that module.

module Ninja
  def attack
    "You are dead."
  end
end

We can then use this multiple ways.

Mixing it in to the parent class

In a situation where we would like every Person to be a ninja, we simply use the include directive:

  class Person
    include Ninja
  end

Every instance of the class now has the Ninja behaviors. When we include a module, we are mixing in its methods to the instance. We could also use the extend method, but this mixes the methods in as class methods.

Notice here that we redefined the class definition. This appends to the existing class; it does not overwrite it. This can be a dangerous technique if used improperly because it can be difficult to track down. This is the simplest form of monkeypatching.

There’s an alternative method.

Mixing in to an instance

We can simply alter the instance of an object, applying these behaviors to a specific instance and affecting nothing else.

  @brian = Person.new
  @brian.extend Ninja

Here, we use the extend method because we need to apply these as class methods to the object instance. In Ruby, classes are objects themselves. Our instance needs to be extended.

A more practical exaple – Geolocation

I was working on a project this weekend where I had several models that needed latitude and longitude data pulled in via Google’s web service. I used the GeoKit gem and the Geokit-Rails plugins to make this happen, but I soon noticed I was adding the same code to multiple classes.

acts_as_mappable
after_validation_on_create :geocode_address

def geocode_address
  geo=Geokit::Geocoders::MultiGeocoder.geocode (address)
  errors.add(:address, "Could not Geocode address") if !geo.success
  self.lat, self.lng = geo.lat,geo.lng if geo.success
end

It seemed immediately apparent that this should go in a module which could be included into my classes. However, I wanted to also make the two class method calls – the after_validation_on_create callback and the acts_as_mappable macro.

Ruby has a facility for that type of situation

self included

def self.included(base)
#
end

This method is called when a module is included into a class, and it gives you access to the class that’s doing the including. You can use this as a handle to call any class methods. With that, my geolocation module looks like this:

module Geolocation

  def self.included(base)
    base.acts_as_mappable
    base.after_validation_on_create :geocode_address
  end

  def geocode_address
    geo=Geokit::Geocoders::MultiGeocoder.geocode (address)
    errors.add(:address, "Could not Geocode address") if !geo.success
    self.lat, self.lng = geo.lat,geo.lng if geo.success
  end

end

So now any class that needs geolocation just needs to look like this:

class Business
  include Geolocation
end

class Nonprofit
  include Geolocation
end

Summary

The above problem could have been solved by using a parent class like MappableOjbect that included the code, but it then makes putting in additional behavior more difficult. Using modules to share code is the preferred way to attach behaviors to objects in your applications.

Next Page »