Запуск метода только один раз для добавления / удаления элементов ассоциации
У меня есть Composition
модель, которая имеет has_and_belongs_to_many :authors
,
Мне нужно запустить метод после того, как композиция сменила авторов, хотя, поскольку она предполагает создание файла PDF (с именами авторов), я хочу вызывать этот метод только один раз, независимо от количества добавленных авторов / удален.
Конечно, я могу добавить / удалить существующих авторов из композиции, поэтому before_save
/ after_save
здесь работать не будет (каким-то образом распознаются новые авторы, добавленные в композицию, но не существующие).
Поэтому я попытался с помощью after_add
/ after_remove
, но указанные здесь обратные вызовы будут вызываться для каждого элемента автора, добавленного / удаленного из композиции.
Есть ли способ, чтобы метод вызывался только один раз для каждого "пакетного действия" добавления / удаления элементов из такого рода отношений?
1 ответ
Вот как может выглядеть сервис:
class UpdateCompositionAuthorsService
attr_accessor *%w(
args
).freeze
class << self
def call(args={})
new(args).call
end
end # Class Methods
#======================================================================================
# Instance Methods
#======================================================================================
def initialize(args={})
@args = args
assign_args
end
def call
do_stuff_to_update_authors
generate_the_pdf
end
private
def do_stuff_to_update_authors
# do your 'batch' stuff here
end
def generate_the_pdf
# do your one-time logic here
end
def assign_args
args.each do |k,v|
class_eval do
attr_accessor k
end
send("#{k}=",v)
end
end
end
Вы бы назвали это что-то вроде:
UpdateCompositionAuthorsService.call(composition: @composition, authors: @authors)
Мне надоело вспоминать, какие аргументы отправлять на мои классы обслуживания, поэтому я создал модуль под названием ActsAs::CallingServices
, Когда включено в class
который хочет вызвать сервисы, модуль предоставляет метод call_service
это позволяет мне сделать что-то вроде:
class FooClass
include ActsAs::CallingServices
def bar
call_service UpdateCompositionAuthorsService
end
end
Затем в класс обслуживания я включаю некоторые дополнительные данные уровня класса, например:
class UpdateCompositionAuthorsService
SERVICE_DETAILS = [
:composition,
:authors
].freeze
...
def call
do_stuff_to_update_authors
generate_the_pdf
end
...
end
Вызывающий класс (FooClass
в данном случае) использует UpdateCompositionAuthorsService::SERVICE_DETAILS
для построения соответствующих аргументов хеш (детали опущены).
У меня также есть метод под названием good_to_go?
(детали опущены), которая включена в мои классы обслуживания, поэтому мой метод вызова обычно выглядит так:
class UpdateCompositionAuthorsService
...
def call
raise unless good_to_go?
do_stuff_to_update_authors
generate_the_pdf
end
...
end
Итак, если набор аргументов плохой, я знаю сразу, а не сталкиваться с nil
ошибка где-то в середине моего сервиса.