Repository: rails/rails
File: activerecord/lib/active_record/persistence.rb (Ruby)

Code before:
    # Updates its receiver just like #update but calls #save! instead
    # of +save+, so an exception is raised if the record is invalid and saving will fail.
    def update!(attributes)
      # The following transaction covers any possible database side-effects of the
      # attributes assignment. For example, setting the IDs of a child collection.
      with_transaction_returning_status do
        assign_attributes(attributes)
        save!
      end
    end

    # Equivalent to <code>update_columns(name => value)</code>.
    def update_column(name, value, touch: nil)
      update_columns(name => value, :touch => touch)
    end

    # Updates the attributes directly in the database issuing an UPDATE SQL
    # statement and sets them in the receiver.
    #
    # This is the fastest way to update attributes because it goes straight to
    # the database, but take into account that in consequence the regular update
    # procedures are totally bypassed. In particular:
    #
    # * \Validations are skipped.
    # * \Callbacks are skipped.
    # * +updated_at+/+updated_on+ are updated if the +touch+ option is set to +true+. Otherwise, they're not updated.
    # * However, attributes are serialized with the same rules as ActiveRecord::Relation#update_all
    #
    # This method raises an ActiveRecord::ActiveRecordError when called on new
    # objects, or when at least one of the attributes is marked as readonly.
    #
    # ==== Parameters
    # * <tt>:touch</tt> option - Touch the timestamp columns when updating.
    # * If attributes names are passed, they are updated along with update_at/on attributes.
    #
    # ==== Examples
    #   # Update a single attribute
    #   user.update_columns(last_request_at: Time.current)
    #   # Update with touch option
    #   user.update_columns(last_request_at: Time.current, touch: true)

    def update_columns(attributes)
      raise ActiveRecordError, "cannot update a new record" if new_record?
      raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
      _raise_readonly_record_error if readonly?

      attributes = attributes.transform_keys do |key|
        name = key.to_s
        name = self.class.attribute_aliases[name] || name
        verify_readonly_attribute(name) || name
      end

Review comment (suggestion):
```suggestion
    # * +updated_at+/+updated_on+ are updated if the +touch+ option is set to +true+.
```

Code after:

        attribute_names.each do |attr_name|
          _write_attribute(attr_name, time)
        end

        _update_row(attribute_names, "touch")
      end

      def _update_row(attribute_names, attempted_action = "update")
        self.class._update_record(
          attributes_with_values(attribute_names),
          _query_constraints_hash
        )
      end

      def create_or_update(**, &block)
        _raise_readonly_record_error if readonly?
        return false if destroyed?
        result = new_record? ? _create_record(&block) : _update_record(&block)
        result != false
      end

      # Updates the associated record with values matching those of the instance attributes.
      # Returns the number of affected rows.
      def _update_record(attribute_names = self.attribute_names)
        attribute_names = attributes_for_update(attribute_names)

        if attribute_names.empty?
          affected_rows = 0
          @_trigger_update_callback = true
        else
          affected_rows = _update_row(attribute_names)
          @_trigger_update_callback = affected_rows == 1
        end

        @previously_new_record = false

        yield(self) if block_given?

        affected_rows
      end

      # Creates a record with values matching those of the instance attributes
      # and returns its id.
      def _create_record(attribute_names = self.attribute_names)
        attribute_names = attributes_for_create(attribute_names)

        self.class.with_connection do |connection|
          returning_columns = self.class._returning_columns_for_insert(connection)

          returning_values = self.class._insert_record(