Rails:在多个控制器操作中设置公共实例变量

时间:2009-05-27 06:18:38

标签: ruby-on-rails model-view-controller filter

如何让几个不同的控制器操作设置一个公共实例变量用于模板,但设置操作后

换句话说,我希望这可以在我的application_controller中使用。

class ApplicationController < ActionController::Base
  after_filter :set_something_common

  def set_something_common
    # All controllers' actions have queried the DB and set @foo for me...
    @bar = some_calculation_on(@foo)
    # ... and all templates expect @bar to bet set.
  end
end

不起作用,因为after_filter在渲染后运行。精细。但是正确的模式是什么?

同样,set_something_common在操作之后运行是很重要的,因为这些操作会执行特定于案例的操作;但他们都设置了@foo

我的想法似乎都不理想:

  • 在需要它的每个操作的底部调用set_something_common()
  • 将所有控制器的特定于案例的代码重构为case_specific_code()并强制它们按顺序运行:

    before_filter :case_specific_code, :set_something_common
    
  • 子类application_controller并重新定义index方法。

有什么想法?感谢。

编辑:马修的回应促使我澄清:

几个控制者的索引()都进行分页,每个参数都使用参数@offset@limit(通过全局before_filter)来查看数据切片。大。现在我想要一个通用方法来为“下一个切片”链接计算RESTful URL。我被鼓励看到url_for()生成一个返回相同资源的URL,所以我尝试了:

def set_something_common # really called set_next_url, truth be told
  @next_url = url_for(:offset => @offset + @limit, :limit => @limit)
end

我会尝试猴子修补Fixnum,所以我可以从模板中执行类似@offset.next_url_for(self, @limit)的操作,但我不确定它是否可行。想想看,如果我要修改模板,那么我也可以设置一个应用程序帮助器。我仍然不确定最佳解决方案是什么。

更新:接受的答案是“使用助手。”

感谢大家的更新。我从中吸取了教训,就像全局变量一样,帮助者是有原因的,当它们明显有益且简洁时,不要被避开。

4 个答案:

答案 0 :(得分:23)

首先,您不希望尝试在控制器操作和模板渲染之间插入代码。为什么?因为您希望控制器操作可以自由选择要提供的响应类型。它可能只返回XML,JSON,头文件,重定向,什么都没有等等。这就是在呈现响应后执行过滤器之后的原因。

其次,你不想要修补补丁Fixnum。我的意思是,也许做,但我没有。至少不是这样,除非我从中获得一些完全邪恶的语义好处,比如能够说3.blind_mice。猴子为这样的随机用例修补它似乎是一个维护头痛的道路。

您提到将所有控制器的特定于案例的代码重构为前置过滤器并按顺序运行它们。这引起了我的注意...... @foo在每种情况下都是一样的?如果是这种情况,那么在过滤器之前就可以正常工作:

before_filter :do_common_stuff
def do_common_stuff
  @foo = common_foo
  @bar = do_something_with @foo
end

这是一种完全合法的方法。但是如果@foo从控制器变为控制器......那么,你还有一些选择。

您可以将之前的过滤器分成两半,并为每个控制器自定义一个。

# application_controller:
before_filter :get_foo, :do_something_common
def do_something_common
  @bar = do_something_with @foo
end

# baz_controller:
def get_foo
  @foo = pull_from_mouth
end

#baf_controller:
def get_foo
  @foo = pull_from_ear
end

但是你知道,如果这是一个简单的案例,不需要数据库访问或网络访问或类似的东西......你的情况不是......不要自杀。不要出汗。把它扔进帮手。这就是他们所要求的,帮助。你基本上只是将一些视图数据重新排列成一个稍微容易使用的形式。帮助是我的投票。您只需将其命名为next_url即可。 :)

答案 1 :(得分:4)

我会在@foo上找到一个返回栏的方法,这样您就可以在视图中使用@foo.bar

<% @bar = @foo.bar %>#如果你真的不想改变自己的观点,但是你没有听到我的意见:)

答案 2 :(得分:4)

在模板中使用<%= do_some_calculations(@foo) %>。 这是直截了当的。

答案 3 :(得分:0)

在处理 Rails 6 应用程序时,我遇到了这个挑战。

我想在另一个控制器(app/views/shared/_header.html.erb)中定义的部分(app/controllers/categories_controller.rb)中使用实例变量。

我想使用的实例变量是@categories,它定义为:

# app/controllers/categories_controller.rb

class CategoriesController < ApplicationController

  def index
    @categories = Category.all
  end
  .
  .
  .
end

这是我的做法

首先,我在app/controllers/application_controller.rb文件中为实例变量定义了一个helper_method:

class ApplicationController < ActionController::Base
  helper_method :categories

  def categories
    @categories = Category.all
  end
end

这使得@categories实例变量可以作为categories全局地用于每个控制器操作和视图:

接下来,我以这种方式在app/views/shared/_header.html.erb中渲染了app/views/layouts/application.html.erb部分:

<%= render partial: '/shared/header' %>

这还将使@categories实例变量全局可用,因为categories可用于将使用局部视图的每个控制器视图,而无需在各个控制器中定义@categories实例变量的视图。

因此,我以部分方式使用了@categories实例变量,该变量在全球范围内可作为categories使用:

# app/views/shared/_header.html.erb

<% categories.each do |category| %>
  <%= link_to category do %>
    <%= category.name %>
  <% end %>
<% end %>

注意:您可以使用本地变量将变量传递到局部变量中:

<%= render partial: '/shared/header', locals: { categories: @categories } %>

但是,这将需要执行控制器操作,该操作将为将使用局部视图的每个控制器视图设置一个@categories实例变量。

您可以在Rails官方文档:https://api.rubyonrails.org/classes/AbstractController/Helpers/ClassMethods.html

中阅读有关帮助程序和帮助程序方法的更多信息。

仅此而已。

我希望这会有所帮助