Итак, у меня есть несколько контроллеров, которые работают со списками экземпляров модели, и как только я сделал один контроллер, я решил переместить все это, чтобы просто повторно использовать код.

module ListController
  extend ActiveSupport::Concern
  #code
end

Но это привело меня к нескольким вопросам.

В первую очередь мне нужны эти контроллеры для работы с разными ресурсами. Например:

module ListController
  extend ActiveSupport::Concern
  included do
    doll =  self.name.to_s.match(/^(\w+)ListController/)[1]
    @resource = doll.downcase
    @klass = doll.singularize.constantize
    define_method("new_#{@resource}_list")  do
      if appropriate_quantity?
        quantity=params[:quantity].to_i
        array = Array.new(quantity) do
          @klass.new
        end
        instance_variable_set("@#{@resource}", array)
      elsif invalid_entry_cookie?
        invalid_entries_from_redis(@klass)
      else
        redirect_to :back
      end
    end
  end
end

Итак, когда модуль включен, я получаю имя контроллера, нахожу часть перед ListController, и по моему собственному соглашению это приводит меня к необходимой модели и ресурсу:

doll =  self.name.to_s.match(/^(\w+)ListController/)[1]#=>Students
@resource = doll.downcase #=>students
@klass = doll.singularize.constantize #=>Student

Вроде неплохо. Но

1) сам модуль не видит переменных экземпляра. Итак, @ resource и @ klass теряют видимость после строки define_method, и все теряет свою точку. Я не могу сделать модуль достаточно гибким, чтобы его можно было использовать повторно, без того, чтобы эти переменные были видны на всем протяжении включенного блока. Решение?

2) с включенным, передать ли @ resource и @ klass каждому контроллеру? Мне это не нравится, потому что они там просто не нужны. Я бы хотел этого избежать.

Joe Half Face

Ответов: 1

Ответы (1)

1) Вы можете использовать фильтр before_action, чтобы настроить эти переменные экземпляра следующим образом:

module ListController
  extend ActiveSupport::Concern

  included do

    # Set instance vars in the current class
    init_proc = Proc.new do |c|
      doll =  c.class.name.match(/^(\w+)ListController/)[1]
      c.instance_variable_set(:@resource, doll.downcase)
      c.instance_variable_set(:@klass, doll.singularize.constantize)
    end

    # Run the above proc before each page load this method is declared in
    before_action(init_proc)

    # Make @resource and @klass available right now
    init_proc.call(self)

    define_method("new_#{@resource}_list")  do
      if appropriate_quantity?
        quantity=params[:quantity].to_i
        array = Array.new(quantity) do
          @klass.new
        end
        instance_variable_set("@#{@resource}", array)
      elsif invalid_entry_cookie?
        invalid_entries_from_redis(@klass)
      else
        redirect_to :back
      end
    end
  end
end

2) Вы можете включить такую ​​проблему в ApplicationController, чтобы избежать включения везде.

2022 WebDevInsider