One of the things that always bothered me about Ruby on Rails apps in general and RESTful routing in general is how quickly it breaks down in the real world. You know the world with clients and designers. It’s all fine and well, your “blogs” page lists all the blogs and “blog/1″ shows the first blog entry and everything is all nice and neat and then someone insists that you have to have a tag cloud on all the pages and maybe a list of most recent comments or a blog roll. The pure and simple joy of the blogs routes loads the blogs model and displays in the blogs views all goes to hell.

Up to now I’ve had two techniques to deal with this and both of them frankly suck.

1) Have the controller load all the models that the view needs. This just reeks. The index action in the blog controller can logically load the blogs but why should it have to know that the designer put a blog roll on that page and it also needs to load those urls along with topics for a tag cloud or comments. I really hate the idea of my controller having to know about design elements when it should be controlling: namely deal with the inputs and data logically associated with that particular resource.

2) Dump all that crap into your application_controller in some filter that get run all the time. At least this is DRY but I really hate the idea of hiding this stuff away where you don’t expect it and can’t find it. When there’s something wrong and you need to address something on the blogs index page you’re naturally going to go to the blogs views/controller/model. Having some truly common stuff in your application controller (current_user) is okay but I’ve too often seen this loading of miscellaneous junk bloat the application_controller beyond reason.

Enter cells. Call em components, or widgets or blocks they’re what pretty much every real world rails app needs. Encapsulate that common element on the side into it’s own bit and then include it like a partial. But like a smart partial that loads the data it needs by itself. It take a partial from a strictly display oriented view thing and adds a lightweight controller to do the controller like stuff we need.

I’d come across cells a while ago and never used them but had a few spare minutes and decided to give it a try and that’s about all it takes. I have a feeling I’ll be using them in just about every project from now on.

Dec 162010

So I have a model that uses an integer to track it’s state which can be one of several predetermined states – proposed, accepted, rejected – which I’ve mapped to an integer. I’ve used ruby magic to make lots of syntactically pretty methods so that I can do things like @proposal.accepted? and can transition it like @proposal.rejected!. It’s all data driven so that I can add new states easily. Off the top of my head:

@@states = {1 => 'Proposed', 2 => 'Accepted', 3 => 'Rejected'}
 
@@states.each_pair do |k,v|
  define_method "#{v.downcase}?" { self.state_id == k }
  define_method "#{v.downcase}!" { self.state_id = k }
end

That’s all great but in a related table I find the need to select only related objects of a certain state. What I initially got is something like this:

has_many accepted_proposals  ... conditions => 'proposals.state_id = 2'

Now if there’s one thing I’ve learned to hate it’s magic numbers sprinkled around the code. What I want is to write something like:

has_many accepted_proposals ... conditions => ['proposals.state_id = ?', Proposal.accepted_state_id]

And so I tried to define it like my other useful utility methods except … you can’t use define_method to make a class method, only an instance method. I didn’t like this but then while discussing it with a colleague I had the thought – method_missing! I’ve never had cause to use to use it before but the idea is pretty simple. Insert your own code that can pattern match against a method name that isn’t previously defined and do something useful. And what you end up with is something like this:

@@state_ids = Hash.new
@@states.each_pair do |k,v|
  @@state_id[v.downcase] = k
end
 
def self.method_missing(method_sym, *arguments, &block)
  if method_sym.to_s =~ /^(.*)_state_id$/
    @@state_ids[$1]
  else
    super
  end
end

So adding a new state to the class variable effectively gives me 3 automagically generated methods, @proposal.state?, @proposal.state! and Proposal.state_state_id. Very sweet.

Now of course I can do this in a more traditional way with a class method that would look something like Proposal.state_id(‘accepted) but that seems vaguely unsatisfying while the method_missing approach just reeks of awesome.

© 2011 nestoriak.com Suffusion theme by Sayontan Sinha