Снова откройте модель ActiveRecord, предоставленную гемом.

Я пытаюсь расширить модель 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 модель?

 agmcleod25 мая 2012 г., 23:22
Это работает, если вы просто используетеclass Vote вместо тогоVote.class_eval? Одна вещь, которую вы также можете сделать, это отредактировать код в самом геме и просто использовать вашу модифицированную версию.
 Dougui01 июн. 2012 г., 15:57
Почему, по-твоему, класс «Голос» перезагружается? В остальном класс находится в каталоге lib, поэтому он такой же, как и вы ...
 Harish Shetty07 июн. 2012 г., 18:23
Какая у тебя версия Rails?
 Tom Lehman01 июн. 2012 г., 00:55
class Vote ведет себя так же, какVote.class_eval - ни работа Я думаю, я мог бы изменить драгоценный камень, но я действительно не хочу смеяться. Какой беспорядок!
 Tom Lehman07 июн. 2012 г., 22:00
@ KandadaBoggu 2.3.4

Ответы на вопрос(6)

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

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".

 Dougui01 июн. 2012 г., 16:31
Извините, я не видел комментарий agmcleod. Наверное, это то же самое ... Это должно работать, но это не так.
 miked04 июн. 2012 г., 21:33
хе, по крайней мере, ты не получил отрицательный голос, как я ... и наши предложения были, ну, в общем, такими же! смешн
 Dougui04 июн. 2012 г., 21:47
Это может быть потому, что вы говорите, чтобы поместить его в инициализатор. Или, может быть, я говорю это после вас.

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 обратные вызовы вместо? Еще не пробовал.

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

Я верю, что ты можешь использовать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)
 Adrien Coquio22 июн. 2012 г., 00:43
обновил мой ответ, я думаю, что ActiveSupport :: Концерн решит вашу проблему

это очень старый драгоценный камень (последний коммит 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 строк рубин

что предложил 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 (это мои предпочтения).

 Tom Lehman01 июн. 2012 г., 00:56
Это не работает - если я уйду изload вto_prepare блок, я получаю ту же ошибку, которую получил, когда файл был вlib. Если я удаляюload, тогда я получаю(can't dup NilClass) ошибк
 miked03 июн. 2012 г., 05:01
Серьезно, ты на самом деле голосовал против меня за это предложение? !! Вау. Извините за попытку помочь, но у меня это прекрасно работает с КАЖДЫМ драгоценным камнем, который я когда-либо исправлял. Вы могли бы также попробовать другое предложение разветвлять драгоценный камень - но не, если вы только собираетесь наказать меня за это ... гы.
 Tom Lehman26 мая 2012 г., 17:27
Зачемconfig/initializers вместо тогоlib? Я предполагаю, что мне нужно сохранитьload заявление вto_prepare блок?
 miked27 мая 2012 г., 00:04
nope не должен нуждаться в этом операторе загрузки, поскольку элементы в файлах инициализатора загружаются один раз при запуске для всех сред.

что вы исправляете класс. Когда 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 есть некоторые новые функции и улучшения.

Ваш ответ на вопрос