views:

548

answers:

1

I have the following module

module SharedMethods

    # Class method
    module ClassMethods

        #
        # Remove white space from end of strings
        def remove_whitespace
            self.attributes.each do |key,value|
                if value.kind_of?(String) && !value.blank?
                    write_attribute key, value.strip
                end
            end
        end


    end

    #
    #
    def self.included(base)
        base.extend(ClassMethods)
    end

end

and I am using it in my models like

include SharedMethods
before_validation :remove_whitespace

However whenever I submit the form I get a "undefined method `remove_whitespace'" message

What can I do to correct this error?

A: 

That's because :remove_whitespace must be an instance method, not a class method.

module SharedMethods

  def self.included(base)
    base.send :include, InstanceMethods
  end

  module InstanceMethods

    # Remove white space from end of strings
    def remove_whitespace
      self.attributes.each do |key,value|
        if value.kind_of(String) && !value.blank?
          write_attribute key, value.strip
        end
      end
    end

  end

end

Unless you need the module to provide both class and instance methods, you can also skip the usage of self.included and simplify your module in this way:

module SharedMethods

  # Remove white space from end of strings
  def remove_whitespace
    self.attributes.each do |key,value|
      if value.kind_of(String) && !value.blank?
        write_attribute key, value.strip
      end
    end
  end

end
Simone Carletti
There will be other methods in this module so would like to stay with using self.includeddoing the above now throws this error private method `include' called for #<Class:0x27416c8>/Users/andy/rails_apps/test_app/vendor/rails/activerecord/lib/active_record/base.rb:1964:in `method_missing'/Users/andy/rails_apps/test_app/lib/shared_methods.rb:7:in `included'
Andy Donovan
private error is coming from base.include. Most direct way around is to work through send: `base.__send__(:include, InstanceMethods)`
Justin Love
You're right. You should either use send(:include, Module) or class_eval { include Module }.
Simone Carletti
@justin whoa! You should never need to include InstanceMethods in a module. That's the point of a module. Just put the methods in the module body and include it ;)
Yehuda Katz
Yehuda that's an interesting point. However, it seems the most part of Rails core modules/plugins uses this architecture. Are you suggesting to avoid the ClassMethods/InstanceMethods convention? I personally find it more readable and intuitive.
Simone Carletti