coderrr

April 24, 2008

Nest and Yield

Filed under: rails, ruby — Tags: , — coderrr @ 11:35 am

I recently ran into an fairly uncommon situation. I needed to nest multiple method calls around a single block:

Model1.transaction do
  Model2.transaction do
    Model3.transaction do
      all_models.each{|o| o.do_something! }
    end
  end
end

The uncommon part was that the objects I was calling the methods on could potentially change. So I had an array containing all of them: model_classes = [ Model1, Model2, Model3 ]. Now the dilemma is given the array how do you produce the above effect, dynamically. I came up with this:

  do_something = lambda { all_models.each {|o| o.do_something! } }
  model_classes.reverse.inject(do_something) do |l, obj|
    lambda { obj.transaction { l.call } }
  end.call

Here’s a clearer way to see how the above builds up the chain of lambdas in reverse:

  lambda_do_something = lambda { all_models.each {|o| o.do_something! } }
  lambda_model3 = lambda { Model3.transaction { lambda_do_something.call } }
  lambda_model2 = lambda { Model2.transaction { lambda_model3.call } }
  lambda_model1 = lambda { Model1.transaction { lambda_model2.call } }

  lambda_model1.call

Now abstract it away and we get nest_and_yield!!

class NestedYielder
  instance_methods.each { |m| undef_method m unless m =~ /^__/ }  # start fresh

  def initialize(enum); @enum = enum; end

  def method_missing(m, *args, &blk)
    @enum.reverse.inject(blk) do |l, obj|
      lambda { obj.send(m, *args) { l.call } }
    end.call
  end
end

module Enumerable
  def nest_and_yield
    NestedYielder.new(self)
  end
end

Here’s some examples of how to use it:

  model_classes.nest_and_yield.transaction { ... }
  model_classes.nest_and_yield.with_scope(:some_conditions => :wahtever) { ... }
  # => Model1.with_scope(:some_conditions => :whatever) { Model2.with_scope(:some_conditions => :whatever) { ... } }
  mutexes.nest_and_yield.synchronize { ... }
  # => mutex1.synchronize { mutex2.synchronize { ... } }

It really needs a better name, any suggestions?

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Silver is the New Black Theme. Create a free website or blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 28 other followers

%d bloggers like this: