【问题标题】:Ruby on Rails FilterChain internals - how to make it reloadable?Ruby on Rails FilterChain 内部 - 如何使其可重新加载?
【发布时间】:2009-08-14 11:56:30
【问题描述】:

我希望在 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 源的链接:

The filter_chain method (line 573)

The FilterChain class (line 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

【问题讨论】:

  • 有关您尝试实现的目标的更多详细信息会很有用。
  • 添加了一点更多细节。有什么具体的我可以添加帮助的吗?
  • 我对原因更感兴趣。
  • 当然,感谢您对这个问题感兴趣。我会把它添加到问题的末尾。
  • 一种想法是修改Controller中的before_filter、after_filter等方法,使它们在子类上调用相同的方法。这实际上会发生: before_filter :render_page_if_exists subclasses.each do |subclass| subclass.class_eval do before_filter :render_page_if_exists end end subclasses method: class Class def subclasses result = [] ObjectSpace.each_object(Class) { |klass|结果

标签: ruby-on-rails filter spree radiant


【解决方案1】:

在 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

取自这里的cmets:

Spree issue 653 Specifying filters in extensions can create filter chains missing filters

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-06
    相关资源
    最近更新 更多