Obama '08
New-formula-starburst

Generic actions for Rails subclasses

1

Single Table Inheritance in Ruby on Rails is cool, and has made my code oh so beautiful in several cases. I ran across an interesting problem yesterday with inheritance classes, and I thought I’d share my solution.

Imagine you have parent class NinjaTurtle, and four subclasses Leonardo, Donatello, Michaelangelo and Raphael.

class NinjaTurtle < ActiveRecord::Base
end

class Leonardo < NinjaTurtle
  has_many :katana
end

class Donatello < NinjaTurtle
  has_one :bo
end

class Michaelangelo < NinjaTurtle
  has_many :nunchaku
end

class Raphael < NinjaTurtle
  has_many :sai
end

So a typical new or create action in any turtle’s controller would look like a bit like this

@donatello = Donatello.new

What if you want one inherited action called new that will create any type of NinjaTurtle, depending on who’s controller handled the request?

The straightforward way would be to just write an action called new in each of the subclasses controller, but that’s a lot of repetition of basically the same thing.

The solution? Make sure your subclass controller inherits its parent class controller, then you can write a generic new action in the parent controller.

class NinjaTurtleController < ApplicationController
  def new
    @ninja_turtle = params[:controller].camelcase.constantize.new
  end
end

class LeonardoController < NinjaTurtleController ; end
class DonatelloController < NinjaTurtleController ; end
class MichaelangeloController < NinjaTurtleController ; end
class RaphealController < NinjaTurtleController ; end

Now, any request to /leonardo/new would create a new Leonardo object and assign it to @ninja_turtle. Similarly, a request to /donatello/new would create a new Donatello object. The beauty is that there is really only one new method that is flexible enough to create the right type of object depending on the controller that handled the request.

How it works

I discovered yesterday that Ruby’s class names are constants. That means that there is a constant named Leonardo that refers to the Leonardo class.

If we’re using Rails convention, which we should be, the controller name that corresponds to the Leonardo class is leonardo. We can grab the controller name from request parameters.

  params[:controller]                        #=> "leonardo" 
  params[:controller].camelcase              #=> "Leonardo" 

and then we use the Rails built-in method constantize to convert our string into a constant, and invoke the new method on the object that we’re trying to create. It essentially evalutates to Leonardo.new, which is exactly what we wanted!

References:
Programming Ruby
Infovore – Getting a class object in Ruby from a string containing that class’s name

Comments

  • Avatar Derek said about 1 year later:

    Shouldn’t the controllers be “NinjaTurtlesController” and “LeonardosController”?

    If so, then params[:controller].constantize.camelcase does not work because the model would be “NinjaTurtles” instead of “NinjaTurtle” which would cause an error…

    Also, how do the subclassed controllers share views? If we are subclasses controllers to keep things DRY, shouldn’t we do the same for the views? I can’t, for the life of me, figure out how to get subclassed controllers share the views of its parent class in a nice clean manner…

Trackbacks

Use the following link to trackback from your own site:
/articles/trackback/279

  • From Viagra uk buy viagra online order viagra.
    Order viagra.
    Order viagra online. Order uk viagra. Order viagra buying viagra uk. Viagra for order lamisil viagra. Viagra uk buy viagra online order viagra.
  • From zipper gay magazine
    zipper gay magazine
    zipper gay magazine

(leave url/email »)

   Comment Markup Help Preview comment