Terra Ashley
Terra Ashley

Reputation: 920

Calling a helper from a class and instance method in a Rails model

I need to call a helper method within a model, from both a class and an instance method, e.g. Model.method(data) and model_instance.method. However, the class method always returns "NoMethodError: undefined method 'helper_method' for #<Class ...>"

model.rb:

class Model < ActiveRecord::Base
  include ModelHelper

  def method
    helper_method(self.data)
  end

  def self.method(data)
    self.helper_method(data)
  end
end

model_helper.rb:

module ModelHelper
  def helper_method(data)
    # logic here
  end
end

I even tried adding def self.helper_method(data) in the helper to no avail.

After quite a bit of seraching, I wasn't able to find anything on how to achieve this, or at least anything that worked.

Upvotes: 2

Views: 2329

Answers (3)

Gopal Gupta
Gopal Gupta

Reputation: 26

you need to define model_helper.rb as:

module ModelHelper
  def self.helper_method(data)
    # logic here
  end
end

and call this method in model.rb as:

class Model < ActiveRecord::Base
  include ModelHelper

  def method
    ModelHelper.helper_method(self.data)
  end

  def self.method(data)
    ModelHelper.helper_method(data)
  end
end

Upvotes: 0

jvillian
jvillian

Reputation: 20253

If there's no additional logic in method, then you can simply do:

class Model < ActiveRecord::Base
  include ModelHelper
  extend  ModelHelper
end

And get both the instance (@model.helper_method) and the class (Model.helper_method) methods.

If, for legacy (or other) reasons, you still want to use method as an instance and class method, but method doesn't do anything different than helper_method, then you could do:

class Model < ActiveRecord::Base
  include ModelHelper
  extend  ModelHelper

  alias method helper_method
  singleton_class.send(:alias_method, :method, :helper_method)
end

And now you can do @model.method and Model.method.

BTW, using modules to include methods in classes is seductive, but can get away from you quickly if you're not careful, leaving you doing a lot of @model.method(:foo).source_location, trying to figure out where something came from. Ask me how I know...

Upvotes: 2

Terra Ashley
Terra Ashley

Reputation: 920

The answer turned out to be pretty simple, and doesn't require any Rails magic: you just re-include the helper and define the class method within a class block:

class Model < ActiveRecord::Base
  include ModelHelper

  def method
    helper_method(self.data)
  end

  # Expose Model.method()
  class << self
    include ModelHelper

    def method(data)
      helper_method(data)
    end
  end
end

No changes to the helper needed at all.

Now you can call method on both the class and an instance!

Upvotes: 2

Related Questions