views:

223

answers:

5

Let's say I have an XML::Element...I want to do something like:

my_xml_element.send("parent.next_sibling.next_sibling")

+4  A: 

In your case it's better to use instance_eval

"Test".instance_eval{chop!.chop!} #=> "Te"

And for your code:

my_xml_element.instance_eval{parent.next_sibling.next_sibling}
khelll
+2  A: 

uh, that's not really what he was asking for if I'm understanding his question correctly. I mean send takes a string or a symbol as an arg, and your solution doesn't. I don't think there's a built in method that will do what you want, but I whipped up a method that will, with a test.

require 'test/unit'

class Example
  def multi_send(str)
    str.split('.').inject(self){|klass, method| klass.send(method) }
  end
end

class MyTest < Test::Unit::TestCase  
  def test_multi_send
    a = Example.new
    methods = "class.to_s.downcase.chop!"
    assert a.multi_send(methods) == 'exampl'
  end
end
brianthecoder
A: 

I think the question is that you specifically have a series of methods defined as a string, and you want to invoke that on some object, right?

class Object
  def call_method_chain(method_chain)
    return self if method_chain.empty?
    method_chain.split('.').inject(self){|o,m| o.send(m.intern)}
  end
end

>> User.first.profile.address.state.name
=> "Virginia"
>> User.first.call_method_chain("profile.address.state.name")
=> "Virginia"
crankharder
Sorry, this won't actually work outside the context of Rails since Object#try isn't defined.
crankharder
Edit: Works w/o Rails now.
crankharder
A: 

Actually, Khelll was almost right. Use this:

methods_chain = "parent.next_sibling.next_sibling"
result = my_xml_element.instance_eval( eval methods_chain )

This code is up to 20 times faster than using split() and allows you to pass chain methods with args, like this:

methods = "power!(2).div(57).+(12).to_f"
42.instance_eval { eval methods }
Alfuken
A: 

The problem with Alfuken's answer based on eval is

A) eval is pretty unsafe although fast

B) if you have an extra (consecutive) dot for method invocation like this

"example".instance_eval{eval "chop!..chop!"}.class
=> "examp".."examp"

"example".instance_eval {eval "chop!..chop!"}.class
=> Range # this is not what is desired
daya