1

I moved my code into a module and now I get an error...

undefined method `delegate_fields_to' for Registration::Car::StepOne

However, the code worked fine before I put it in the module. What am I missing?

module Registration
  class Base
    module ActAsDelegation

      def self.form_fields_mapping
        [{}]
      end

      def self.fields_of_model(model)
        form_fields_mapping.select {|record| record[:model] == model }.map {|record| record[:name] }
      end

      def self.delegate_fields_to(*models)
        models.each do |model|
          fields_of_model(model).each do |attr|
            delegate attr.to_sym, "#{attr}=".to_sym, to: model if attr.present?
          end
        end
      end
    end
  end
end



module Registration
  class Base
    include ActiveModel::Model
    attr_reader :user
    include ActAsDelegation
  end
end


module Registration
  module Car
    class StepOne < Registration::Base
      delegate_fields_to(:car, :truck)
    end
  end
end
mrzasa
  • 22,895
  • 11
  • 56
  • 94
user2012677
  • 5,465
  • 6
  • 51
  • 113

1 Answers1

2

If you want to use methods of a mixed-in module on class level, you need to

  1. use instance methods in module and
  2. use extend instead of include

When you include a module into a class, you add its instance methods to instance of this class. When you extend a class with a module, you add module instance methods to the class. In the latter case you can use that methods in the class body.

See below:

 module Registration
  class Base
    module ActAsDelegation

      def form_fields_mapping
        [{}]
      end

      def fields_of_model(model)
        form_fields_mapping.select {|record| record[:model] == model }.map {|record| record[:name] }
      end

      def delegate_fields_to(*models)
        models.each do |model|
          fields_of_model(model).each do |attr|
            delegate attr.to_sym, "#{attr}=".to_sym, to: model if attr.present?
          end
        end
      end
    end
  end
end

module Registration
  class Base
    include ActiveModel::Model
    attr_reader :user
    extend ActAsDelegation
  end
end

See also:

mrzasa
  • 22,895
  • 11
  • 56
  • 94
  • What if my module has both class methods and instance methods? can I keep them in one module? and somehow include or extend all the methods in the class? – user2012677 Aug 30 '19 at 20:19
  • You mean you want to add class and instance methods to the class that get the module? – mrzasa Aug 30 '19 at 20:21
  • I would like to have Registration::Base::ActAsDelegation hold both class and instance methods – user2012677 Aug 30 '19 at 20:22
  • See the `A Common Idiom` section in http://www.railstips.org/blog/archives/2009/05/15/include-vs-extend-in-ruby/. – mrzasa Aug 30 '19 at 20:23
  • thank you, for `def self.included(base)`, what is the base value and where does it come from? do I need to assign it or call `self.included?` in the main class? – user2012677 Aug 30 '19 at 20:29
  • `base` is the module/class being enriched (in you case `Registration::Base`) https://ruby-doc.org/core-2.2.1/Module.html#method-i-included. `included` is a hook method called by the interpreter when a module is being included, and it takes care of passing this value, you don't need to call it manually. – mrzasa Aug 30 '19 at 20:32