Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails using included helpers inside class methods

Does anybody know why the included method doesn't work inside a class method?

class MyClass
  include ActionView::Helpers::NumberHelper

  def test
    puts "Uploading #{number_to_human_size 123}"
  end

  def self.test
    puts "Uploading #{number_to_human_size 123}"
  end
end


ree-1.8.7-2011.03 :004 > MyClass.new.test
Uploading 123 Bytes
 => nil 
ree-1.8.7-2011.03 :005 > MyClass.test
NoMethodError: undefined method `number_to_human_size' for MyClass:Class
    from /path/to/my/code.rb:9:in `test'
    from (irb):5
ree-1.8.7-2011.03 :006 >
like image 533
Brian Armstrong Avatar asked Jun 09 '11 23:06

Brian Armstrong


1 Answers

It's hard to tell without seeing your helper code, but include will insert all of the methods in that module into instances of the class you include into. extend is used to bring methods into a class. Therefore, if you just have methods defined in NumberHelper, these are being put onto all instances, but not the class, of MyClass.

The way that lots of Rails extensions work is using techniques that have been consolidated into ActiveSupport::Concern. Here is a good overview.

Essentially, extending ActiveSupport::Concern in your modules will allow you to specify, in sub-modules called ClassMethods and InstanceMethods, what functions you want to be added to classes and instances into which you include your module. For example:

module Foo
    extend ActiveSupport::Concern
    module ClassMethods
        def bar
            puts "I'm a Bar!"
        end
    end
    module InstanceMethods
        def baz
            puts "I'm a Baz!"
        end
    end
end

class Quox
    include Foo
end

Quox.bar
=> "I'm a Bar"
Quox.new.baz
=> "I'm a Baz"

I've used this before to do things like define the bar function in ClassMethods, then also make it available to instances by defining a bar method of the same name that just calls this.class.bar, making it callable from both. There are lots of other helpful things that ActiveSupport::Concern does, like allowing you to define blocks that are called back when the module is included.

Now, this is happening here specifically because you're includeing your helper, which might indicate that this functionality is more general-purpose than a helper - helpers are only automatically included in views, since they are only intended to help with views. If you want to use your helper in a view, you could use the helper_method macro in your class to make that method visible to your views, or, even better, make a module as above and not think about it as a helper, but use include to mix it in to the classes you want to use it in. I think I would go that route - there's nothing that says you can't make a HumanReadableNumber module and include it in NumberHelper to make it easily available across your views.

like image 97
Matt Avatar answered Nov 15 '22 22:11

Matt