# frozen-string-literal: true module Sequel module Plugins # The association_multi_add_remove plugin allows adding, removing and setting # multiple associated objects in a single method call. # By default Sequel::Model defines singular add_* and remove_* # methods that operate on a single associated object, this adds plural forms # that operate on multiple associated objects. Example: # # artist.albums # => [album1] # artist.add_albums([album2, album3]) # artist.albums # => [album1, album2, album3] # artist.remove_albums([album3, album1]) # artist.albums # => [album2] # artist.albums = [album2, album3] # artist.albums # => [album2, album3] # # It can handle all situations that the normal singular methods handle, but there is # no attempt to optimize behavior, so using these methods will not improve performance. # # The add/remove/set methods defined by this plugin use a transaction, # so if one add/remove/set fails and raises an exception, all adds/removes/set # will be rolled back. If you are using database sharding and want to save # to a specific shard, call Model#set_server to set the server for this instance, # as the transaction will be opened on that server. # # You can customize the method names used for adding/removing multiple associated # objects using the :multi_add_method and :multi_remove_method association options. # # Usage: # # # Allow adding/removing/setting multiple associated objects in a single call # # for all model subclass instances (called before loading subclasses): # Sequel::Model.plugin :association_multi_add_remove # # # Allow adding/removing/setting multiple associated objects in a single call # # for Album instances (called before defining associations in the class): # Album.plugin :association_multi_add_remove module AssociationMultiAddRemove module ClassMethods private # Define the methods use to add/remove/set multiple associated objects # in a single method call. def def_association_instance_methods(opts) super if opts[:adder] add_method = opts[:add_method] multi_add_method = opts[:multi_add_method] || :"add_#{opts[:name]}" multi_add_method = nil if add_method == multi_add_method if multi_add_method association_module_def(multi_add_method, opts) do |objs, *args| db.transaction(:server=>@server){objs.map{|obj| send(add_method, obj, *args)}.compact} end end end if opts[:remover] remove_method = opts[:remove_method] multi_remove_method = opts[:multi_remove_method] || :"remove_#{opts[:name]}" multi_remove_method = nil if remove_method == multi_remove_method if multi_remove_method association_module_def(multi_remove_method, opts) do |objs, *args| db.transaction(:server=>@server){objs.map{|obj| send(remove_method, obj, *args)}.compact} end end end if multi_add_method && multi_remove_method association_module_def(:"#{opts[:name]}=", opts) do |objs, *args| db.transaction(:server=>@server) do existing_objs = send(opts.association_method) send(multi_remove_method, (existing_objs - objs), *args) send(multi_add_method, (objs - existing_objs), *args) nil end end end end end end end end