123456789_123456789_123456789_123456789_123456789_

Module: Mongoid::Persistable::Updatable

Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Included In:
Defined in: lib/mongoid/persistable/updatable.rb

Overview

Defines behavior for persistence operations that update existing documents.

Instance Method Summary

Instance Method Details

#enforce_immutability_of_id_field! (private)

Checks to see if the _id field has been modified. If it has, and if the document has already been persisted, this is an error. Otherwise, returns without side-effects.

Note that if ‘Mongoid::Config.immutable_ids` is false, this will do nothing.

Raises:

[ GitHub ]

  
# File 'lib/mongoid/persistable/updatable.rb', line 195

def enforce_immutability_of_id_field!
  # special case here: we *do* allow the _id to be mutated if it was
  # previously nil. This addresses an odd case exposed in
  # has_one/proxy_spec.rb where `person.create_address` would
  # (somehow?) create the address with a nil _id first, before then
  # saving it *again* with the correct _id.

  if _id_changed? && !_id_was.nil? && persisted?
    if Mongoid::Config.immutable_ids
      raise Errors::ImmutableAttribute.new(:_id, _id)
    else
      Mongoid::Warnings.warn_mutable_ids
    end
  end
end

#init_atomic_updatesArray<Hash> (private)

This method is for internal use only.

Initialize the atomic updates.

Examples:

Initialize the atomic updates.

document.init_atomic_updates

Returns:

  • (Array<Hash>)

    The updates and conflicts.

[ GitHub ]

  
# File 'lib/mongoid/persistable/updatable.rb', line 78

def init_atomic_updates
  updates = atomic_updates
  conflicts = updates.delete(:conflicts) || {}
  [ updates, conflicts ]
end

#prepare_update(options = {}) ⇒ true | false (private)

This method is for internal use only.

Prepare the update for execution. Validates and runs callbacks, etc.

Examples:

Prepare for update.

document.prepare_update do
  collection.update(atomic_selector)
end

Parameters:

  • options (Hash) (defaults to: {})

    The options.

Options Hash (options):

  • :touch (true | false)

    Whether or not the updated_at attribute will be updated with the current time.

Returns:

  • (true | false)

    The result of the update.

Raises:

[ GitHub ]

  
# File 'lib/mongoid/persistable/updatable.rb', line 99

def prepare_update(options = {})
  raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly
  enforce_immutability_of_id_field!
  ensure_client_compatibility!
  return false if performing_validations?(options) &&
    invalid?(options[:context] || :update)
  process_flagged_destroys
  update_children = cascadable_children(:update)
  process_touch_option(options, update_children) do
    run_all_callbacks_for_update(update_children) do
      result = yield(self)
      self.previously_new_record = false
      post_process_persist(result, options)
      true
    end
  end
end

#process_touch_option(options, children) (private)

If there is a touch option and it is false, this method will call the timeless method so that the updated_at attribute is not updated. It will call the timeless method on all of the cascadable children as well. Note that timeless is cleared in the before_update callback.

Parameters:

  • options (Hash)

    The options.

  • children (Array<Document>)

    The children that the :update callbacks will be executed on.

Options Hash (options):

  • :touch (true | false)

    Whether or not the updated_at attribute will be updated with the current time.

[ GitHub ]

  
# File 'lib/mongoid/persistable/updatable.rb', line 176

def process_touch_option(options, children)
  if options.fetch(:touch, true)
    yield
  else
    timeless
    children.each(&:timeless)
    suppress_touch_callbacks { yield }
  end
end

#run_all_callbacks_for_update(update_children) (private)

Consolidates all the callback invocations into a single place, to avoid cluttering the logic in #prepare_update.

Parameters:

  • update_children (Array<Document>)

    The children that the :update callbacks will be executed on.

[ GitHub ]

  
# File 'lib/mongoid/persistable/updatable.rb', line 216

def run_all_callbacks_for_update(update_children)
  run_callbacks(:commit, with_children: true, skip_if: -> { in_transaction? }) do
    run_callbacks(:save, with_children: false) do
      run_callbacks(:update, with_children: false) do
        run_callbacks(:persist_parent, with_children: false) do
          _mongoid_run_child_callbacks(:save) do
            _mongoid_run_child_callbacks(:update, children: update_children) do
              yield
            end # _mongoid_run_child_callbacks :update
          end # _mongoid_run_child_callbacks :save
        end # :persist_parent
      end # :update
    end # :save
  end # :commit
end

#update(attributes = {}) ⇒ true | false Also known as: #update_attributes

Update the document attributes in the database.

Examples:

Update the document’s attributes

document.update(:title => "Sir")

Parameters:

  • attributes (Hash) (defaults to: {})

    The attributes to update.

Returns:

  • (true | false)

    True if validation passed, false if not.

[ GitHub ]

  
# File 'lib/mongoid/persistable/updatable.rb', line 40

def update(attributes = {})
  assign_attributes(attributes)
  save
end

#update!(attributes = {}) ⇒ true | false Also known as: #update_attributes!

Update the document attributes in the database and raise an error if validation failed.

Examples:

Update the document’s attributes.

document.update!(:title => "Sir")

Parameters:

  • attributes (Hash) (defaults to: {})

    The attributes to update.

Returns:

  • (true | false)

    True if validation passed.

Raises:

  • (Errors::Validations)

    If validation failed.

  • (Errors::Callbacks)

    If a callback returns false.

[ GitHub ]

  
# File 'lib/mongoid/persistable/updatable.rb', line 58

def update!(attributes = {})
  result = update_attributes(attributes)
  unless result
    fail_due_to_validation! unless errors.empty?
    fail_due_to_callback!(:update_attributes!)
  end
  result
end

#update_attribute(name, value) ⇒ true | false

Update a single attribute and persist the entire document. This skips validation but fires the callbacks.

Examples:

Update the attribute.

person.update_attribute(:title, "Sir")

Parameters:

  • name (Symbol | String)

    The name of the attribute.

  • value (Object)

    The new value of the attribute.a

Returns:

  • (true | false)

    True if save was successful, false if not.

Raises:

[ GitHub ]

  
# File 'lib/mongoid/persistable/updatable.rb', line 24

def update_attribute(name, value)
  as_writable_attribute!(name, value) do |access|
    normalized = name.to_s
    process_attribute(normalized, value)
    save(validate: false)
  end
end

#update_attributes(attributes = {})

Alias for #update.

[ GitHub ]

  
# File 'lib/mongoid/persistable/updatable.rb', line 44

alias :update_attributes :update

#update_attributes!(attributes = {})

Alias for #update!.

[ GitHub ]

  
# File 'lib/mongoid/persistable/updatable.rb', line 66

alias :update_attributes! :update!

#update_document(options = {}) ⇒ true | false (private)

Update the document in the database.

Examples:

Update an existing document.

document.update

Parameters:

  • options (Hash) (defaults to: {})

    Options to pass to update.

Options Hash (options):

  • :validate (true | false)

    Whether or not to validate.

Returns:

  • (true | false)

    True if succeeded, false if not.

[ GitHub ]

  
# File 'lib/mongoid/persistable/updatable.rb', line 127

def update_document(options = {})
  prepare_update(options) do
    updates, conflicts = init_atomic_updates
    unless updates.empty?
      coll = collection(_root)
      selector = atomic_selector

      # TODO: DRIVERS-716: If a new "Bulk Write" API is introduced, it may
      # become possible to handle the writes for conflicts in the following call.
      coll.find(selector).update_one(positionally(selector, updates), session: _session)

      # The following code applies updates which would cause
      # path conflicts in MongoDB, for example when changing attributes
      # of foo.0.bars while adding another foo. Each conflicting update
      # is applied using its own write.
      conflicts.each_pair do |modifier, changes|

        # Group the changes according to their root key which is
        # the top-level association name.
        # This handles at least the cases described in MONGOID-4982.
        conflicting_change_groups = changes.group_by do |key, _|
          key.split(".", 2).first
        end.values

        # Apply changes in batches. Pop one change from each
        # field-conflict group round-robin until all changes
        # have been applied.
        while batched_changes = conflicting_change_groups.map(&:pop).compact.to_h.presence
          coll.find(selector).update_one(
            positionally(selector, modifier => batched_changes),
            session: _session,
          )
        end
      end
    end
  end
end