类变量和模块包含,特别是在ActionController中

时间:2010-03-03 20:03:17

标签: ruby-on-rails ruby module actioncontroller class-variables

我希望在单独的模块中初始化某种单一列表,然后可以将其包含在控制器中并在控制器级别进行修改,并在控制器实例级别进行访问。我认为类变量可以在这里工作,但是发生了一些奇怪的事情,它们似乎没有在我的结束类中初始化。

更具体地说:

我在模块中有许多控制器,包括一些默认功能。

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
例如,

。这不是实际的代码,但这是目前最多的互动。

我想用一些其他功能(列表的可排序接口)来扩展它,就像这样[注意我希望能够逐个类地交换排序顺序列表功能] :

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 个答案:

答案 0 :(得分:2)

类实例变量是可行的方法,当然。您实际上很少需要使用类变量。

在具体回答您的问题时,请记住,模块中定义的任何代码只能在模块加载时执行而不是在包含模块时执行。这种区别可能并不明显,特别是当人们认为“包含”等同于“要求”时。

你需要的是:

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,并且它位于调用类的上下文中。

我对此进行了一些修改,以包含一些特定的样式更改:

  • 在方法内而不是在声明中声明默认值。避免产生不可读的长行,或者不得不使用单行复杂的数据结构。
  • 使用将在这种情况下完成工作的类实例变量。
  • 使用Ruby样式的var = mutator方法而不是set_var()

答案 1 :(得分:0)

您必须在模块中添加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

答案 2 :(得分:0)

在注意到@tadman已经响应之前,我最终使用了一个类级实例变量并且include()没有自动包含的子模块。

最终看起来像这样:

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下

<强> 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