0

我希望在 Ruby on Rails 2.3.2 中有一种更动态的方式来处理过滤器链。

目前,过滤器链是在加载类时构建的。我非常需要在加载类后的某个时间点重建过滤器链。

这与使用扩展的 Rails 项目(我猜是 Spree,Radiant 也可能受到影响)有关,该扩展可以在为 MyController 构建过滤器链后执行以下操作:

MyController.class_eval do
  before_filter :my_filter_method
end

ApplicationController.class_eval do
  before_filter :app_filter_method
end

这里的问题是app_filter_method过滤器不会被添加到 MyController 过滤器链中。这是因为 MyController 的过滤器链是从 ApplicationController 的过滤器链的早期副本构建的。ApplicationController 的过滤器链的这个副本还没有应用app_filter_method过滤器。

到目前为止,我能想到 2 个地方可能会发生 FilterChain 的重建:

1) 每次在 MyController.filter_chain 上调用它

2) 可按需重新加载。所以 MyController.reload_filter_chain 将重建过滤器链,使用来自链中 MyController 子类的过滤器。

也许指定一个 FilterChain 的子类,例如为每个请求构建过滤器链的 ReloadableFilterChain 可能会这样做 - 想法?

以下是 GitHub 上用于 Ruby on Rails 2.3.2 的 filters.rb 源的链接:

filter_chain 方法(第 573 行)

FilterChain 类(第 10 行)

我希望你们中的一些人可能对如何做到这一点有一些见解或建议。

非常感谢任何帮助。

艾略特

根据要求提供更多详细信息:

Spree 是一个使用扩展来修改行为的 Rails 应用程序。

有时扩展用于向控制器添加过滤器(例如在问题中的示例代码中)。一个示例使用是静态内容扩展。

静态内容扩展允许您显示存储在数据库中的 HTML 页面,以针对您指定的任何请求路径显示。例如,您可以显示这些 HTML 页面之一,而不是 Spree 为 /products 请求路径显示的默认内容。

静态内容扩展过滤所有请求并检查路径。如果路径与 db 中的某个页面匹配,则过滤器会呈现该页面。

静态内容扩展声明其过滤器,如下所示:

Spree::BaseController.class_eval do
  before_filter :render_page_if_exists

  def render_page_if_exists
    ...
  end
end
4

1 回答 1

0

回到 Rails 1.0,过滤器链并没有像这样被缓存,所以不会有问题。

过滤器链被缓存的原因是为了性能。几乎没有理由不缓存它。通过四处寻找希望它具有动态性的其他人,我只找到了一个帖子。因此,除非您遇到这样的情况,否则在应用程序启动后似乎很少需要过滤器链是动态的或可重新加载的。

考虑到这一点,我认为像 FilterChain.reload 这样的方法将是保持性能提升的好方法。

在 Spree 中,可以在加载扩展后调用 FilterChain.reload。我更喜欢我在之前的评论中建议的解决方案(修补 before_filter),因为我认为它对 Rails 核心有用。

FilterChain.class_eval do

  # Reloads all filter chains on all controllers, working from the ApplicationController down
  # through the class hierarchy.  e.g. Spree::BaseController would get its filter chain reloaded
  # before its subclasses like ProductsController. We do this as ProductsController's filter chain
  # relies on a copy of the Spree::BaseController filter chain.
  def self.reload
    # ApplicationController.filter_chain does not need reloading, it will be the only correct
    # filter chain for sure as it is not inherited from a superclass.
    reload_child_filter_chains(ApplicationController)
  end

  def self.reload_child_filter_chains(controller_class)
    controller_class.immediate_subclasses.each do |controller_child|
      # Reload filter chain on each controller who's immediate parent is the controller_class
      controller_child.filter_chain.merge_filter_chain(controller_class.filter_chain)
      # Reload the children of controller_child
      reload_child_filter_chains(controller_child)
    end
  end

  # New instance method on FilterChain to merges the given parent chain into itself.
  def merge_filter_chain(parent_chain)
    # Compare self and parent_chain and insert any parent_chain filters that
    # are missing from self.  You may need special handling for
    # Filters that were marked for skipping or with :only, etc. conditions.
    ...
  end
end

取自这里的评论:

Spree issue 653 在扩展中指定过滤器可以创建过滤器链缺少过滤器

于 2009-08-15T09:22:14.120 回答