# encoding: utf-8 module Mongoid module Atomic # This class contains the logic for supporting atomic operations against the # database. class Modifiers < Hash # Add the atomic $addToSet modifiers to the hash. # # @example Add the $addToSet modifiers. # modifiers.add_to_set({ "preference_ids" => [ "one" ] }) # # @param [ Hash ] modifications The add to set modifiers. # # @since 2.4.0 def add_to_set(modifications) modifications.each_pair do |field, value| if add_to_sets.has_key?(field) value.each do |val| add_to_sets[field]["$each"].push(val) end else add_to_sets[field] = { "$each" => value } end end end # Adds pull all modifiers to the modifiers hash. # # @example Add pull all operations. # modifiers.pull_all({ "addresses" => { "street" => "Bond" }}) # # @param [ Hash ] modifications The pull all modifiers. # # @since 3.0.0 def pull_all(modifications) modifications.each_pair do |field, value| add_operation(pull_alls, field, value) pull_fields[field.split(".", 2)[0]] = field end end # Adds pull all modifiers to the modifiers hash. # # @example Add pull all operations. # modifiers.pull({ "addresses" => { "_id" => { "$in" => [ 1, 2, 3 ]}}}) # # @param [ Hash ] modifications The pull all modifiers. # # @since 3.0.0 def pull(modifications) modifications.each_pair do |field, value| pulls[field] = value pull_fields[field.split(".", 2)[0]] = field end end # Adds push modifiers to the modifiers hash. # # @example Add push operations. # modifiers.push({ "addresses" => { "street" => "Bond" }}) # # @param [ Hash ] modifications The push modifiers. # # @since 2.1.0 def push(modifications) modifications.each_pair do |field, value| push_fields[field] = field mods = push_conflict?(field) ? conflicting_pushes : pushes add_operation(mods, field, {'$each' => Array.wrap(value)}) end end # Adds set operations to the modifiers hash. # # @example Add set operations. # modifiers.set({ "title" => "sir" }) # # @param [ Hash ] modifications The set modifiers. # # @since 2.1.0 def set(modifications) modifications.each_pair do |field, value| next if field == "_id" mods = set_conflict?(field) ? conflicting_sets : sets add_operation(mods, field, value) set_fields[field.split(".", 2)[0]] = field end end # Adds unset operations to the modifiers hash. # # @example Add unset operations. # modifiers.unset([ "addresses" ]) # # @param [ Array ] modifications The unset relation names. # # @since 2.2.0 def unset(modifications) modifications.each do |field| unsets.update(field => true) end end private # Add the operation to the modifications, either appending or creating a # new one. # # @example Add the operation. # modifications.add_operation(mods, field, value) # # @param [ Hash ] mods The modifications. # @param [ String ] field The field. # @param [ Hash ] value The atomic op. # # @since 2.2.0 def add_operation(mods, field, value) if mods.has_key?(field) if mods[field].is_a?(Array) value.each do |val| mods[field].push(val) end elsif mods[field]['$each'] mods[field]['$each'].concat(value['$each']) end else mods[field] = value end end # Get the $addToSet operations or intialize a new one. # # @example Get the $addToSet operations. # modifiers.add_to_sets # # @return [ Hash ] The $addToSet operations. # # @since 2.4.0 def add_to_sets self["$addToSet"] ||= {} end # Is the operation going to be a conflict for a $set? # # @example Is this a conflict for a set? # modifiers.set_conflict?(field) # # @param [ String ] field The field. # # @return [ true, false ] If this field is a conflict. # # @since 2.2.0 def set_conflict?(field) name = field.split(".", 2)[0] pull_fields.has_key?(name) || push_fields.has_key?(name) end # Is the operation going to be a conflict for a $push? # # @example Is this a conflict for a push? # modifiers.push_conflict?(field) # # @param [ String ] field The field. # # @return [ true, false ] If this field is a conflict. # # @since 2.2.0 def push_conflict?(field) name = field.split(".", 2)[0] set_fields.has_key?(name) || pull_fields.has_key?(name) || (push_fields.keys.count { |item| item =~ /#{name}/ } > 1) end # Get the conflicting pull modifications. # # @example Get the conflicting pulls. # modifiers.conflicting_pulls # # @return [ Hash ] The conflicting pull operations. # # @since 2.2.0 def conflicting_pulls conflicts["$pullAll"] ||= {} end # Get the conflicting push modifications. # # @example Get the conflicting pushs. # modifiers.conflicting_pushs # # @return [ Hash ] The conflicting push operations. # # @since 2.2.0 def conflicting_pushes conflicts["$push"] ||= {} end # Get the conflicting set modifications. # # @example Get the conflicting sets. # modifiers.conflicting_sets # # @return [ Hash ] The conflicting set operations. # # @since 2.2.0 def conflicting_sets conflicts["$set"] ||= {} end # Get the push operations that would have conflicted with the sets. # # @example Get the conflicts. # modifiers.conflicts # # @return [ Hash ] The conflicting modifications. # # @since 2.1.0 def conflicts self[:conflicts] ||= {} end # Get the names of the fields that need to be pulled. # # @example Get the pull fields. # modifiers.pull_fields # # @return [ Array ] The pull fields. # # @since 2.2.0 def pull_fields @pull_fields ||= {} end # Get the names of the fields that need to be pushed. # # @example Get the push fields. # modifiers.push_fields # # @return [ Array ] The push fields. # # @since 2.2.0 def push_fields @push_fields ||= {} end # Get the names of the fields that need to be set. # # @example Get the set fields. # modifiers.set_fields # # @return [ Array ] The set fields. # # @since 2.2.0 def set_fields @set_fields ||= {} end # Get the $pullAll operations or intialize a new one. # # @example Get the $pullAll operations. # modifiers.pull_alls # # @return [ Hash ] The $pullAll operations. # # @since 3.0.0 def pull_alls self["$pullAll"] ||= {} end # Get the $pull operations or intialize a new one. # # @example Get the $pull operations. # modifiers.pulls # # @return [ Hash ] The $pull operations. # # @since 3.0.0 def pulls self["$pull"] ||= {} end # Get the $push/$each operations or initialize a new one. # # @example Get the $push/$each operations. # modifiers.pushes # # @return [ Hash ] The $push/$each operations. # # @since 2.1.0 def pushes self["$push"] ||= {} end # Get the $set operations or intialize a new one. # # @example Get the $set operations. # modifiers.sets # # @return [ Hash ] The $set operations. # # @since 2.1.0 def sets self["$set"] ||= {} end # Get the $unset operations or initialize a new one. # # @example Get the $unset operations. # modifiers.unsets # # @return [ Hash ] The $unset operations. # # @since 2.2.0 def unsets self["$unset"] ||= {} end end end end