Запуск метода только один раз для добавления / удаления элементов ассоциации

У меня есть 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 ошибка где-то в середине моего сервиса.

Другие вопросы по тегам