ActiveSupport try method and method_missing

Joan R
1 min readAug 22, 2017

--

Today I was working on a project where there was a class that looked (roughly) like this:

class LazyColor
def initialize(real_color_id, name)
@real_color_id = real_color_id
@name = name
end
def method_missing(method, *args, &block)
if real_color.respond_to?(method)
real_color.public_send(method, *args)
else
super
end
end
attr_reader :real_color_id, :name private def real_color
@real_color ||= Color.find(real_color_id)
end

So, if we send a message to a LazyColor instance and it can’t answer, it will try to use the real_color to respond for it. Let’s see an example:

real_color = Color.create(name: 'red', intensity: 20)
lazy_color = LazyColor.new(real_color.id, name: real_color.name)
puts lazy_color.intensity
=> 10

As you can see, even though lazy_color doesn’t answer to intensity, we were able to get the value from the real_color, because the method_missing implementation of LazyColor delegated to that real_color. Now, imagine a scenario that we don’t know if we are going to have a LazyColor, or a nilvalue, so we want to take advantage of try, like this:

real_color = Color.create(name: 'red', intensity: 10)
lazy_color = LazyColor.new(real_color.id, name: real_color.name)
puts lazy_color.try(:intensity) || 20
=> 20

The reason this gives us 20 is because how try is implemented, it basically invokes the public method of that object itself, without ever going through method_missing.

I found this problem interesting because I have seen many projects where the code has to contemplate nil objects. Specifically in our case, we were doing try just in case instead of a lazy_color we had a nil object. By doing that, we actually caused a regression on how lazy_color handles messages that are not part of its public interface.

--

--

Joan R
Joan R

Written by Joan R

Software engineering, management, cooking, education, homeschooling, investing and personal growth are my main interests right now.

No responses yet