Вопрос по ruby-on-rails – Снова откройте модель ActiveRecord, предоставленную гемом.

5

Я пытаюсь расширить модель ActiveRecord Vote) что драгоценный камень https: //github.com/peteonrails/vote_f) предоставляет мое приложение. (Т.е. нетvote.rb вapp/models)

Мой первый подход состоял в том, чтобы создать файл с именемlib/extend_vote.rb, содержащий код:

Vote.class_eval do
  after_create :create_activity_stream_event
  has_one :activity_stream_event

  def create_activity_stream_event
    # something..
  end
end

Это работает, когда создается первый голос, но когда я пытаюсь создать каждый последующий голос, я получаю ошибкуTypeError (can't dup NilClass).

Я думаю, что эта ошибка вызвана тем, чтоVote class перезагружается автоматически после каждого запроса, но код вlib/extend_vote.rb загружается только один раз, когда сервер запускается, и это вызываетhas_one :activity_stream_event Ассоциация вести себя странно. (Кроме того, проблема исчезнет, если я установлюconfig.cache_classes = true вdevelopment.rb)

Чтобы решить эту проблему, я попытался перезагрузить расширения голосов при каждом запросе, добавивto_prepare заблокировать мойdevelopment.rb:

config.to_prepare do
  load 'extend_vote.rb'
end

Это решает(can't dup NilClass) проблема, но теперь, когда я создаю новый голос,create_activity_stream_event обратный вызов вызывается в дополнительное время. То есть первый голос вызывает его один раз, второй - дважды, и т. Д., И т. Д. Так что, похоже,to_prepare block слишком активно перезагружает расширение и добавляет дубликаты обратных вызовов.

Какой лучший способ добавить методы и обратные вызовы к этомуVote модель?

@ KandadaBoggu 2.3.4 Tom Lehman
Это работает, если вы просто используетеclass Vote вместо тогоVote.class_eval? Одна вещь, которую вы также можете сделать, это отредактировать код в самом геме и просто использовать вашу модифицированную версию. agmcleod
Какая у тебя версия Rails? Harish Shetty
Почему, по-твоему, класс «Голос» перезагружается? В остальном класс находится в каталоге lib, поэтому он такой же, как и вы ... Dougui
class Vote ведет себя так же, какVote.class_eval - ни работа Я думаю, я мог бы изменить драгоценный камень, но я действительно не хочу смеяться. Какой беспорядок! Tom Lehman

Ваш Ответ

6   ответов
6

должно быть правильным решением, чтобы модуль не включался несколько раз в один и тот же класс]

Я верю, что ты можешь использоватьActiveSupport :: Концерн для предотвращения включения модуля несколько раз, в результате чего вызывается обратный вызов несколько раз. Смотрите пример ниже:

module VotePatch
  extend ActiveSupport::Concern

  included do
    after_create :create_activity_stream_event
    has_one :activity_stream_event
  end

  module InstanceMethods
    def create_activity_stream_event
      #your code here
    end  
  end

end

Vote.send(:include, VotePatch)
обновил мой ответ, я думаю, что ActiveSupport :: Концерн решит вашу проблему Adrien Coquio
4

это очень старый драгоценный камень (последний коммит 3 года), и, судя по всему, он не будет работать с rails 3.x как есть. В Rails 3.x движки упрощают подобные вещи.

Как я понимаю, проблема в первом случае не в том, что модель голосования перезагружается (не должно), а в том, чтоactivity_stream_event модель перезагружена. Поскольку модель голосования не перезагружена, ассоциация остается висеть на версииactivity_stream_event класс до перезагрузки. Поскольку рельсы выводят классы до того, как они будут перезагружены, это вызывает проблемы.

С этим в моем, попробуйте это взломать:

#in config/initializers/abstract_vote.rb
AbstractVote = Vote
AbstractVote.abstract_class = true
Object.send :remove_const, :Vote

#in app/models/vote.rb

class Vote < AbstractVote
  after_create :create_activity_stream_event
  has_one :activity_stream_event

  def create_activity_stream_event
  end
end

Для этого вам нужно иметь собственный класс для голосования, который наследуется от класса в жемчужине.

Но опять же, я призываю вас найти что-то более современное или свернуть свое собственное (драгоценный камень всего ~ 250 строк рубин

1

что предложил agmcleod в комментариях, но вместо того, чтобы поместить его в lib, вставьте Конфигурации / Инициализаторы / vote.rb:

 class Vote
   after_create :create_activity_stream_event
   has_one :activity_stream_event

   def create_activity_stream_event
   # something..
   end
 end

Конечно, вы можете разветвлять гем, вносить изменения и ссылаться на свою разветвленную версию в своем Gemfile (это мои предпочтения).

Зачемconfig/initializers вместо тогоlib? Я предполагаю, что мне нужно сохранитьload заявление вto_prepare блок? Tom Lehman
nope не должен нуждаться в этом операторе загрузки, поскольку элементы в файлах инициализатора загружаются один раз при запуске для всех сред. miked
Это не работает - если я уйду изload вto_prepare блок, я получаю ту же ошибку, которую получил, когда файл был вlib. Если я удаляюload, тогда я получаю(can't dup NilClass) ошибк Tom Lehman
Серьезно, ты на самом деле голосовал против меня за это предложение? !! Вау. Извините за попытку помочь, но у меня это прекрасно работает с КАЖДЫМ драгоценным камнем, который я когда-либо исправлял. Вы могли бы также попробовать другое предложение разветвлять драгоценный камень - но не, если вы только собираетесь наказать меня за это ... гы. miked
1

ActiveSupport::Concerns, которые Рельсовый путь для расширения моделей. Его код будет работать, и вы должны его использовать.

Однако это не будет работать все время в процессе разработки, потому что когда Rails перезагружает ваши классы при изменении файла, он не будет повторно оценивать#send линия. Единственное решение, которое я смог найти, это присоединение кActionDispatch обратный вызов в производстве, чтобы гарантировать, что файл будет требоваться после каждой загрузки страницы:

if Rails.env.development?
  ActionDispatch::Callbacks.to_prepare do
    require_dependency "../../lib/vote_fu_extensions"
  end
end

В производстве или если вы установитеcache_classes true в вашей конфигурации, вам не нужно этого делать.

ИсточниRoR руководства по настройке Это говорит о том, что это устарело и вы должны использоватьActionDispatch::Reloader обратные вызовы вместо? Еще не пробовал.
0

что вы исправляете класс. Когда rails пытается перезагрузить константы, он не учитывает ваш файл.

Попробуйте использовать методику модуля, как указано ниже.

Добавьте файл с именемlib/vote_fu_extension.rb

module VoteFuExtension
  def self.included(base)
    base.has_one :activity_stream_event
    base.after_create :create_activity_stream_event
  end
  def create_activity_stream_event
    # something..
  end  
end
Vote.send(:include, VoteFuExtension)

Добавьте инициализатор с именемconfig/initializers/vote_fu.rb

require "vote_fu_extension"

Заметк

Если вы хотите добавить методы класса кVote модель относится к этомуотве.

Бесстыжий плагин: Мой Вилка изvote_fu @ gem есть некоторые новые функции и улучшения.

0

не могли бы вы попробовать что-то вроде этого:

class Vote
    after_create :create_activity_stream_event
    has_one :activity_stream_event

    def create_activity_stream_event
        # something..
    end
end

Я думаю, что это добавит вашу функцию и вызовет функции "after_create" и "has_hone".

хе, по крайней мере, ты не получил отрицательный голос, как я ... и наши предложения были, ну, в общем, такими же! смешн miked
Извините, я не видел комментарий agmcleod. Наверное, это то же самое ... Это должно работать, но это не так. Dougui
Это может быть потому, что вы говорите, чтобы поместить его в инициализатор. Или, может быть, я говорю это после вас. Dougui

Похожие вопросы