Дополнение модели из внешнего драгоценного камня - PullRequest
6 голосов
/ 26 октября 2010

Я использую refinerycms на нашем сайте, чтобы позволить менее техническому персоналу обновлять контент.Внутри драгоценного камня у них есть класс Page, который отображает каждую страницу верхнего уровня на сайте.Я хотел бы использовать гем act_as_taggable в этом классе Page.Теперь я могу добавить объявление act_as_taggle непосредственно в файл page.rb, но тогда мне потребуется отдельное git-репо, чтобы отслеживать различия между моей версией и официальным выпуском.

Основываясь на некоторых других вопросах здесь, в 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 / брюк.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, а не конкретного экземпляра класса.Так как же мне добавить Act_as_taggable в класс Page при каждом запросе?

1 Ответ

14 голосов
/ 26 октября 2010

Вам необходимо поместить свой код 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

Подход двигателя

ЕслиВы не хотите изменять 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.

Надеюсь, это поможет

...