如何在Rails 3应用程序中的所有其他控制器before_filter之后在gem中追加before_filter?

时间:2011-09-16 15:54:30

标签: ruby-on-rails ruby ruby-on-rails-3 gem

有一个宝石,追加 before_filter到Rails应用:

class Railtie < Rails::Railtie
  initializer "..." do
    ActiveSupport.on_load(:action_controller) do
      ActionController::Base.send(:include, Filter)

...

module Filter
  extend ActiveSupport::Concern

  included do
    append_before_filter :set_locale
  end

  def set_locale
     ....

这是应用程序中的一些控制器:

class DesktopsController < ApplicationController
  before_filter :set_language_in_session

现在问题是来自gem的before_filter被放入DesktopsController的before_filter之前的过滤器链中:

DesktopsController._process_action_callbacks.select { |c| c.kind == :before }.collect { |filter| filter.filter }
=> [
    [0] :set_locale,
    [1] :set_language_in_session
]

如何将gem中的before_filter set_locale )置于所有其他过滤器之后?秘密可能在于这一行:

ActiveSupport.on_load(:action_controller) do

但我尝试过不同的图书馆而没有任何运气......

顺便说一下。这是宝石的full code。 Ruby 1.9.2,Rails 3.0.5。

7 个答案:

答案 0 :(得分:3)

改编宝石永远不会奏效。 gem被初始化为rails过程中的第一件事,因此在任何实际的控制器启动之前。所以这意味着,无论你做什么,很可能来自宝石的过滤器仍然是第一个宝石。

所以我看到的唯一解决方案是:

  • 在您自己的控制器中使用prepend_before_filter执行set_locale之前需要执行的操作。
  • 如果您需要明确控制,请使用skip_before_filter和明确的before_filter

在rails 2应用程序中,我们设计了一个hack来确保某些过滤器作为最后一个过滤器运行,通过覆盖perform_action_without_filters(请参阅Is there a way to force a before_filter to always execute last?)但我们在升级到rails 3时将其删除了

答案 1 :(得分:2)

我的回答是基于nickgrim的answer,我也发现它非常聪明。传递给included的块在包含模块时会触发类(class_eval)。您可以通过在set_locale_filter块(以及其他方式)中定义类方法来使included成为类方法。

module Filter
  extend ActiveSupport::Concern

  included do
    append_before_filter :set_locale_filter

    def self.set_locale_filter
      append_before_filter :set_locale
    end
  end

  def set_locale
    # 
  end
end

有人,如果我弄错了,请纠正我。现在已经很晚了:))

答案 2 :(得分:0)

试试这个:

class DesktopsController < ApplicationController
  skip_before_filter :set_locale
  before_filter      :set_language_in_session
  before_filter      :set_locale
end 

<强>参考

skip_before_filter doc

答案 3 :(得分:0)

我还没有尝试过,但你可以查看prepend_before_filter,它声称将提供的过滤器提升到列表的顶部(最终在:set_locale之前)。

class DesktopsController < ApplicationController
  prepend_before_filter      :set_language_in_session
end 

文档:http://apidock.com/rails/ActionController/Filters/ClassMethods/prepend_before_filter

要尝试的另一件事是使用around_filter:set_language_in_session(即使你在动作后没有做任何事情)。过滤器在before_filters之前发生,因此它可能是强制它首先发生的好方法。

答案 4 :(得分:0)

gem本身位于Github上,因此您可以对其进行分叉并更改代码,然后让代码拉出您的版本的gem。这是一个选择吗?如果是这样,您可以在gem中注释before_filer,并在ApplicationController中自行调用:

module Filter
  extend ActiveSupport::Concern

  included do
    # append_before_filter :set_locale
  end

  def set_locale
    ....

现在只需在所有其他过滤器之后自行调用:

class ApplicationController < ActionController::Base
  before_filter :do_this, :do_that, :set_locale
  ...
end

你想什么?

答案 5 :(得分:-1)

根本没试过,但是:你能拥有宝石before_filter运行append_before_filter吗?类似的东西:

module Filter
  ...

  included do
    append_before_filter :append_set_locale_filter
  end

  def append_set_locale_filter
    append_before_filter :set_locale
  end

  def set_locale
    ...
  end

  ...
end

答案 6 :(得分:-1)

就个人而言,我可能会编写一段中间件来执行您的操作,因为这样您就不必担心过滤器发生的顺序。

http://guides.rubyonrails.org/rails_on_rack.html