Rails 5.1 adds delegate_missing_to

Neeraj Singh

By Neeraj Singh

on May 30, 2017

This blog is part of our  Rails 5.1 series.

When we use method_missing then we should also use respond_to_missing?. Because of this code becomes verbose since both method_missing and respond_to_missing? need to move in tandem.

DHH in the issue itself provided a good example of this verbosity.

1
2class Partition
3  def initialize(first_event)
4    @events = [ first_event ]
5  end
6
7  def people
8    if @events.first.detail.people.any?
9      @events.collect { |e| Array(e.detail.people) }.flatten.uniq
10    else
11      @events.collect(&:creator).uniq
12    end
13  end
14
15  private
16    def respond_to_missing?(name, include_private = false)
17      @events.respond_to?(name, include_private)
18    end
19
20    def method_missing(method, *args, &block)
21      @events.public_send(method, *args, &block)
22    end
23end

He proposed to use a new method delegate_missing_to. Here is how it can be used.

1class Partition
2  delegate_missing_to :@events
3
4  def initialize(first_event)
5    @events = [ first_event ]
6  end
7
8  def people
9    if @events.first.detail.people.any?
10      @events.collect { |e| Array(e.detail.people) }.flatten.uniq
11    else
12      @events.collect(&:creator).uniq
13    end
14  end
15end

Why not SimpleDelegator

We at BigBinary have used SimpleDelegator. However one issue with this is that statically we do not know to what object the calls are getting delegated to since at run time the delegator could be anything.

DHH had following to say about this pattern.

I prefer not having to hijack the inheritance tree for such a simple feature.

Why not delegate method

Delegate method works. However here we need to white list all the methods and in some cases the list can get really long. Following is a real example from a real project.

1delegate :browser_status, :browser_stats_present?,
2         :browser_failed_count, :browser_passed_count,
3         :sequential_id, :project, :initiation_info,
4         :test_run, success?,
5         to: :test_run_browser_stats

Delegate everything

Sometimes we just want to delegate all missing methods. In such cases method delegate_missing_to does the job neatly. Note that the delegation happens to only public methods of the object being delegated to.

Check out the pull request for more details on this.

Stay up to date with our blogs. Sign up for our newsletter.

We write about Ruby on Rails, ReactJS, React Native, remote work,open source, engineering & design.