πŸ“œ ⬆️ ⬇️

Ghost Methods in Ruby (translation)

I bring to your attention the translation of a fragment of the book Metaprogramming Ruby 2 by Paolo Perrotta.

What is method_missing?


In Ruby, we can call methods that don't exist, but this will return an error to us. For example:

class Lawyer; end nick = Lawyer.new nick.talk_simple NoMethodError: undefined method 'talk_simple' for #<Lawyer:0x007f801aa81938> 

Do you remember how the search method works? When you call the talk_simple method, Ruby goes to the nick object class and enumerates the methods there. If he cannot find a method there, he will look for it in the parents of this class, then in Object and finally in BasicObject. Well, since Ruby cannot find the talk_simple method anywhere, it runs the method_missing method for nick. Ruby knows that this method is there, because it is a private BasicObject method from which all objects are inherited.

You can call this method for experiment yourself. Since the method is private, you can do it like this:
')
 nick.send :method_missing, :my_method NoMethodError: undefine method 'my_method' for #<Lawyer:0x007f801aa81938> 

You just did what Ruby does, you told the object β€œI just tried to call the method: my_method, but it doesn't, so return me an error.” BasicObject # method_missing returns NoMethodError, which is what it is for.

Redefinition of method method_missing


Most likely, you will never need to call the method_missing method yourself. Instead, you can rewrite this method to intercept unknown calls. Each such call stores information about the method names and the arguments that were passed.

 class Lawyer def method_missing(method, *args) puts "You called: #{method}(#{args.join(', ')})" puts "(You also passed it a block)" if block_given? end end bob = Lawyer.new bob.talk_simple('a', 'b') do # a block end You called: talk_simple(a, b) (You also passed it a block) 

Redefining the method method_missing gives you the opportunity to call methods that are not actually described, that is, do not exist. Consider a little more detail.

Ghost methods


When you need to declare a lot of similar methods, calling them through method_missing will help you to escape from this. It's like telling an object that β€œIf a method is called which doesn't exist, just do it.” From the side of the method call that works through method_missing, everything looks like a normal call to the usual method, but inside the class, this method simply does not exist. It is this trick that is called the Ghost Method. Let's look at the examples.

Hashie example


Heme Hashie contains a bit of magic called Hashie :: Mash. Mash is a more powerful version of the standard Ruby OpenStruct library, is a hash-like object, whose attributes work as Ruby variables. If you need a new attribute, you simply declare the values ​​for that attribute. Here is how it works.

 require 'hashie' icecream = Hashie::Mash.new icecream.flavor = 'strawberry' icecream.flavor # => 'strawberry' 

This works because Hashie :: Mash is a subclass of Ruby Hash, and these attributes are essentially ghostly methods. Here is how method_missing is implemented:

 module Hashie class Mash< Hashie::Hash def method_missing(method_name, *args, &blk) return self.[](method_name, &blk) if key?(method_name) match = method_name.to_s.match(/(.*?)([?=!]?)$/) case match[2] when "=" self[match[1]] = args.first # ... else default(method_name, *args, &blk) end end # ... end end 

If the name of the method being invoked is the name of the key in the hash (as flavor), then Hashie :: Mash # method_missing simply calls the [] method to return the corresponding value. If the name ends with β€œ=”, then method_missing cuts off the extra characters and gets the value to be saved. If the name does not contain something else, then method_missing simply returns some standard value. Hashie :: Mash also contains other special characters, such as β€œ?”.

respond_to_missing?


If you want to test the ghost method with respond_to ?, then Ruby is understandable to you.

 john = Lawyer.new john.respond_to?(:talk_simple) # => false 

This can be problematic, since we can often use respond_to for checks. But Ruby provides a good mechanism for making friends with respond_to and ghostly methods.

respond_to? calls the respond_to_missing method? which returns true if it is a ghost method. In order for this to not bring more problems during development, override every time along with method_missing and respond_to_missing.

 class Lawyer # ... def respond_to_missing?(method, include_private = false) methods.include?(method) || super end end bill = Lawyer.new bill.respond_to?(:talk_simple) # => true 

A little earlier, the rubists redefined themselves respond_to? method. But now there is a respond_to_missing? and override respond_to? is considered to be the wrong way to work with method_missing.

Ghost Methods is only a small part of the interesting features in Ruby.

Source: https://habr.com/ru/post/269785/


All Articles