4
votes

I'd like to create a module called StatusesExtension that defines a has_statuses method. When a class extends StatusesExtension, it will have validations, scopes, and accessors for on those statuses. Here's the module:

module StatusesExtension
  def has_statuses(*status_names)
    validates :status, presence: true, inclusion: { in: status_names }

    # Scopes
    status_names.each do |status_name|
      scope "#{status_name}", where(status: status_name)
    end

    # Accessors
    status_names.each do |status_name|
      define_method "#{status_name}?" do
        status == status_name
      end
    end
  end
end

Here's an example of a class that extends this module:

def Question < ActiveRecord::Base
  extend StatusesExtension
  has_statuses :unanswered, :answered, :ignored
end

The problem I'm encountering is that while scopes are being defined, the instance methods (answered?, unanswered?, and ignored?) are not. For example:

> Question.answered
=> [#<Question id: 1, ...>]
> Question.answered.first.answered?
=> false # Should be true

How can I use modules to define both class methods (scopes, validations) and instance methods (accessors) within the context of a single class method (has_statuses) of a module?

Thank you!

1
They are being defined, just not right. If you get a result other than NoMethodError, the method has been defined.Linuxios

1 Answers

4
votes

As the comments have said, the method is being defined, just not working as expected. I suspect this is because you are comparing a string with a symbol within the method (status_names is an array of symbols, and status will be a string). Try the following:

status_names.each do |status_name|
  define_method "#{status_name}?" do
    status == status_name.to_s
  end
end