3 Practical Uses of Ruby's `method_missing` Worth Knowing.

Ask questions Research chat →

https://manny.codes/3-practical-uses-of-ruby-method-missing/ · scraped

ruby

Attachments

Scraped Content

— 1306 words · 2026-02-14 17:40:59 UTC ·

Excerpt

Ruby’s dynamism is awe-inspiring. One of the methods that contribute to this is method_missing. method_missing is defined in BasicObject. method_missing allows us to: Let’s discuss the practical uses of this method. ## What is Ruby’s method_missing? We define methods inside classes. Ruby traverses a method lookup path when an object calls a method, starting from the object’s class and up the object’s class’s ancestor chain to reach the method. If the method the object calls is available in the lookup path, Ruby calls it. On some occasions, the method isn’t present in the ancestor chain. All method lookups that fail end up reaching a method called method_missing. method_missing lives inside the BasicObject class, the root of all classes. For a class, Aircraft, that mixes in no other modules or prepends other module or inherit other classes, this is how the traversal would look like (I’ve omitted the lookup through eigenclasses for simplicity): ![](https://prod-files-secure.s3.us-w
Ruby’s dynamism is awe-inspiring. One of the methods that contribute to this is method_missing. method_missing is defined in BasicObject. method_missing allows us to: Let’s discuss the practical uses of this method. ## What is Ruby’s method_missing? We define methods inside classes. Ruby traverses a method lookup path when an object calls a method, starting from the object’s class and up the object’s class’s ancestor chain to reach the method. If the method the object calls is available in the lookup path, Ruby calls it. On some occasions, the method isn’t present in the ancestor chain. All method lookups that fail end up reaching a method called method_missing. method_missing lives inside the BasicObject class, the root of all classes. For a class, Aircraft, that mixes in no other modules or prepends other module or inherit other classes, this is how the traversal would look like (I’ve omitted the lookup through eigenclasses for simplicity): ![](https://prod-files-secure.s3.us-west-2.amazonaws.com/871f1661-80b8-4d0c-ac3b-2adfc6ff4c66/ae09de22-40f1-41fb-8ae5-b76353800b2e/ruby-method-missing-lookup.gif?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466XCHE7Z3W%2F20260214%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20260214T174059Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEEEaCXVzLXdlc3QtMiJHMEUCIFYlnvCqzw2BeMygFHkD%2FKPCnewc4pabFPRvzqBO%2FduOAiEArFdfAX1U5KVSz9dspL9dtuD3tW6kiGW%2Bk3ljBU3d7HIq%2FwMIChAAGgw2Mzc0MjMxODM4MDUiDCGSvFzRwB2rBWXeaSrcA%2BzSPPO6JGE1N%2BmnSto1goV3yRnGOGkwwM2UbmUkst82JUAlzYn2%2FKkbncDT4ev8iFXt%2ByahoSRtKchOyLJU0SoXPpbpicI1rbXIeIs2X4qiTdrs7Pitfu7Jnyqng3q%2F%2BUZrxZ1d%2BILmpaqE7Tdsi6isXkRhvo3dZsnhK1VDQf%2FGbI%2BpdAiChxIqxd356H6gBwyYf4TgkmMR26I7bYXhOIVDWEU9XIYip4d%2BhzqCg%2BTm8CrY2Ror%2BZihb0V16VOvEXpEK9OPovZPggcUnxIV%2BSn7JviZeVSPaGgB0aTqwV%2FRWUKzzjlQqNXwe%2Bn0fIxpJ6GjWeAAUA2JlJYSkP8JXVJLhwnXHJ2W82d6i2e9u4K08uKu1WIqqyv96TKPZ%2FNwYonpDFOEC0%2FCjFZHkIwkZX9zXClIi2cqOzV3kN3OAuYtUug53k4s0qo4DbfwrCNnpk1S0Mz4Emgn1gkohTe8ESC2ghXV2CNT9BfAzHj19FzABl74q%2FHY2ZfHZSAKzyr9WjKsWQvDt10D7QfrzNo3Cbw2FxcOrJNZhhpuBNjRq5kLdqop98YJz1jA8lYC6sNieanP55Tc7CCT1eMGROZE5e19pqXuxrmEyyFyH4POHJtZxaFo8UCcwbu6dI6KMLvRwswGOqUBk77Uc3xujMB5AAz7b%2FuNcqRRlNxOvQ3M%2FV%2B5MWYviL14QXtXCjZAYzyKaVxTezYT8C7Du4lup6UjqGoJ2flazJ%2BtpIxS4Uqo6uRp0gZvx3DBopwEJRD8QzT1xezDX9Mkj%2FHElpaIp4F1idFJoH1bBlNuP%2B1wWiMZJgIXTwTDnJhqiZT4l0PfD0Ij507bmHbEkqMCINvkGgaTyDa2t2mwG3wngoos&X-Amz-Signature=7f4360fd12428b00858401b6213afb6efbc572fc6ae8d0f7c2d44e7813c73f11&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject) 3 Practical Uses of Ruby method_missing ## method_missing For Error Handling Perhaps the most common use of method_missing is its support for gracefully handling errors. When an object invokes a method, Ruby navigates the method lookup chain looking for the method to execute. On reaching BasicObject, method_missing is called. The interpreter raises an error at this point. Here’s an example: Here’s what happens when you call an inexistent method on an object: When the search for a #fly method in the inheritance tree ends in vain, then the method call aircraft.fly will output <main>: undefined method 'fly' for #<Aircraft:0x00007fe66c1591e0 @passengers=0> (NoMethodError). It would be nice to be able to handle this error in a friendlier way. Ruby permits this by making programmers override method_missing. method_missing takes three parameters; a method name, its list of arguments and a block. The last two are optional. To handle these errors, you intercept the call to BasicObject’s method_missing with your own. Here’s a rewrite of our Aircraft code: With this change, we now have 'method_missing': You called add_passengers with [275]. This method doesn't exist. (NoMethodError) as output, this looks more welcoming than the error we saw before. This example shows the extent to which Ruby allows programmers to own their code. A slightly more sophisticated example may involve the use of super where if someone calls an inexistent method, you’d want first to perform some operation on the method and then only pass control to the original method_missing method as shown in this example from Ruby’s documentation: ```plain text class Roman def roman_to_int(str) # ... end def method_missing(symbol, *args) str = symbol.id2name begin roman_to_int(str) rescue super(symbol, *args) end end end r = Roman.new r.iv #=> 4 r.xxiii #=> 23 r.mm #=> 2000 r.foo #=> NoMethodError ``` With more complicated usages of method_missing, come more significant responsibilities. When you override method_missing, you have to make sure that whatever method you call inside it is available. Otherwise, you’ll end up going in circles and eventually get a SystemStackError. Expanding on the Aircraft class, here’s something more meaningful. If a method call on an Aircraft instance starts with “add_”, we call public_send on the instance passing it a setter method that sets the variable of whatever comes after “add_”. ```plain text class Aircraft attr_accessor :passengers def initialize @passengers = 0 end def method_missing(method_name, *args, &block) if method_name =~ /add_(.*)/ public_send("#{Regexp.last_match(1)}=", *args) else super end end end aircraft = Aircraft.new aircraft.add_passengers(275) p aircraft.passengers #=> 275 p aircraft.respond_to?(:add_passengers) #=> false ``` In the method_missing method, if the method called on any instance of Aircraft matches “add_”, we call a setter method and pass it the arguments that came with the method call. However, it would help if you took note that respond_to “add_passengers” returns false, this is not true, and that’s why it’s good practice always to override respond_to_missing? method anytime you override method_missing. In our case, we add: ```plain text def respond_to_missing?(method_name, include_private = false) method_name =~ /add_(.*)/ || super end ``` ```plain text class Aircraft attr_accessor :passengers def initialize @passengers = 0 end def method_missing(method_name, *args, &block) if method_name =~ /add_(.*)/ public_send("#{Regexp.last_match(1)}=", *args) else super end end def respond_to_missing?(method_name, include_private = false) method_name =~ /add_(.*)/ || super end end aircraft = Aircraft.new aircraft.add_passengers(275) p aircraft.passengers #=> 275 p aircraft.respond_to?(:add_passengers) #=> true ``` ## Method Delegation With method_missing According to Wikipedia, delegation refers to evaluating a member (property or method) of one object (the receiver) in the context of another original object (the sender), this means, an object is asking another object to perform the actions in the method the receiver is calling. You can use Ruby’s method_missing to delegate methods to another class. Here’s a contrived example: ```plain text class Referee def red_card? puts "true" end end class Linesman def initialize(referee) @referee = referee end def method_missing(method, *args) @referee.send(method) end end linesman = Linesman.new(Referee.new) linesman.red_card? # => true ``` In the code above we call red_card? on linesman, an instance of Linesman, but it’s an instance of Referee that does the job, this is an example of some form of delegation. Ruby provides a library called delegate in its Standard Library that you might want to check out first if you need to do serious method delegation, ## Building DSLs And Libraries With method_missing A domain-specific language (DSL) is a language with which you can do specific tasks. Unlike Ruby, a DSL like RSpec only useful for testing. DSLs are relatively easier to write. DSLs are everywhere. A few examples are: - Capistrano - Rake - Sinatra - Rails Routing - factory_bot Ruby’s method_missing makes it very easy to build DSLs. Let’s try our shot with the simplest XML generator. Here’s a modified snippet I grabbed from The Ruby Programming Language book. ```plain text class XML class XML def initialize(output) @output = output end def method_missing(tag, attributes = {}) @output << "<#{tag}" if block_given? @output << '>' content = yield @output << content.to_s if content @output << "</#{tag}>" else @output << '/>' end nil end def self.generate(output, &block) XML.new(output).instance_eval(&block) end end ``` This code allows us to write something like: ```plain text XML.generate(STDOUT) do html do head do title "Page Title" end body do strong do "Strong Text" end end end end ``` Running this produces: ```plain text <html><head><title/></head><body><strong>Strong Text</strong></body></html> ``` And just like that, we’ve harnessed the power of method_missing to create a DSL that generates XML. An excellent example of a DSL that relies on method_missing is The Late Jim Weirich’s Builder gem. The Builder gem generates XML markup from blocks, much like what we just wrote but more sophisticated. Another example that might seem familiar is Ruby on Rails’s dependence on method_missing to create dynamic finders like the find_by_* group of methods in ActiveRecord. ## Question On The Traversal For method_missing I learned yesterday from a group that Ruby does the traversal for method_missing twice. The first one is to reach the method_missing in BasicObject, and the second traversal is to check if you overrode method_missing: this makes sense, otherwise, how would Ruby pick up the method_missing you’ve overwritten. I haven’t found any reference on this. Please share if you have some documentation somewhere about this fact. ## Summary We’ve seen how to use method_missing to handle errors and to do method delegation. With the help of another metaprogramming construct, we saw how to use instance_eval to write a DSL that generates XML. We looked at how Ruby traverses the ancestor chain to find methods and calls method_missing if it finds nothing. We learned that it’s a good idea to overwrite respond_to_missing? any time we overwrite method_missing?; this covers the list of things you can do with method_missing. If you can think of any other use cases, please share them in the comments below. Consider subscribing to my newsletter for a chance to get my upcoming eBook collection of rad Ruby idioms, tips & tricks or follow me on Twitter to explore Ruby, JavaScript and web technologies.

Visibility

Visible to everyone

Reading Status

Related Bookmarks

My Note


Saved!

Annotations

Export as Markdown
+ Annotate selection

Add Annotation