Why can't I use method call inside "module" block for define_method?

57 views Asked by At

I have a model class with attr_reader - type_slugs (returns an array of string) which I'd like to use to generate several methods.

For example:

module Orders
  module Extensions
    module Order
      module TypeCheckable
        extend ::ActiveSupport::Concern

        ::Orders::Config.type_slugs.each do |type|
          define_method("#{type}_order_type?") { order_type == type }
        end
      end
    end
  end
end

But it gives me NoMethodError: undefined method `type_slugs' for Orders::Config:Module

Same error happens if I use any model method like:

some_method_defined_in_model.each do |type|
  define_method("#{type}_order_type?") { order_type == type }
end

But it works just fine if I use a word array

%w(message product another).each do |type|
  define_method("#{type}_order_type?") { order_type == type }
end

Is it even possible to use Orders::Config.type_slugs in this case? If not, why so?

1

There are 1 answers

0
mechnicov On BEST ANSWER

From the information you provided, it follows that the module Orders::Config does not have type_slugs method

If you want your code works, you need define it, something like this:

module Orders
  module Config
    def self.type_slugs
      %w[message product another]
    end
  end
end

Or if that list doesn't change dynamically:

module Orders
  module Config
    TYPE_SLUGS = %w[message product another].freeze
  end
end

In that case change ::Orders::Config.type_slugs to ::Orders::Config::TYPE_SLUGS

When use Rails concerns, you should use included for instance methods

module Orders
  module Extensions
    module Order
      module TypeCheckable
        extend ::ActiveSupport::Concern

        included do
          ::Orders::Config::TYPE_SLUGS.each do |type|
            define_method("#{type}_order_type?") { order_type == type }
          end
        end
      end
    end
  end
end

After that you can use it in your models

class SuperOrder < ApplicationRecord
  include Orders::Extensions::Order::TypeCheckable
end

class BestOrder < ApplicationRecord
  include Orders::Extensions::Order::TypeCheckable
end

And call methods like this

SuperOrder.new.product_order_type? # compare super order type with "product"
BestOrder.new.message_order_type? # compare best order type with "message"