从外部宝石扩充模型

Pau*_*der 6 ruby metaprogramming refinerycms

我在我们的网站上使用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
Run Code Online (Sandbox Code Playgroud)

配置/初始化/ pants.rb

require 'page_extensions'

Page.send :include, Pants::Extensions::Page
Run Code Online (Sandbox Code Playgroud)

应用程序/视图/布局/ application.html.erb

...
Tags: <%= @page.tag_list %>
Run Code Online (Sandbox Code Playgroud)

我第一次从服务器请求页面时它正确输出页面上的所有标签.但是,如果我点击刷新I而不是NoMethodError指示tag_list未定义.

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

par*_*ndt 14

您需要将module_eval代码放入config.to_prepare do块中.最简单的方法是config/application.rb创建引擎或创建引擎.代码是相同的,除了它每次运行站点时执行而不仅仅是第一次(特别是适用于开发模式)和仅在初始化过程之前执行的代码(也就是需要文件)进入config.before_initialize do块.

config.to_prepare重要的原因是因为在开发模式下,代码会在每个请求上重新加载,但初始化程序通常不会.这意味着Page,您正在运行module_eval,只会运行一次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
Run Code Online (Sandbox Code Playgroud)

引擎方法

如果您不想修改,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
Run Code Online (Sandbox Code Playgroud)

如果你使用引擎方法,你还需要创建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
Run Code Online (Sandbox Code Playgroud)

然后在你Gemfile添加这一行:

gem 'add_page_extensions', :path => 'vendor/engines'
Run Code Online (Sandbox Code Playgroud)

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

希望这可以帮助