@Object#send@ - the joy of Rubyists and the scourge of those who would write refactoring tools. Let’s talk about it.
I recently ended up writing some code that would have proven really hideous if I couldn’t call @#send@. So I had a class like the following (written using code generation for brevity):
class Foo
%w{one two three}.each do |name|
class_eval <<-EOC
def #{name}
'#{name}'
end
EOC
end
end
I needed to call @one@, @two@ or @three@ from a bit of code that takes some user input (in this case, an attribute value in a template language). So I cooked something like this up:
%w{one two three foo}.each do |arg|
meth = case arg
when 'one'
:one
when 'two'
:two
when 'three'
:three
else
raise 'Unknown arg'
end
puts Foo.new.send(meth)
end
So I’m using a case statement to convert valid method names to symbols, which I then pass to @#send@ and bam!, my method is called. This made my code a ton easier to write and I’m here espousing the technique to you.
But what is the drawback? Well, if someone were to ever really get up the courage to tackle the task of building a refactoring browser for Ruby, this sort of thing would give them fits. They can’t really tell where those methods are called on my class until they are actually called. Heck, given the code above, they can’t even figure out what methods exist on @Foo@ without running the code.
The other drawback is that this code is a little hard to read. Most new casual Rubyists won’t think to search for the symbol version of a method name. Its even harder if you’re still somewhat new to Ruby and you aren’t aware this sort of thing even happens.
For me personally, the concise code I can write with @#send@ far outweighs the drawbacks. I preach the importance of code reading regularly, and its how one can get over the “hump” that is knowing how to navigate software that sends dynamic messages to objects.
Your homework is to share how you feel about @#send@ in the comments.