Using strings to drill down into objects

Sorry folks the example was not correct

irb(main):001:0> drill_down = lambda do |obj, method_names|
irb(main):002:1* o ||= obj
irb(main):003:1> method_names.split('.').each {|m| o = o.send(m)}
irb(main):004:1> o
irb(main):005:1> end
=> #<Proc:0x40221e48@(irb):1>
irb(main):006:0> drill_down.call(1,'succ.to_s')
=> "2"
irb(main):007:0> drill_down.call(1,'succ.succ.succ.to_s')
=> "4"
irb(main):008:0>

This is what I currently have working. I'm using it to define the columns
in a report so I need to determine the result of arbitrary fields within
the object model. I just look at the code and think there is a better way.

···

**********************************************************************
This communication may contain CONFIDENTIAL information and may also be the subject of LEGAL PROFESSIONAL PRIVILEGE and/or under copyright. If you are not an intended recipient, you MUST NOT keep, forward, copy, use, save or rely on this communication and any such action is unauthorised and prohibited. If you have received this communication in error, please reply to this e-mail to notify the sender of its incorrect delivery, and then delete both it and your reply. Thank you
**********************************************************************

Ross X Dawson wrote:

Sorry folks the example was not correct

irb(main):001:0> drill_down = lambda do |obj, method_names|
irb(main):002:1* o ||= obj
irb(main):003:1> method_names.split('.').each {|m| o = o.send(m)}
irb(main):004:1> o
irb(main):005:1> end
=> #<Proc:0x40221e48@(irb):1>
irb(main):006:0> drill_down.call(1,'succ.to_s')
=> "2"
irb(main):007:0> drill_down.call(1,'succ.succ.succ.to_s')
=> "4"
irb(main):008:0>

This is what I currently have working. I'm using it to define the columns
in a report so I need to determine the result of arbitrary fields within
the object model. I just look at the code and think there is a better way.

OK, that makes more sense. Still, we don't know what you have to start
with (I guess it's not simply 1 :), and how much flexibility you need in
accessing various things about that thing.

For example, do you really need a lambda as your "drill down" function?
One might think if it's so flexible, a simple method would suffice.

Also, are you sure you have to traverse an object hierarchy by following
arbitrarily specifiable attribute accessors in turn? It seems quite
weird for a data model.

Finally, given that you already use lambdas, you could just use those to
specify the way to access what you're looking for, starting from a given
object.

Just to keep some resemblance to your code (it could be made even simpler):

def drill_down(obj, func)
  yield.call(obj)
end

drill_down(1, lambda{|o| o.succ.to_s}) #=> "2"
drill_down(1, lambda{|o| o.succ.succ.succ.to_s}) #=> "4"

And then you can simply use code blocks passed to the method call:

def drill_down(obj)
  yield obj
end

drill_down(1) {|o| o.succ.to_s} #=> "2"
drill_down(1) {|o| o.succ.succ.succ.to_s} #=> "4"

If the method which calls drill_down receives the lambdas in question as
parameters, you can still pass them on as code blocks:

some_lambda = lambda{|o| o.succ.to_s}
drill_down(1, &some_lambda) #=> "2"

mortee

Ross, I would implement something like that with Enumerable#inject:

  def drill_down(obj, method_names)
    method_names.split('.').inject(obj) {|o, m| o.send(m)}
  end

But I'm not quite sure that having strings with method calls is the
way to go. Where do the strings with the method names come from? Do
the users of your application have to enter them somewhere? What about
arguments to the method calls?

Regards,
Pit

···

2007/10/12, Ross X Dawson <Ross.Dawson@aas.com.au>:

irb(main):001:0> drill_down = lambda do |obj, method_names|
irb(main):002:1* o ||= obj
irb(main):003:1> method_names.split('.').each {|m| o = o.send(m)}
irb(main):004:1> o
irb(main):005:1> end
=> #<Proc:0x40221e48@(irb):1>
irb(main):006:0> drill_down.call(1,'succ.to_s')
=> "2"
irb(main):007:0> drill_down.call(1,'succ.succ.succ.to_s')
=> "4"