переменные класса и включение модуля, особенно в ActionController - PullRequest
2 голосов
/ 03 марта 2010

Я хочу иметь какой-то один список, который инициализируется в отдельном модуле, затем может быть включен в контроллер и изменен на уровне класса контроллера, а также доступен на уровне экземпляра контроллера.Я думал, что здесь будут работать переменные класса, но происходит что-то странное, кажется, что они не инициализируются в моем конечном классе.

Более конкретно:

У меня много контроллеров, включая некоторыефункциональность по умолчанию, например, в модуле.

class BlahController < ApplicationController
  include DefaultFunctionality
end

class FooController < ApplicationController
  include DefaultFunctionality
end

 module DefaultFunctionality 
   def show 
     render 'shared/show'
   end 
   def model
     controller_name
   end
 end

.Это не реальный код, но это наибольшее взаимодействие, которое у него есть на данный момент.

Я бы хотел расширить это с помощью некоторой другой функциональности (сортируемый интерфейс для списков), например, так [примечание I 'я хотел бы иметь возможность обменивать функциональность списка порядка сортировки по классам на основе классов]:

module DefaultFunctionality
 module Sortable
  def sort_params
    params.slice(:order, :sort_direction).reverse_merge(default_sort_params)
  end
  def default_sort_params
    @@sorts.first
  end
  def set_sorts(sorts = []) #sorts = [{:order => "most_recent", :sort_direction => :desc},...]
     @@sorts = sorts
  end
 end
 include Sortable
 set_sorts([{:order => :alphabetical, :sort_direction => :asc}] #never run?
end

Идея состоит в том, чтобы убедиться, что я могу поменять множество всехвозможные сортировки по классам, например, так:

class FooController < ApplicationController
  include DefaultFunctionality #calls the default set_sorts
  set_sorts([{:order => :most_recent, :sort_direction => :asc}]) 
end

А также для создания хороших ссылок в представлениях, как показано ниже, за исключением того, что я получаю сообщение об ошибке.

___/blah/1 => shared/show.html.erb__
<%= link_to("upside down", polymorphic_path(model, sort_params) %><%#BOOOM uninitialized class variable @@sorts for BlahController %>

Я считаю, что class_var - плохой вызов, но я не могу думать о том, что еще я мог бы использовать.(экземпляр класса var?)

Ответы [ 3 ]

2 голосов
/ 03 марта 2010

Варианты экземпляров класса - это, безусловно, путь. Редко вам действительно нужно использовать переменную класса.

В конкретном ответе на вашу проблему имейте в виду, что любой код, определенный в вашем модуле, выполняется только один раз , когда модуль загружен , а не когда модуль включен. Это различие может быть неочевидным, особенно когда люди считают «включать» эквивалентным «требовать».

Что вам нужно, это:

module DefaultFunctionality
  def sort_params
    params.slice(:order, :sort_direction).reverse_merge(default_sort_params)
  end

  def default_sort_params
    @sorts.first
  end

  def sorts=(sorts = nil)
    @sorts = sorts || [{:order => "most_recent", :sort_direction => :desc}]
  end

  def self.included(base_class)
    self.sorts = ([{:order => :alphabetical, :sort_direction => :asc}]
  end
end

Способ, которым вы ловите класс, включающий ваш модуль, состоит в том, чтобы определить Module.included соответственно. В этом случае set_sorts вызывается каждый раз, когда этот модуль включен, и находится в контексте вызывающего класса.

Я немного изменил это, чтобы включить несколько конкретных изменений стиля:

  • Объявление ваших значений по умолчанию внутри метода, а не в объявлении. Предотвращает создание нечитабельно длинных строк или использование однострочных сложных структур данных.
  • Использование переменных экземпляра класса, которые будут выполнять работу в этом случае.
  • Использование метода var = mutator в стиле Ruby вместо set_var ()
0 голосов
/ 04 марта 2010

В итоге я использовал переменную экземпляра уровня класса и включил () без автоматически включаемых подмодулей, прежде чем заметил, что @tadman ответил.

В итоге все выглядело так:

class BlahController < ApplicationController
  include DefaultControllerFunctionality
  include DefaultControllerFunctionality::Sortable
end

class FooController < ApplicationController
  include DefaultControllerFunctionality
  include DefaultControllerFunctionality::Sortable
  sorts=([{:order => :most_recent, :sort_direction => :desc}])
end

в контроллерах и в / lib

Библиотека / default_controller_functionality.rb

 module DefaultFunctionality 
   def show 
     render 'shared/show'
   end 
   def model
     controller_name
   end
 end

lib / default_controller_functionality / sortable.rb

 module DefaultControllerFunctionality
   module Sortable
    def self.included(base)
      base.helper_method :sort_params
      base.class_eval <<-END
          @sorts=[]
          class << self; attr_accessor :sorts end
       END
       #this line ^ makes it so that the module 
       #doesn't work when included in any other 
       #module than the class you want @sorts to end up in. 
       #So don't include(Sortable) in DefaultControllerFunctionality and expect .sorts to work in FooController.

      base.sorts= [{:order => :alphabetical, :sort_direction => :asc}]
    end
    def sort_params
      params.slice(:order, :sort_direction).reverse_merge(default_sort_params)
    end
    def default_sort_params
      self.class.sorts.first
    end
   end
 end
0 голосов
/ 03 марта 2010

Вы должны добавить метод included в свой модуль, чтобы это работало.

module DefaultFunctionality

 def self.included(base)
   base.include(Sortable)
   set_sorts([{:order => :alphabetical, :sort_direction => :asc}] #never run?
 end

 module Sortable
  # other methods

  def set_sorts(sorts = [{:order => "most_recent", :sort_direction => :desc}])
     @@sorts = sorts
  end
 end 
end
...