Rails, использующие встроенные помощники внутри методов класса

#ruby-on-rails #ruby

#ruby-on-rails #ruby

Вопрос:

Кто-нибудь знает, почему включенный метод не работает внутри метода класса?

 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 >
  

Ответ №1:

Для тех, кто хочет использовать некоторые пользовательские помощники в библиотеке уровня класса или модели, иногда не стоит включать все помощники. Вместо этого вызовите его напрямую:

 class MyClass
  def test
    ::ApplicationController.helpers.number_to_human_size(42)
  end
end
  

(Взято из http://makandracards.com/makandra/1307-how-to-use-helper-methods-inside-a-model )

Ответ №2:

Трудно сказать, не видя вашего вспомогательного кода, но include все методы в этом модуле будут вставлены в экземпляры класса, в который вы включаете. extend используется для переноса методов в класс. Следовательно, если у вас есть только методы, определенные в NumberHelper, они помещаются во все экземпляры MyClass, но не в класс.

Способ, которым работает множество расширений Rails, использует методы, которые были объединены в ActiveSupport::Concern . Вот хороший обзор.

По сути, расширение ActiveSupport::Concern в ваших модулях позволит вам указать в подмодулях, называемых ClassMethods и InstanceMethods, какие функции вы хотите добавить к классам и экземплярам, в которые вы включаете свой модуль. Например:

 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"
  

Я использовал это раньше, чтобы делать такие вещи, как определение функции bar в ClassMethods, а затем также сделать ее доступной для экземпляров, определив метод bar с тем же именем, который просто вызывает this.class.bar, делая его вызываемым из обоих. Есть много других полезных вещей, которые делает ActiveSupport::Concern, например, позволяет вам определять блоки, которые вызываются обратно, когда модуль включен.

Так вот, это происходит именно здесь, потому что вы include используете свой helper, что может указывать на то, что эта функциональность более универсальна, чем helper — помощники автоматически включаются только в представления, поскольку они предназначены только для помощи с представлениями. Если вы хотите использовать своего помощника в представлении, вы могли бы использовать helper_method макрос в своем классе, чтобы сделать этот метод видимым для ваших представлений, или, что еще лучше, создать модуль, как указано выше, и не думать о нем как о вспомогательном средстве, а использовать include, чтобы включить его в классы, в которых вы хотите его использовать. Я думаю, что я бы пошел по этому пути — ничто не говорит о том, что вы не можете создать модуль HumanReadableNumber и include его в NumberHelper, чтобы сделать его легко доступным в ваших представлениях.

Комментарии:

1. Таким образом, мы можем просто расширить класс с помощью помощника, если нам нужны эти функции, верно?

Ответ №3:

Я столкнулся с той же проблемой. Вот как я это решил,

 helper = Object.new.extend(ActionView::Helpers::NumberHelper)
helper.number_to_human_size(1000000)
  

Благодаря RailsForum.

Комментарии:

1. Очень простое решение.. Спасибо 🙂

2. Это не мой вопрос, поэтому я не могу принять ваш ответ как приемлемый. 🙁

Ответ №4:

Я столкнулся с той же проблемой. в моем случае замена include на extend заставила это работать.

 class MyClass
  extend ActionView::Helpers::NumberHelper
  ...
end