class PaperTrail::ModelConfig

Configures an ActiveRecord model, mostly at application boot time, but also sometimes mid-request, with methods like enable/disable.

Constants

DPR_CLASS_NAME_OPTION
DPR_PASSING_ASSOC_NAME_DIRECTLY_TO_VERSIONS_OPTION
E_CANNOT_RECORD_AFTER_DESTROY
E_HPT_ABSTRACT_CLASS

Public Class Methods

new(model_class) click to toggle source
# File lib/paper_trail/model_config.rb, line 32
def initialize(model_class)
  @model_class = model_class
end

Public Instance Methods

on_create() click to toggle source

Adds a callback that records a version after a “create” event.

@api public

# File lib/paper_trail/model_config.rb, line 39
def on_create
  @model_class.after_create { |r|
    r.paper_trail.record_create if r.paper_trail.save_version?
  }
  append_option_uniquely(:on, :create)
end
on_destroy(recording_order = "before") click to toggle source

Adds a callback that records a version before or after a “destroy” event.

@api public

# File lib/paper_trail/model_config.rb, line 49
def on_destroy(recording_order = "before")
  assert_valid_recording_order_for_on_destroy(recording_order)
  @model_class.send(
    "#{recording_order}_destroy",
    lambda do |r|
      return unless r.paper_trail.save_version?
      r.paper_trail.record_destroy(recording_order)
    end
  )
  append_option_uniquely(:on, :destroy)
end
on_touch() click to toggle source

Adds a callback that records a version after a “touch” event.

Rails < 6.0 (no longer supported by PT) had a bug where dirty-tracking did not occur during a ‘touch`. (github.com/rails/rails/issues/33429) See also: github.com/paper-trail-gem/paper_trail/issues/1121 github.com/paper-trail-gem/paper_trail/issues/1161 github.com/paper-trail-gem/paper_trail/pull/1285

@api public

# File lib/paper_trail/model_config.rb, line 93
def on_touch
  @model_class.after_touch { |r|
    if r.paper_trail.save_version?
      r.paper_trail.record_update(
        force: false,
        in_after_callback: true,
        is_touch: true
      )
    end
  }
end
on_update() click to toggle source

Adds a callback that records a version after an “update” event.

@api public

# File lib/paper_trail/model_config.rb, line 64
def on_update
  @model_class.before_save { |r|
    r.paper_trail.reset_timestamp_attrs_for_update_if_needed
  }
  @model_class.after_update { |r|
    if r.paper_trail.save_version?
      r.paper_trail.record_update(
        force: false,
        in_after_callback: true,
        is_touch: false
      )
    end
  }
  @model_class.after_update { |r|
    r.paper_trail.clear_version_instance
  }
  append_option_uniquely(:on, :update)
end
setup(options = {}) click to toggle source

Set up ‘@model_class` for PaperTrail. Installs callbacks, associations, “class attributes”, instance methods, and more. @api private

# File lib/paper_trail/model_config.rb, line 108
def setup(options = {})
  options[:on] ||= %i[create update destroy touch]
  options[:on] = Array(options[:on]) # Support single symbol
  @model_class.send :include, ::PaperTrail::Model::InstanceMethods
  setup_options(options)
  setup_associations(options)
  @model_class.after_rollback { paper_trail.clear_rolled_back_versions }
  setup_callbacks_from_options options[:on]
end
version_class() click to toggle source

@api private

# File lib/paper_trail/model_config.rb, line 119
def version_class
  @version_class ||= @model_class.version_class_name.constantize
end

Private Instance Methods

append_option_uniquely(option, value) click to toggle source

@api private

# File lib/paper_trail/model_config.rb, line 126
def append_option_uniquely(option, value)
  collection = @model_class.paper_trail_options.fetch(option)
  return if collection.include?(value)
  collection << value
end
assert_concrete_activerecord_class(class_name) click to toggle source

Raises an error if the provided class is an ‘abstract_class`. @api private

# File lib/paper_trail/model_config.rb, line 134
def assert_concrete_activerecord_class(class_name)
  if class_name.constantize.abstract_class?
    raise Error, format(E_HPT_ABSTRACT_CLASS, @model_class, class_name)
  end
end
assert_valid_recording_order_for_on_destroy(recording_order) click to toggle source

@api private

# File lib/paper_trail/model_config.rb, line 141
def assert_valid_recording_order_for_on_destroy(recording_order)
  unless %w[after before].include?(recording_order.to_s)
    raise ArgumentError, 'recording order can only be "after" or "before"'
  end

  if recording_order.to_s == "after" && cannot_record_after_destroy?
    raise Error, E_CANNOT_RECORD_AFTER_DESTROY
  end
end
cannot_record_after_destroy?() click to toggle source
# File lib/paper_trail/model_config.rb, line 151
def cannot_record_after_destroy?
  ::ActiveRecord::Base.belongs_to_required_by_default
end
check_version_class_name(options) click to toggle source
# File lib/paper_trail/model_config.rb, line 155
def check_version_class_name(options)
  # @api private - `version_class_name`
  @model_class.class_attribute :version_class_name
  if options[:class_name]
    PaperTrail.deprecator.warn(
      format(
        DPR_CLASS_NAME_OPTION,
        class_name: options[:class_name].inspect
      ),
      caller(1)
    )
    options[:versions][:class_name] = options[:class_name]
  end
  @model_class.version_class_name = options[:versions][:class_name] || "PaperTrail::Version"
  assert_concrete_activerecord_class(@model_class.version_class_name)
end
check_versions_association_name(options) click to toggle source
# File lib/paper_trail/model_config.rb, line 172
def check_versions_association_name(options)
  # @api private - versions_association_name
  @model_class.class_attribute :versions_association_name
  @model_class.versions_association_name = options[:versions][:name] || :versions
end
define_has_many_versions(options) click to toggle source
# File lib/paper_trail/model_config.rb, line 178
def define_has_many_versions(options)
  options = ensure_versions_option_is_hash(options)
  check_version_class_name(options)
  check_versions_association_name(options)
  scope = get_versions_scope(options)
  @model_class.has_many(
    @model_class.versions_association_name,
    scope,
    class_name: @model_class.version_class_name,
    as: :item,
    **options[:versions].except(:name, :scope)
  )
end
ensure_versions_option_is_hash(options) click to toggle source
# File lib/paper_trail/model_config.rb, line 192
def ensure_versions_option_is_hash(options)
  unless options[:versions].is_a?(Hash)
    if options[:versions]
      PaperTrail.deprecator.warn(
        format(
          DPR_PASSING_ASSOC_NAME_DIRECTLY_TO_VERSIONS_OPTION,
          versions_name: options[:versions].inspect
        ),
        caller(1)
      )
    end
    options[:versions] = {
      name: options[:versions]
    }
  end
  options
end
event_attribute_option(option_name) click to toggle source

Process an ‘ignore`, `skip`, or `only` option.

# File lib/paper_trail/model_config.rb, line 211
def event_attribute_option(option_name)
  [@model_class.paper_trail_options[option_name]].
    flatten.
    compact.
    map { |attr| attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s }
end
get_versions_scope(options) click to toggle source
# File lib/paper_trail/model_config.rb, line 218
def get_versions_scope(options)
  options[:versions][:scope] || -> { order(model.timestamp_sort_order) }
end
setup_associations(options) click to toggle source
# File lib/paper_trail/model_config.rb, line 222
def setup_associations(options)
  # @api private - version_association_name
  @model_class.class_attribute :version_association_name
  @model_class.version_association_name = options[:version] || :version

  # The version this instance was reified from.
  # @api public
  @model_class.send :attr_accessor, @model_class.version_association_name

  # @api public - paper_trail_event
  @model_class.send :attr_accessor, :paper_trail_event

  define_has_many_versions(options)
end
setup_callbacks_from_options(options_on = []) click to toggle source
# File lib/paper_trail/model_config.rb, line 237
def setup_callbacks_from_options(options_on = [])
  options_on.each do |event|
    public_send("on_#{event}")
  end
end
setup_options(options) click to toggle source
# File lib/paper_trail/model_config.rb, line 243
def setup_options(options)
  # @api public - paper_trail_options - Let's encourage plugins to use eg.
  # `paper_trail_options[:versions][:class_name]` rather than
  # `version_class_name` because the former is documented and the latter is
  # not.
  @model_class.class_attribute :paper_trail_options
  @model_class.paper_trail_options = options.dup

  %i[ignore skip only].each do |k|
    @model_class.paper_trail_options[k] = event_attribute_option(k)
  end
  @model_class.paper_trail_options[:meta] ||= {}
end