fbpx

Module Decorators

Why decorate objects instead of using inheritance?

It’s a more dynamic, flexible and transparent alternative to subclassing. There’s several different ways of using the decorator pattern in Ruby, but I’m just going to show the module/extend approach. You can read about other decorator techniques in Dan Croak’s blog post.

Use a module and extend to add instance methods on an object. This will still maintain the ability to use super as demonstrated in this trivial example:

class Report
  def number_of_pages
    10
  end
end

module CustomerAdmendment
  def number_of_pages
    super + 5
  end
end

module AdminAdmendment
  def number_of_pages
    super + 1
  end
end

report = Report.new
report.number_of_pages # => 10
report.extend(CustomerAdmendment).number_of_pages # => 15
report.extend(AdminAdmendment).number_of_pages  # => 16

With the module/extend technique we can maintain the original interface and delegate through all the decorators.

Ruby’s Safe Navigation Operator

nil‘s in Ruby can be annoying. Thankfully, Ruby version 2.3.0 gave us a safe navigation operator, something that other languages like C# and Swift already had. It’s very much like ActiveSupport’s try method, which checks if a receiver responds to a method before calling that method. If the receive doesn’t respond to the method, nil is returned.

Usage

Here is a trivial example to demonstrate how this works. Let’s say you need to call upcase an object attribute that may or may not be defined. At some point, this might happen:

person = Struct.new(:first_name)
bob = person.new
=> #<struct first_name=nil>

bob.first_name.upcase
# => undefined method `upcase' for nil:NilClass

Before Ruby 2.3.0, we might have solved this problem with something like:

bob.first_name.upcase if bob.first_name
=> nil

Using the Safe Navigation Operator (&.) we can do this:

bob.first_name&.upcase
=> nil

In this example, calling first_name on the bob instance returns nil, so &. halts the method chain right there.