【问题标题】:Augmenting a model from an external gem从外部 gem 增强模型
【发布时间】:2010-10-25 20:44:43
【问题描述】:

我在我们的网站中使用refinerycms 来让技术人员更新内容。在 gem 中,他们有一个 Page 类,用于映射站点上的每个顶级页面。我想在这个 Page 类上使用acts_as_taggable gem。现在我可以将acts_as_taggle 声明直接添加到page.rb 文件中,但是我必须维护一个单独的git repo 来跟踪我的版本和正式版本之间的差异。

基于 SO 上的其他一些问题,我创建了一个初始化程序和扩展,如下所示:

lib/page_extensions.rb:

module Pants
  module Extensions

    module Page
      module ClassMethods
        def add_taggable
          acts_as_taggable
        end
      end

      def self.included(base)
        base.extend(ClassMethods).add_taggable
      end

    end

  end
end

config/initializers/pants.rb

require 'page_extensions'

Page.send :include, Pants::Extensions::Page

app/views/layouts/application.html.erb

...
Tags: <%= @page.tag_list %>

我第一次从服务器请求页面时,它会正确输出页面上的所有标签。但是,如果我点击刷新,我会得到一个 NoMethodError,表明 tag_list 未定义。

我是 Rails 新手,所以也许我的假设是错误的,但我希望调用 Page.send 会对 Page 类而不是类的特定实例进行永久性更改。那么如何在每个请求中将acts_as_taggable 添加到 Page 类中呢?

【问题讨论】:

  • 请将其重新标记为“refinerycms”,谢谢。

标签: ruby metaprogramming refinerycms


【解决方案1】:

您需要将 module_eval 代码放入 config.to_prepare do 块中。最容易做到这一点的地方是config/application.rb 或创建一个引擎。代码是相同的,除了它在您每次运行站点时都会执行,而不仅仅是第一次运行(尤其适用于开发模式),而且代码仅在初始化过程(也就是需要文件)之前执行到 config.before_initialize do 块中。

config.to_prepare 之所以重要是因为在开发模式下,每次请求都会重新加载代码,但初始化程序通常不会。这意味着您正在运行 module_eval 的 Page 只会让 module_eval 运行一次,但会在每次请求时重新加载自己。 config.to_prepare 是一个每次运行的 Rails 钩子,为这种情况提供了极大的便利。

config/application.rb 方法

class Application < Rails::Application
  # ... other stuff ...

  config.before_initialize do
    require 'page_extensions'
  end

  config.to_prepare do
    Page.send :include, Pants::Extensions::Page
  end
end

引擎方法

如果您不想修改 config/application.rb,那么您可以在 Refinery CMS 中创建如下所示的 vendor/engines/add_page_extensions/lib/add_page_extensions.rb

require 'refinery'

module Refinery
  module AddPageExtensions
    class Engine < Rails::Engine

      config.before_initialize do
        require 'page_extensions'
      end

      config.to_prepare do
        Page.send :include, Pants::Extensions::Page
      end

    end
  end
end

如果您使用引擎方法,您还需要创建 vendor/engines/add_page_extensions/add_page_extensions.gemspec,其中应包含一个简单的 gemspec:

Gem::Specification.new do |s|
  s.name = 'add_page_extensions'
  s.require_paths = %w(lib)
  s.version = 1.0
  s.files = Dir["lib/**/*"]
end

然后在你的Gemfile 中添加这一行:

gem 'add_page_extensions', :path => 'vendor/engines'

如果您采用引擎方法,您可能希望将所有逻辑放在引擎的 lib 目录中,包括 Pants::Extensions::Page 代码。

希望对你有帮助

【讨论】:

  • 这帮助我尝试从我的扩展中覆盖页面扩展中的 PageController 方法。唯一不同的是我必须将实际的覆盖代码放在 to_prepare 块中。如果我使用包含,由于类重新加载,它在开发模式下仍然不稳定。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-10
  • 1970-01-01
  • 1970-01-01
  • 2014-12-18
  • 1970-01-01
  • 1970-01-01
  • 2013-02-26
相关资源
最近更新 更多