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

Code before:
      status = nil
      connection = self.class.connection
      ensure_finalize = !connection.transaction_open?

      connection.transaction do
        add_to_transaction(ensure_finalize || has_transactional_callbacks?)
        remember_transaction_record_state

        status = yield
        raise ActiveRecord::Rollback unless status
      end
      status
    end

    def trigger_transactional_callbacks? # :nodoc:
      (@_new_record_before_last_commit || _trigger_update_callback) && persisted? ||
        _trigger_destroy_callback && destroyed?
    end

    private
      attr_reader :_committed_already_called, :_trigger_update_callback, :_trigger_destroy_callback

      # Save the new record state and id of a record so it can be restored later if a transaction fails.
      def remember_transaction_record_state
        @_start_transaction_state ||= {
          id: id,
          new_record: @new_record,
          previously_new_record: @previously_new_record,
          destroyed: @destroyed,
          attributes: @attributes,
          frozen?: frozen?,
          level: 0
        }
        @_start_transaction_state[:level] += 1

        if _committed_already_called
          @_new_record_before_last_commit = false
        else
          @_new_record_before_last_commit = @_start_transaction_state[:new_record]
        end
      end

      # Clear the new record state and id of a record.
      def clear_transaction_record_state
        return unless @_start_transaction_state
        @_start_transaction_state[:level] -= 1
        @_start_transaction_state = nil if @_start_transaction_state[:level] < 1
      end

      # Restore the new record state and id of a record that was previously saved by a call to save_record_state.
      def restore_transaction_record_state(force_restore_state = false)

Review comment (suggestion):
Have there been any discussions about priming object shapes via `attr_*`?  Or would that not match the overall implementation (e.g. it would break `defined? @foo`)?

(Though cases like this might not be straightforward, since the `attr_*` is on a module that gets included.)


Code after:
      status = nil
      connection = self.class.connection
      ensure_finalize = !connection.transaction_open?

      connection.transaction do
        add_to_transaction(ensure_finalize || has_transactional_callbacks?)
        remember_transaction_record_state

        status = yield
        raise ActiveRecord::Rollback unless status
      end
      status
    end

    def trigger_transactional_callbacks? # :nodoc:
      (@_new_record_before_last_commit || _trigger_update_callback) && persisted? ||
        _trigger_destroy_callback && destroyed?
    end

    private
      attr_reader :_committed_already_called, :_trigger_update_callback, :_trigger_destroy_callback

      def init_internals
        super
        @_committed_already_called = nil
      end

      # Save the new record state and id of a record so it can be restored later if a transaction fails.
      def remember_transaction_record_state
        @_start_transaction_state ||= {
          id: id,
          new_record: @new_record,
          previously_new_record: @previously_new_record,
          destroyed: @destroyed,
          attributes: @attributes,
          frozen?: frozen?,
          level: 0
        }
        @_start_transaction_state[:level] += 1

        if _committed_already_called
          @_new_record_before_last_commit = false
        else
          @_new_record_before_last_commit = @_start_transaction_state[:new_record]
        end
      end

      # Clear the new record state and id of a record.
      def clear_transaction_record_state
        return unless @_start_transaction_state
        @_start_transaction_state[:level] -= 1