Me
Blog about Ruby, Rails, and more!

Using define_method to dynamically define predicate methods

2017-07-16

In my last post I demonstrated how class_eval can be used to define predicate methods. You can also define predicate methods in a similar manner using define_method.

Lets take a look at the User class from the previous example:

class User
  POSSIBLE_STATUSES = %w(Active Inactive Disabled Banned)

  attr_accessor :status

  def initialize
    @status = 'Inactive'
  end

  POSSIBLE_STATUSES.each do |status|
    class_eval <<-CODE
      def #{status.downcase}?
        self.status == '#{status}'
      end
    CODE
  end
end

Currently it uses class_eval to dynamically define predicate methods for every possible user status. Lets refactor the User class to use define_method instead.

class User
  POSSIBLE_STATUSES = %w(Active Inactive Disabled Banned)

  attr_accessor :status

  def initialize
    @status = 'Inactive'
  end

  POSSIBLE_STATUSES.each do |status|
    define_method("#{status.downcase}?") do
      self.status == status
    end
  end
end

This will give us a predicate method for every possible status in the POSSIBLE_STATUSES array. We can test each of these methods in irb:

irb(main):039:0> User.new.active?
=> false
irb(main):040:0> User.new.disabled?
=> false
irb(main):041:0> User.new.banned?
=> false
irb(main):042:0> User.new.inactive?
=> true

The default status of a new user is 'Inactive', which we set in the initialize method of our User class. All other predicate status methods return false, since the user can only have one status at a time.

There is a performance benefit to using define_method though, for reasons I will not go into here. However, if you are dynamically defining only a handful of methods you probably won't notice any performance difference, so use whatever syntax you prefer.

If you want to read more about the perforance differences between the two, check out this great post by Aaron Patterson.