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

发布于 2024-08-23 12:06:44 字数 1577 浏览 5 评论 0原文

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

更具体地说:

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

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 是一个错误的调用,但我想不出我还可以使用什么。 (类实例变量?)

I want to have some kind of single list that is initialized in a seperate module, then can be included in a controller and modified at the controller-class level, and accessed at the controller-instance level. I thought class variables would work here, but something strange is happening, they don't seem to be being initialized within my ending class.

More specifically:

I have many controllers all including some default functionality, in a module.

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

, for instance. This isn't the actual code, but that's the most interaction it has at the moment.

I'd like to extend this with some other functionality (a sortable interface for lists,) like so [note I'd like to be able to swap out the sort-order-list functionality on a class by class basis]:

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

The idea is to make sure that I'm able to swap out the set of all possible sorts on a class by class basis, like so:

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

And also to make nice links in the views, like below, except that I'm getting an error.

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

I figure the class_var is a bad call, but I can't think of what else I might use. (a class instance var?)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

夏日浅笑〃 2024-08-30 12:06:44

当然,类实例变量是正确的选择。您实际上很少需要使用类变量。

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

您需要的是:

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()

Class instance vars are the way to go, for sure. Rarely do you actually need to use a class variable.

In specific answer to your problem, keep in mind that any code defined in your module is executed only once when the module is loaded not when the module is included. This distinction may not be obvious, especially when people consider "include" to be equivalent to "require".

What you need is this:

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

The way you catch a class including your module is to define Module.included accordingly. In this case, set_sorts is called each time this module is included, and it is in the context of the calling class.

I've modified this a bit to include a few specific style changes:

  • Declaring your defaults inside the method instead of in the declaration. Avoids producing unreadably long lines, or having to one-line otherwise complicated data structures.
  • Using class instance variables which will do the job in this case.
  • Using a Ruby-style var= mutator method instead of set_var()
别理我 2024-08-30 12:06:44

您必须向模块添加一个 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

You have to add a included method to your module for this to work.

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
未蓝澄海的烟 2024-08-30 12:06:44

在注意到 @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_functionity.rb

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

lib/default_controller_functionity/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

I ended up using a class level instance variable and included() with no autoincluded submodules, before noticing that @tadman had responded.

It ended up looking like this:

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

in the controllers, and under /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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文