Kernel#send should be deprecated and removed from future rubies.
send always felt weird when I was an active ruby developer but I have never seen any strong criticisms on it. It's either an attack on all family of send-like calls or a fierce defence that "using
send is not a bad practice". Only some style guides argue against
send because it may overlap with existing methods. This post was written after reading one of defences.
send belongs to a family of 3 methods that allow calling a method by its name as a parameter:
BasicObject#__send__, the base method
Kernel#send, alias for
Kernel#public_send, that calls only public methods
My problem is only with the second one.
What's wrong with
"What is the problem? After all it's only an alias!". Two arguments combined:
It breaks encapsulation
It looks perfectly normal
Breaking encapsulation is a too common practice in a Ruby world. Monkey patch a library? Why not!
bundle update now breaks the project? It always does that! Let's invent updating practices instead of not interfering with guts of external libraries.
Non-public methods are not in any library's interface and are never covered by semver promise for example so you work with them at your own risk. If you do it regularly, it's a code smell.
Let's update a field dynamically:
But look again at these calls. The first one looks more natural, more logical and it is simply shorter. This technically makes this metaprogramming pattern unsafe by default. Maybe if they were named
send, this post wouldn't exist.
Ok, maybe the library is bugged and there is some error with caches
# this looks like a regular call # you may easily miss it: obj.send :update_caches, [:key1, :key2] # this looks like a code smell # so if there is no huge explanation # why it is done and when it can be removed, # you do `git blame` and go hit someone with a hammer: obj.__send__ :update_caches, [:key1, :key2]
The second method clearly shows us that you're messing with some internals and you must have a good reason for that. This weirdly looking call is easy to spot even if you're looking through code without proper attention.
public_sendis fine as long as you put only trusted data in the first parameter. It's a very useful and safe tool for metaprogramming. It's one of that parts that I love about the language.
__send__is a base feature of the language, a building block that cannot be easily removed or hidden. Second, as stated above, the method name with double underscores on both sides clearly shows that something hacky is happening and indicates a potential code smell.