Merge pull request #582 from airblade/formatting_comments
Format comments
This commit is contained in:
commit
b5c2bce93d
|
@ -43,12 +43,14 @@ module PaperTrail
|
||||||
!!paper_trail_store[:request_enabled_for_controller]
|
!!paper_trail_store[:request_enabled_for_controller]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sets whether PaperTrail is enabled or disabled for this model in the current request.
|
# Sets whether PaperTrail is enabled or disabled for this model in the
|
||||||
|
# current request.
|
||||||
def self.enabled_for_model(model, value)
|
def self.enabled_for_model(model, value)
|
||||||
paper_trail_store[:"enabled_for_#{model}"] = value
|
paper_trail_store[:"enabled_for_#{model}"] = value
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns `true` if PaperTrail is enabled for this model in the current request, `false` otherwise.
|
# Returns `true` if PaperTrail is enabled for this model in the current
|
||||||
|
# request, `false` otherwise.
|
||||||
def self.enabled_for_model?(model)
|
def self.enabled_for_model?(model)
|
||||||
!!paper_trail_store.fetch(:"enabled_for_#{model}", true)
|
!!paper_trail_store.fetch(:"enabled_for_#{model}", true)
|
||||||
end
|
end
|
||||||
|
@ -63,10 +65,9 @@ module PaperTrail
|
||||||
PaperTrail.config.timestamp_field
|
PaperTrail.config.timestamp_field
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sets who is responsible for any changes that occur.
|
# Sets who is responsible for any changes that occur. You would normally use
|
||||||
# You would normally use this in a migration or on the console,
|
# this in a migration or on the console, when working with models directly.
|
||||||
# when working with models directly. In a controller it is set
|
# In a controller it is set automatically to the `current_user`.
|
||||||
# automatically to the `current_user`.
|
|
||||||
def self.whodunnit=(value)
|
def self.whodunnit=(value)
|
||||||
paper_trail_store[:whodunnit] = value
|
paper_trail_store[:whodunnit] = value
|
||||||
end
|
end
|
||||||
|
@ -76,8 +77,8 @@ module PaperTrail
|
||||||
paper_trail_store[:whodunnit]
|
paper_trail_store[:whodunnit]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sets any information from the controller that you want PaperTrail
|
# Sets any information from the controller that you want PaperTrail to
|
||||||
# to store. By default this is set automatically by a before filter.
|
# store. By default this is set automatically by a before filter.
|
||||||
def self.controller_info=(value)
|
def self.controller_info=(value)
|
||||||
paper_trail_store[:controller_info] = value
|
paper_trail_store[:controller_info] = value
|
||||||
end
|
end
|
||||||
|
@ -117,8 +118,8 @@ module PaperTrail
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Thread-safe hash to hold PaperTrail's data.
|
# Thread-safe hash to hold PaperTrail's data. Initializing with needed
|
||||||
# Initializing with needed default values.
|
# default values.
|
||||||
def self.paper_trail_store
|
def self.paper_trail_store
|
||||||
RequestStore.store[:paper_trail] ||= { :request_enabled_for_controller => true }
|
RequestStore.store[:paper_trail] ||= { :request_enabled_for_controller => true }
|
||||||
end
|
end
|
||||||
|
@ -135,12 +136,15 @@ module PaperTrail
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Ensure `ProtectedAttributes` gem gets required if it is available before the `Version` class gets loaded in
|
# Ensure `ProtectedAttributes` gem gets required if it is available before the
|
||||||
|
# `Version` class gets loaded in.
|
||||||
unless PaperTrail.active_record_protected_attributes?
|
unless PaperTrail.active_record_protected_attributes?
|
||||||
PaperTrail.send(:remove_instance_variable, :@active_record_protected_attributes)
|
PaperTrail.send(:remove_instance_variable, :@active_record_protected_attributes)
|
||||||
begin
|
begin
|
||||||
require 'protected_attributes'
|
require 'protected_attributes'
|
||||||
rescue LoadError; end # will rescue if `ProtectedAttributes` gem is not available
|
rescue LoadError
|
||||||
|
# In case `ProtectedAttributes` gem is not available.
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
ActiveSupport.on_load(:active_record) do
|
ActiveSupport.on_load(:active_record) do
|
||||||
|
|
|
@ -1,19 +1,24 @@
|
||||||
module PaperTrail
|
module PaperTrail
|
||||||
module Cleaner
|
module Cleaner
|
||||||
# Destroys all but the most recent version(s) for items on a given date (or on all dates). Useful for deleting drafts.
|
# Destroys all but the most recent version(s) for items on a given date
|
||||||
|
# (or on all dates). Useful for deleting drafts.
|
||||||
#
|
#
|
||||||
# Options:
|
# Options:
|
||||||
# :keeping An `integer` indicating the number of versions to be kept for each item per date.
|
#
|
||||||
# Defaults to `1`.
|
# - :keeping - An `integer` indicating the number of versions to be kept for
|
||||||
# :date Should either be a `Date` object specifying which date to destroy versions for or `:all`,
|
# each item per date. Defaults to `1`.
|
||||||
# which will specify that all dates should be cleaned. Defaults to `:all`.
|
# - :date - Should either be a `Date` object specifying which date to
|
||||||
# :item_id The `id` for the item to be cleaned on, or `nil`, which causes all items to be cleaned.
|
# destroy versions for or `:all`, which will specify that all dates
|
||||||
# Defaults to `nil`.
|
# should be cleaned. Defaults to `:all`.
|
||||||
|
# - :item_id - The `id` for the item to be cleaned on, or `nil`, which
|
||||||
|
# causes all items to be cleaned. Defaults to `nil`.
|
||||||
|
#
|
||||||
def clean_versions!(options = {})
|
def clean_versions!(options = {})
|
||||||
options = {:keeping => 1, :date => :all}.merge(options)
|
options = {:keeping => 1, :date => :all}.merge(options)
|
||||||
gather_versions(options[:item_id], options[:date]).each do |item_id, versions|
|
gather_versions(options[:item_id], options[:date]).each do |item_id, versions|
|
||||||
versions.group_by { |v| v.send(PaperTrail.timestamp_field).to_date }.each do |date, _versions|
|
versions.group_by { |v| v.send(PaperTrail.timestamp_field).to_date }.each do |date, _versions|
|
||||||
# remove the number of versions we wish to keep from the collection of versions prior to destruction
|
# Remove the number of versions we wish to keep from the collection
|
||||||
|
# of versions prior to destruction.
|
||||||
_versions.pop(options[:keeping])
|
_versions.pop(options[:keeping])
|
||||||
_versions.map(&:destroy)
|
_versions.map(&:destroy)
|
||||||
end
|
end
|
||||||
|
@ -22,13 +27,18 @@ module PaperTrail
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Returns a hash of versions grouped by the `item_id` attribute formatted like this: {:item_id => PaperTrail::Version}.
|
# Returns a hash of versions grouped by the `item_id` attribute formatted
|
||||||
# If `item_id` or `date` is set, versions will be narrowed to those pointing at items with those ids that were created on specified date.
|
# like this: {:item_id => PaperTrail::Version}. If `item_id` or `date` is
|
||||||
|
# set, versions will be narrowed to those pointing at items with those ids
|
||||||
|
# that were created on specified date.
|
||||||
def gather_versions(item_id = nil, date = :all)
|
def gather_versions(item_id = nil, date = :all)
|
||||||
raise ArgumentError.new("`date` argument must receive a Timestamp or `:all`") unless date == :all || date.respond_to?(:to_date)
|
raise ArgumentError.new("`date` argument must receive a Timestamp or `:all`") unless date == :all || date.respond_to?(:to_date)
|
||||||
versions = item_id ? PaperTrail::Version.where(:item_id => item_id) : PaperTrail::Version
|
versions = item_id ? PaperTrail::Version.where(:item_id => item_id) : PaperTrail::Version
|
||||||
versions = versions.between(date.to_date, date.to_date + 1.day) unless date == :all
|
versions = versions.between(date.to_date, date.to_date + 1.day) unless date == :all
|
||||||
versions = PaperTrail::Version.all if versions == PaperTrail::Version # if versions has not been converted to an ActiveRecord::Relation yet, do so now
|
|
||||||
|
# If `versions` has not been converted to an ActiveRecord::Relation yet,
|
||||||
|
# do so now.
|
||||||
|
versions = PaperTrail::Version.all if versions == PaperTrail::Version
|
||||||
versions.group_by(&:item_id)
|
versions.group_by(&:item_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# This file only needs to be loaded if the gem is being used outside of Rails, since otherwise
|
# This file only needs to be loaded if the gem is being used outside of Rails,
|
||||||
# the model(s) will get loaded in via the `Rails::Engine`
|
# since otherwise the model(s) will get loaded in via the `Rails::Engine`.
|
||||||
require "paper_trail/frameworks/active_record/models/paper_trail/version_association"
|
require "paper_trail/frameworks/active_record/models/paper_trail/version_association"
|
||||||
require "paper_trail/frameworks/active_record/models/paper_trail/version"
|
require "paper_trail/frameworks/active_record/models/paper_trail/version"
|
||||||
|
|
|
@ -36,24 +36,26 @@ module PaperTrail
|
||||||
#
|
#
|
||||||
# The columns `ip` and `user_agent` must exist in your `versions` # table.
|
# The columns `ip` and `user_agent` must exist in your `versions` # table.
|
||||||
#
|
#
|
||||||
# Use the `:meta` option to `PaperTrail::Model::ClassMethods.has_paper_trail`
|
# Use the `:meta` option to
|
||||||
# to store any extra model-level data you need.
|
# `PaperTrail::Model::ClassMethods.has_paper_trail` to store any extra
|
||||||
|
# model-level data you need.
|
||||||
def info_for_paper_trail
|
def info_for_paper_trail
|
||||||
{}
|
{}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns `true` (default) or `false` depending on whether PaperTrail should
|
# Returns `true` (default) or `false` depending on whether PaperTrail
|
||||||
# be active for the current request.
|
# should be active for the current request.
|
||||||
#
|
#
|
||||||
# Override this method in your controller to specify when PaperTrail should
|
# Override this method in your controller to specify when PaperTrail
|
||||||
# be off.
|
# should be off.
|
||||||
def paper_trail_enabled_for_controller
|
def paper_trail_enabled_for_controller
|
||||||
::PaperTrail.enabled?
|
::PaperTrail.enabled?
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Tells PaperTrail whether versions should be saved in the current request.
|
# Tells PaperTrail whether versions should be saved in the current
|
||||||
|
# request.
|
||||||
def set_paper_trail_enabled_for_controller
|
def set_paper_trail_enabled_for_controller
|
||||||
::PaperTrail.enabled_for_controller = paper_trail_enabled_for_controller
|
::PaperTrail.enabled_for_controller = paper_trail_enabled_for_controller
|
||||||
end
|
end
|
||||||
|
@ -63,8 +65,8 @@ module PaperTrail
|
||||||
::PaperTrail.whodunnit = user_for_paper_trail if ::PaperTrail.enabled_for_controller?
|
::PaperTrail.whodunnit = user_for_paper_trail if ::PaperTrail.enabled_for_controller?
|
||||||
end
|
end
|
||||||
|
|
||||||
# Tells PaperTrail any information from the controller you want
|
# Tells PaperTrail any information from the controller you want to store
|
||||||
# to store alongside any changes that occur.
|
# alongside any changes that occur.
|
||||||
def set_paper_trail_controller_info
|
def set_paper_trail_controller_info
|
||||||
::PaperTrail.controller_info = info_for_paper_trail if ::PaperTrail.enabled_for_controller?
|
::PaperTrail.controller_info = info_for_paper_trail if ::PaperTrail.enabled_for_controller?
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,8 @@ require 'active_support/core_ext/object' # provides the `try` method
|
||||||
module PaperTrail
|
module PaperTrail
|
||||||
module Sinatra
|
module Sinatra
|
||||||
|
|
||||||
# Register this module inside your Sinatra application to gain access to controller-level methods used by PaperTrail
|
# Register this module inside your Sinatra application to gain access to
|
||||||
|
# controller-level methods used by PaperTrail.
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
app.use RequestStore::Middleware
|
app.use RequestStore::Middleware
|
||||||
app.helpers self
|
app.helpers self
|
||||||
|
|
|
@ -8,31 +8,42 @@ module PaperTrail
|
||||||
end
|
end
|
||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
# Declare this in your model to track every create, update, and destroy. Each version of
|
# Declare this in your model to track every create, update, and destroy.
|
||||||
# the model is available in the `versions` association.
|
# Each version of the model is available in the `versions` association.
|
||||||
#
|
#
|
||||||
# Options:
|
# Options:
|
||||||
# :on the events to track (optional; defaults to all of them). Set to an array of
|
#
|
||||||
# `:create`, `:update`, `:destroy` as desired.
|
# - :on - The events to track (optional; defaults to all of them). Set
|
||||||
# :class_name the name of a custom Version class. This class should inherit from `PaperTrail::Version`.
|
# to an array of `:create`, `:update`, `:destroy` as desired.
|
||||||
# :ignore an array of attributes for which a new `Version` will not be created if only they change.
|
# - :class_name - The name of a custom Version class. This class should
|
||||||
# it can also aceept a Hash as an argument where the key is the attribute to ignore (a `String` or `Symbol`),
|
# inherit from `PaperTrail::Version`.
|
||||||
# which will only be ignored if the value is a `Proc` which returns truthily.
|
# - :ignore - An array of attributes for which a new `Version` will not be
|
||||||
# :if, :unless Procs that allow to specify conditions when to save versions for an object
|
# created if only they change. It can also aceept a Hash as an
|
||||||
# :only inverse of `ignore` - a new `Version` will be created only for these attributes if supplied
|
# argument where the key is the attribute to ignore (a `String` or
|
||||||
# it can also aceept a Hash as an argument where the key is the attribute to track (a `String` or `Symbol`),
|
# `Symbol`), which will only be ignored if the value is a `Proc` which
|
||||||
# which will only be counted if the value is a `Proc` which returns truthily.
|
# returns truthily.
|
||||||
# :skip fields to ignore completely. As with `ignore`, updates to these fields will not create
|
# - :if, :unless - Procs that allow to specify conditions when to save
|
||||||
# a new `Version`. In addition, these fields will not be included in the serialized versions
|
# versions for an object.
|
||||||
# of the object whenever a new `Version` is created.
|
# - :only - Inverse of `ignore`. A new `Version` will be created only
|
||||||
# :meta a hash of extra data to store. You must add a column to the `versions` table for each key.
|
# for these attributes if supplied it can also aceept a Hash as an
|
||||||
# Values are objects or procs (which are called with `self`, i.e. the model with the paper
|
# argument where the key is the attribute to track (a `String` or
|
||||||
# trail). See `PaperTrail::Controller.info_for_paper_trail` for how to store data from
|
# `Symbol`), which will only be counted if the value is a `Proc` which
|
||||||
# the controller.
|
# returns truthily.
|
||||||
# :versions the name to use for the versions association. Default is `:versions`.
|
# - :skip - Fields to ignore completely. As with `ignore`, updates to
|
||||||
# :version the name to use for the method which returns the version the instance was reified from.
|
# these fields will not create a new `Version`. In addition, these
|
||||||
# Default is `:version`.
|
# fields will not be included in the serialized versions of the object
|
||||||
# :save_changes whether or not to save changes to the object_changes column if it exists. Default is true
|
# whenever a new `Version` is created.
|
||||||
|
# - :meta - A hash of extra data to store. You must add a column to the
|
||||||
|
# `versions` table for each key. Values are objects or procs (which
|
||||||
|
# are called with `self`, i.e. the model with the paper trail). See
|
||||||
|
# `PaperTrail::Controller.info_for_paper_trail` for how to store data
|
||||||
|
# from the controller.
|
||||||
|
# - :versions - The name to use for the versions association. Default
|
||||||
|
# is `:versions`.
|
||||||
|
# - :version - The name to use for the method which returns the version
|
||||||
|
# the instance was reified from. Default is `:version`.
|
||||||
|
# - :save_changes - Whether or not to save changes to the object_changes
|
||||||
|
# column if it exists. Default is true
|
||||||
#
|
#
|
||||||
def has_paper_trail(options = {})
|
def has_paper_trail(options = {})
|
||||||
# Lazily include the instance methods so we don't clutter up
|
# Lazily include the instance methods so we don't clutter up
|
||||||
|
@ -64,7 +75,8 @@ module PaperTrail
|
||||||
|
|
||||||
attr_accessor :paper_trail_event
|
attr_accessor :paper_trail_event
|
||||||
|
|
||||||
if ::ActiveRecord::VERSION::MAJOR >= 4 # `has_many` syntax for specifying order uses a lambda in Rails 4
|
# `has_many` syntax for specifying order uses a lambda in Rails 4
|
||||||
|
if ::ActiveRecord::VERSION::MAJOR >= 4
|
||||||
has_many self.versions_association_name,
|
has_many self.versions_association_name,
|
||||||
lambda { order(model.timestamp_sort_order) },
|
lambda { order(model.timestamp_sort_order) },
|
||||||
:class_name => self.version_class_name, :as => :item
|
:class_name => self.version_class_name, :as => :item
|
||||||
|
@ -76,7 +88,11 @@ module PaperTrail
|
||||||
end
|
end
|
||||||
|
|
||||||
options[:on] ||= [:create, :update, :destroy]
|
options[:on] ||= [:create, :update, :destroy]
|
||||||
options_on = Array(options[:on]) # so that a single symbol can be passed in without wrapping it in an `Array`
|
|
||||||
|
# Wrap the :on option in an array if necessary. This allows a single
|
||||||
|
# symbol to be passed in.
|
||||||
|
options_on = Array(options[:on])
|
||||||
|
|
||||||
after_create :record_create, :if => :save_version? if options_on.include?(:create)
|
after_create :record_create, :if => :save_version? if options_on.include?(:create)
|
||||||
if options_on.include?(:update)
|
if options_on.include?(:update)
|
||||||
before_save :reset_timestamp_attrs_for_update_if_needed!, :on => :update
|
before_save :reset_timestamp_attrs_for_update_if_needed!, :on => :update
|
||||||
|
@ -85,7 +101,7 @@ module PaperTrail
|
||||||
end
|
end
|
||||||
after_destroy :record_destroy, :if => :save_version? if options_on.include?(:destroy)
|
after_destroy :record_destroy, :if => :save_version? if options_on.include?(:destroy)
|
||||||
|
|
||||||
# Reset the transaction id when the transaction is closed
|
# Reset the transaction id when the transaction is closed.
|
||||||
after_commit :reset_transaction_id
|
after_commit :reset_transaction_id
|
||||||
after_rollback :reset_transaction_id
|
after_rollback :reset_transaction_id
|
||||||
after_rollback :clear_rolled_back_versions
|
after_rollback :clear_rolled_back_versions
|
||||||
|
@ -110,40 +126,47 @@ module PaperTrail
|
||||||
@paper_trail_version_class ||= version_class_name.constantize
|
@paper_trail_version_class ||= version_class_name.constantize
|
||||||
end
|
end
|
||||||
|
|
||||||
# Used for Version#object attribute
|
# Used for `Version#object` attribute.
|
||||||
def serialize_attributes_for_paper_trail!(attributes)
|
def serialize_attributes_for_paper_trail!(attributes)
|
||||||
# don't serialize before values before inserting into columns of type `JSON` on `PostgreSQL` databases
|
# Don't serialize before values before inserting into columns of type
|
||||||
|
# `JSON` on `PostgreSQL` databases.
|
||||||
return attributes if self.paper_trail_version_class.object_col_is_json?
|
return attributes if self.paper_trail_version_class.object_col_is_json?
|
||||||
|
|
||||||
serialized_attributes.each do |key, coder|
|
serialized_attributes.each do |key, coder|
|
||||||
if attributes.key?(key)
|
if attributes.key?(key)
|
||||||
# Fall back to current serializer if `coder` has no `dump` method
|
# Fall back to current serializer if `coder` has no `dump` method.
|
||||||
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
||||||
attributes[key] = coder.dump(attributes[key])
|
attributes[key] = coder.dump(attributes[key])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# TODO: There is a lot of duplication between this and
|
||||||
|
# `serialize_attributes_for_paper_trail!`.
|
||||||
def unserialize_attributes_for_paper_trail!(attributes)
|
def unserialize_attributes_for_paper_trail!(attributes)
|
||||||
# don't serialize before values before inserting into columns of type `JSON` on `PostgreSQL` databases
|
# Don't serialize before values before inserting into columns of type
|
||||||
|
# `JSON` on `PostgreSQL` databases.
|
||||||
return attributes if self.paper_trail_version_class.object_col_is_json?
|
return attributes if self.paper_trail_version_class.object_col_is_json?
|
||||||
|
|
||||||
serialized_attributes.each do |key, coder|
|
serialized_attributes.each do |key, coder|
|
||||||
if attributes.key?(key)
|
if attributes.key?(key)
|
||||||
|
# Fall back to current serializer if `coder` has no `dump` method.
|
||||||
|
# TODO: Shouldn't this be `:load`?
|
||||||
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
||||||
attributes[key] = coder.load(attributes[key])
|
attributes[key] = coder.load(attributes[key])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Used for Version#object_changes attribute
|
# Used for Version#object_changes attribute.
|
||||||
def serialize_attribute_changes_for_paper_trail!(changes)
|
def serialize_attribute_changes_for_paper_trail!(changes)
|
||||||
# don't serialize before values before inserting into columns of type `JSON` on `PostgreSQL` databases
|
# Don't serialize before values before inserting into columns of type `JSON`
|
||||||
|
# on `PostgreSQL` databases.
|
||||||
return changes if self.paper_trail_version_class.object_changes_col_is_json?
|
return changes if self.paper_trail_version_class.object_changes_col_is_json?
|
||||||
|
|
||||||
serialized_attributes.each do |key, coder|
|
serialized_attributes.each do |key, coder|
|
||||||
if changes.key?(key)
|
if changes.key?(key)
|
||||||
# Fall back to current serializer if `coder` has no `dump` method
|
# Fall back to current serializer if `coder` has no `dump` method.
|
||||||
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
||||||
old_value, new_value = changes[key]
|
old_value, new_value = changes[key]
|
||||||
changes[key] = [coder.dump(old_value),
|
changes[key] = [coder.dump(old_value),
|
||||||
|
@ -152,12 +175,17 @@ module PaperTrail
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# TODO: There is a lot of duplication between this and
|
||||||
|
# `serialize_attribute_changes_for_paper_trail!`.
|
||||||
def unserialize_attribute_changes_for_paper_trail!(changes)
|
def unserialize_attribute_changes_for_paper_trail!(changes)
|
||||||
# don't serialize before values before inserting into columns of type `JSON` on `PostgreSQL` databases
|
# Don't serialize before values before inserting into columns of type
|
||||||
|
# `JSON` on `PostgreSQL` databases.
|
||||||
return changes if self.paper_trail_version_class.object_changes_col_is_json?
|
return changes if self.paper_trail_version_class.object_changes_col_is_json?
|
||||||
|
|
||||||
serialized_attributes.each do |key, coder|
|
serialized_attributes.each do |key, coder|
|
||||||
if changes.key?(key)
|
if changes.key?(key)
|
||||||
|
# Fall back to current serializer if `coder` has no `dump` method.
|
||||||
|
# TODO: Shouldn't this be `:load`?
|
||||||
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
coder = PaperTrail.serializer unless coder.respond_to?(:dump)
|
||||||
old_value, new_value = changes[key]
|
old_value, new_value = changes[key]
|
||||||
changes[key] = [coder.load(old_value),
|
changes[key] = [coder.load(old_value),
|
||||||
|
@ -236,7 +264,8 @@ module PaperTrail
|
||||||
self.class.paper_trail_on! if paper_trail_was_enabled
|
self.class.paper_trail_on! if paper_trail_was_enabled
|
||||||
end
|
end
|
||||||
|
|
||||||
# Utility method for reifying. Anything executed inside the block will appear like a new record
|
# Utility method for reifying. Anything executed inside the block will
|
||||||
|
# appear like a new record.
|
||||||
def appear_as_new_record
|
def appear_as_new_record
|
||||||
instance_eval {
|
instance_eval {
|
||||||
alias :old_new_record? :new_record?
|
alias :old_new_record? :new_record?
|
||||||
|
@ -246,7 +275,8 @@ module PaperTrail
|
||||||
instance_eval { alias :new_record? :old_new_record? }
|
instance_eval { alias :new_record? :old_new_record? }
|
||||||
end
|
end
|
||||||
|
|
||||||
# Temporarily overwrites the value of whodunnit and then executes the provided block.
|
# Temporarily overwrites the value of whodunnit and then executes the
|
||||||
|
# provided block.
|
||||||
def whodunnit(value)
|
def whodunnit(value)
|
||||||
raise ArgumentError, 'expected to receive a block' unless block_given?
|
raise ArgumentError, 'expected to receive a block' unless block_given?
|
||||||
current_whodunnit = PaperTrail.whodunnit
|
current_whodunnit = PaperTrail.whodunnit
|
||||||
|
@ -344,15 +374,19 @@ module PaperTrail
|
||||||
_changes.to_hash
|
_changes.to_hash
|
||||||
end
|
end
|
||||||
|
|
||||||
# Invoked via`after_update` callback for when a previous version is reified and then saved
|
# Invoked via`after_update` callback for when a previous version is
|
||||||
|
# reified and then saved.
|
||||||
def clear_version_instance!
|
def clear_version_instance!
|
||||||
send("#{self.class.version_association_name}=", nil)
|
send("#{self.class.version_association_name}=", nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Invoked via callback when a user attempts to persist a reified
|
||||||
|
# `Version`.
|
||||||
def reset_timestamp_attrs_for_update_if_needed!
|
def reset_timestamp_attrs_for_update_if_needed!
|
||||||
return if self.live? # invoked via callback when a user attempts to persist a reified `Version`
|
return if self.live?
|
||||||
timestamp_attributes_for_update_in_model.each do |column|
|
timestamp_attributes_for_update_in_model.each do |column|
|
||||||
# ActiveRecord 4.2 deprecated `reset_column!` in favor of `restore_column!`
|
# ActiveRecord 4.2 deprecated `reset_column!` in favor of
|
||||||
|
# `restore_column!`.
|
||||||
if respond_to?("restore_#{column}!")
|
if respond_to?("restore_#{column}!")
|
||||||
send("restore_#{column}!")
|
send("restore_#{column}!")
|
||||||
else
|
else
|
||||||
|
@ -382,7 +416,7 @@ module PaperTrail
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# saves associations if the join table for `VersionAssociation` exists
|
# Saves associations if the join table for `VersionAssociation` exists.
|
||||||
def save_associations(version)
|
def save_associations(version)
|
||||||
return unless PaperTrail.config.track_associations?
|
return unless PaperTrail.config.track_associations?
|
||||||
self.class.reflect_on_all_associations(:belongs_to).each do |assoc|
|
self.class.reflect_on_all_associations(:belongs_to).each do |assoc|
|
||||||
|
@ -424,8 +458,8 @@ module PaperTrail
|
||||||
if v.respond_to?(:call)
|
if v.respond_to?(:call)
|
||||||
v.call(self)
|
v.call(self)
|
||||||
elsif v.is_a?(Symbol) && respond_to?(v)
|
elsif v.is_a?(Symbol) && respond_to?(v)
|
||||||
# if it is an attribute that is changing in an existing object,
|
# If it is an attribute that is changing in an existing object,
|
||||||
# be sure to grab the current version
|
# be sure to grab the current version.
|
||||||
if has_attribute?(v) && send("#{v}_changed?".to_sym) && data[:event] != 'create'
|
if has_attribute?(v) && send("#{v}_changed?".to_sym) && data[:event] != 'create'
|
||||||
send("#{v}_was".to_sym)
|
send("#{v}_was".to_sym)
|
||||||
else
|
else
|
||||||
|
@ -435,6 +469,7 @@ module PaperTrail
|
||||||
v
|
v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Second we merge any extra data from the controller (if available).
|
# Second we merge any extra data from the controller (if available).
|
||||||
data.merge(PaperTrail.controller_info || {})
|
data.merge(PaperTrail.controller_info || {})
|
||||||
end
|
end
|
||||||
|
@ -449,8 +484,8 @@ module PaperTrail
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# returns hash of attributes (with appropriate attributes serialized),
|
# Returns hash of attributes (with appropriate attributes serialized),
|
||||||
# ommitting attributes to be skipped
|
# ommitting attributes to be skipped.
|
||||||
def object_attrs_for_paper_trail(attributes_hash)
|
def object_attrs_for_paper_trail(attributes_hash)
|
||||||
attrs = attributes_hash.except(*self.paper_trail_options[:skip])
|
attrs = attributes_hash.except(*self.paper_trail_options[:skip])
|
||||||
if PaperTrail.serialized_attributes?
|
if PaperTrail.serialized_attributes?
|
||||||
|
@ -459,10 +494,9 @@ module PaperTrail
|
||||||
attrs
|
attrs
|
||||||
end
|
end
|
||||||
|
|
||||||
# This method determines whether it is appropriate to generate a new
|
# Determines whether it is appropriate to generate a new version
|
||||||
# version instance. A timestamp-only update (e.g. only `updated_at`
|
# instance. A timestamp-only update (e.g. only `updated_at` changed) is
|
||||||
# changed) is considered notable unless an ignored attribute was also
|
# considered notable unless an ignored attribute was also changed.
|
||||||
# changed.
|
|
||||||
def changed_notably?
|
def changed_notably?
|
||||||
if ignored_attr_has_changed?
|
if ignored_attr_has_changed?
|
||||||
timestamps = timestamp_attributes_for_update_in_model.map(&:to_s)
|
timestamps = timestamp_attributes_for_update_in_model.map(&:to_s)
|
||||||
|
@ -473,8 +507,8 @@ module PaperTrail
|
||||||
end
|
end
|
||||||
|
|
||||||
# An attributed is "ignored" if it is listed in the `:ignore` option
|
# An attributed is "ignored" if it is listed in the `:ignore` option
|
||||||
# and/or the `:skip` option. Returns true if an ignored attribute
|
# and/or the `:skip` option. Returns true if an ignored attribute has
|
||||||
# has changed.
|
# changed.
|
||||||
def ignored_attr_has_changed?
|
def ignored_attr_has_changed?
|
||||||
ignored = self.paper_trail_options[:ignore] + self.paper_trail_options[:skip]
|
ignored = self.paper_trail_options[:ignore] + self.paper_trail_options[:skip]
|
||||||
ignored.any? && (changed & ignored).any?
|
ignored.any? && (changed & ignored).any?
|
||||||
|
@ -482,7 +516,8 @@ module PaperTrail
|
||||||
|
|
||||||
def notably_changed
|
def notably_changed
|
||||||
only = self.paper_trail_options[:only].dup
|
only = self.paper_trail_options[:only].dup
|
||||||
# remove Hash arguments and then evaluate whether the attributes (the keys of the hash) should also get pushed into the collection
|
# Remove Hash arguments and then evaluate whether the attributes (the
|
||||||
|
# keys of the hash) should also get pushed into the collection.
|
||||||
only.delete_if do |obj|
|
only.delete_if do |obj|
|
||||||
obj.is_a?(Hash) && obj.each { |attr, condition| only << attr if condition.respond_to?(:call) && condition.call(self) }
|
obj.is_a?(Hash) && obj.each { |attr, condition| only << attr if condition.respond_to?(:call) && condition.call(self) }
|
||||||
end
|
end
|
||||||
|
@ -491,7 +526,8 @@ module PaperTrail
|
||||||
|
|
||||||
def changed_and_not_ignored
|
def changed_and_not_ignored
|
||||||
ignore = self.paper_trail_options[:ignore].dup
|
ignore = self.paper_trail_options[:ignore].dup
|
||||||
# remove Hash arguments and then evaluate whether the attributes (the keys of the hash) should also get pushed into the collection
|
# Remove Hash arguments and then evaluate whether the attributes (the
|
||||||
|
# keys of the hash) should also get pushed into the collection.
|
||||||
ignore.delete_if do |obj|
|
ignore.delete_if do |obj|
|
||||||
obj.is_a?(Hash) && obj.each { |attr, condition| ignore << attr if condition.respond_to?(:call) && condition.call(self) }
|
obj.is_a?(Hash) && obj.each { |attr, condition| ignore << attr if condition.respond_to?(:call) && condition.call(self) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,10 +7,10 @@ module PaperTrail
|
||||||
included do
|
included do
|
||||||
belongs_to :item, :polymorphic => true
|
belongs_to :item, :polymorphic => true
|
||||||
|
|
||||||
# Since the test suite has test coverage for this, we want to declare the
|
# Since the test suite has test coverage for this, we want to declare
|
||||||
# association when the test suite is running. This makes it pass
|
# the association when the test suite is running. This makes it pass when
|
||||||
# when DB is not initialized prior to test runs such as when we run on
|
# DB is not initialized prior to test runs such as when we run on Travis
|
||||||
# Travis CI (there won't be a db in `test/dummy/db/`)
|
# CI (there won't be a db in `test/dummy/db/`).
|
||||||
if PaperTrail.config.track_associations?
|
if PaperTrail.config.track_associations?
|
||||||
has_many :version_associations, :dependent => :destroy
|
has_many :version_associations, :dependent => :destroy
|
||||||
end
|
end
|
||||||
|
@ -47,8 +47,8 @@ module PaperTrail
|
||||||
where 'event <> ?', 'create'
|
where 'event <> ?', 'create'
|
||||||
end
|
end
|
||||||
|
|
||||||
# Expects `obj` to be an instance of `PaperTrail::Version` by default, but can accept a timestamp if
|
# Expects `obj` to be an instance of `PaperTrail::Version` by default,
|
||||||
# `timestamp_arg` receives `true`
|
# but can accept a timestamp if `timestamp_arg` receives `true`
|
||||||
def subsequent(obj, timestamp_arg = false)
|
def subsequent(obj, timestamp_arg = false)
|
||||||
if timestamp_arg != true && self.primary_key_is_int?
|
if timestamp_arg != true && self.primary_key_is_int?
|
||||||
return where(arel_table[primary_key].gt(obj.id)).order(arel_table[primary_key].asc)
|
return where(arel_table[primary_key].gt(obj.id)).order(arel_table[primary_key].asc)
|
||||||
|
@ -75,7 +75,8 @@ module PaperTrail
|
||||||
).order(self.timestamp_sort_order)
|
).order(self.timestamp_sort_order)
|
||||||
end
|
end
|
||||||
|
|
||||||
# defaults to using the primary key as the secondary sort order if possible
|
# Defaults to using the primary key as the secondary sort order if
|
||||||
|
# possible.
|
||||||
def timestamp_sort_order(direction = 'asc')
|
def timestamp_sort_order(direction = 'asc')
|
||||||
[arel_table[PaperTrail.timestamp_field].send(direction.downcase)].tap do |array|
|
[arel_table[PaperTrail.timestamp_field].send(direction.downcase)].tap do |array|
|
||||||
array << arel_table[primary_key].send(direction.downcase) if self.primary_key_is_int?
|
array << arel_table[primary_key].send(direction.downcase) if self.primary_key_is_int?
|
||||||
|
@ -137,12 +138,14 @@ module PaperTrail
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns whether the `object` column is using the `json` type supported by PostgreSQL
|
# Returns whether the `object` column is using the `json` type supported
|
||||||
|
# by PostgreSQL.
|
||||||
def object_col_is_json?
|
def object_col_is_json?
|
||||||
[:json, :jsonb].include?(columns_hash['object'].type)
|
[:json, :jsonb].include?(columns_hash['object'].type)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns whether the `object_changes` column is using the `json` type supported by PostgreSQL
|
# Returns whether the `object_changes` column is using the `json` type
|
||||||
|
# supported by PostgreSQL.
|
||||||
def object_changes_col_is_json?
|
def object_changes_col_is_json?
|
||||||
[:json, :jsonb].include?(columns_hash['object_changes'].try(:type))
|
[:json, :jsonb].include?(columns_hash['object_changes'].try(:type))
|
||||||
end
|
end
|
||||||
|
@ -150,22 +153,31 @@ module PaperTrail
|
||||||
|
|
||||||
# Restore the item from this version.
|
# Restore the item from this version.
|
||||||
#
|
#
|
||||||
# Optionally this can also restore all :has_one and :has_many (including has_many :through) associations as
|
# Optionally this can also restore all :has_one and :has_many (including
|
||||||
# they were "at the time", if they are also being versioned by PaperTrail.
|
# has_many :through) associations as they were "at the time", if they are
|
||||||
|
# also being versioned by PaperTrail.
|
||||||
#
|
#
|
||||||
# Options:
|
# Options:
|
||||||
# :has_one set to `true` to also reify has_one associations. Default is `false`.
|
#
|
||||||
# :has_many set to `true` to also reify has_many and has_many :through associations.
|
# - :has_one
|
||||||
# Default is `false`.
|
# - `true` - Also reify has_one associations.
|
||||||
# :mark_for_destruction set to `true` to mark the has_one/has_many associations that did not exist in the
|
# - `false - Default.
|
||||||
# reified version for destruction, instead of remove them. Default is `false`.
|
# - :has_many
|
||||||
# This option is handy for people who want to persist the reified version.
|
# - `true` - Also reify has_many and has_many :through associations.
|
||||||
# :dup `false` default behavior
|
# - `false` - Default.
|
||||||
# `true` it always create a new object instance. It is useful for comparing two versions of the same object
|
# - :mark_for_destruction
|
||||||
# :unversioned_attributes `:nil` - (default) attributes undefined in version record
|
# - `true` - Mark the has_one/has_many associations that did not exist in
|
||||||
# are set to nil in reified record
|
# the reified version for destruction, instead of removing them.
|
||||||
# `:preserve` - attributes undefined in version record are
|
# - `false` - Default. Useful for persisting the reified version.
|
||||||
# not modified
|
# - :dup
|
||||||
|
# - `false` - Default.
|
||||||
|
# - `true` - Always create a new object instance. Useful for
|
||||||
|
# comparing two versions of the same object.
|
||||||
|
# - :unversioned_attributes
|
||||||
|
# - `:nil` - Default. Attributes undefined in version record are set to
|
||||||
|
# nil in reified record.
|
||||||
|
# - `:preserve` - Attributes undefined in version record are not modified.
|
||||||
|
#
|
||||||
def reify(options = {})
|
def reify(options = {})
|
||||||
return nil if object.nil?
|
return nil if object.nil?
|
||||||
|
|
||||||
|
@ -180,22 +192,22 @@ module PaperTrail
|
||||||
|
|
||||||
attrs = self.class.object_col_is_json? ? object : PaperTrail.serializer.load(object)
|
attrs = self.class.object_col_is_json? ? object : PaperTrail.serializer.load(object)
|
||||||
|
|
||||||
# Normally a polymorphic belongs_to relationship allows us
|
# Normally a polymorphic belongs_to relationship allows us to get the
|
||||||
# to get the object we belong to by calling, in this case,
|
# object we belong to by calling, in this case, `item`. However this
|
||||||
# `item`. However this returns nil if `item` has been
|
# returns nil if `item` has been destroyed, and we need to be able to
|
||||||
# destroyed, and we need to be able to retrieve destroyed
|
# retrieve destroyed objects.
|
||||||
# objects.
|
|
||||||
#
|
#
|
||||||
# In this situation we constantize the `item_type` to get hold of
|
# In this situation we constantize the `item_type` to get hold of the
|
||||||
# the class...except when the stored object's attributes
|
# class...except when the stored object's attributes include a `type`
|
||||||
# include a `type` key. If this is the case, the object
|
# key. If this is the case, the object we belong to is using single
|
||||||
# we belong to is using single table inheritance and the
|
# table inheritance and the `item_type` will be the base class, not the
|
||||||
# `item_type` will be the base class, not the actual subclass.
|
# actual subclass. If `type` is present but empty, the class is the base
|
||||||
# If `type` is present but empty, the class is the base class.
|
# class.
|
||||||
|
|
||||||
if options[:dup] != true && item
|
if options[:dup] != true && item
|
||||||
model = item
|
model = item
|
||||||
# Look for attributes that exist in the model and not in this version. These attributes should be set to nil.
|
# Look for attributes that exist in the model and not in this
|
||||||
|
# version. These attributes should be set to nil.
|
||||||
if options[:unversioned_attributes] == :nil
|
if options[:unversioned_attributes] == :nil
|
||||||
(model.attribute_names - attrs.keys).each { |k| attrs[k] = nil }
|
(model.attribute_names - attrs.keys).each { |k| attrs[k] = nil }
|
||||||
end
|
end
|
||||||
|
@ -203,13 +215,14 @@ module PaperTrail
|
||||||
inheritance_column_name = item_type.constantize.inheritance_column
|
inheritance_column_name = item_type.constantize.inheritance_column
|
||||||
class_name = attrs[inheritance_column_name].blank? ? item_type : attrs[inheritance_column_name]
|
class_name = attrs[inheritance_column_name].blank? ? item_type : attrs[inheritance_column_name]
|
||||||
klass = class_name.constantize
|
klass = class_name.constantize
|
||||||
# the `dup` option always returns a new object, otherwise we should attempt
|
# The `dup` option always returns a new object, otherwise we should
|
||||||
# to look for the item outside of default scope(s)
|
# attempt to look for the item outside of default scope(s).
|
||||||
if options[:dup] || (_item = klass.unscoped.find_by_id(item_id)).nil?
|
if options[:dup] || (_item = klass.unscoped.find_by_id(item_id)).nil?
|
||||||
model = klass.new
|
model = klass.new
|
||||||
elsif options[:unversioned_attributes] == :nil
|
elsif options[:unversioned_attributes] == :nil
|
||||||
model = _item
|
model = _item
|
||||||
# Look for attributes that exist in the model and not in this version. These attributes should be set to nil.
|
# Look for attributes that exist in the model and not in this
|
||||||
|
# version. These attributes should be set to nil.
|
||||||
(model.attribute_names - attrs.keys).each { |k| attrs[k] = nil }
|
(model.attribute_names - attrs.keys).each { |k| attrs[k] = nil }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -218,7 +231,7 @@ module PaperTrail
|
||||||
model.class.unserialize_attributes_for_paper_trail! attrs
|
model.class.unserialize_attributes_for_paper_trail! attrs
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set all the attributes in this version on the model
|
# Set all the attributes in this version on the model.
|
||||||
attrs.each do |k, v|
|
attrs.each do |k, v|
|
||||||
if model.has_attribute?(k)
|
if model.has_attribute?(k)
|
||||||
model[k.to_sym] = v
|
model[k.to_sym] = v
|
||||||
|
@ -243,8 +256,9 @@ module PaperTrail
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns what changed in this version of the item. `ActiveModel::Dirty#changes`.
|
# Returns what changed in this version of the item.
|
||||||
# returns `nil` if your `versions` table does not have an `object_changes` text column.
|
# `ActiveModel::Dirty#changes`. returns `nil` if your `versions` table does
|
||||||
|
# not have an `object_changes` text column.
|
||||||
def changeset
|
def changeset
|
||||||
return nil unless self.class.column_names.include? 'object_changes'
|
return nil unless self.class.column_names.include? 'object_changes'
|
||||||
|
|
||||||
|
@ -268,8 +282,8 @@ module PaperTrail
|
||||||
self.paper_trail_originator
|
self.paper_trail_originator
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns who changed the item from the state it had in this version.
|
# Returns who changed the item from the state it had in this version. This
|
||||||
# This is an alias for `whodunnit`.
|
# is an alias for `whodunnit`.
|
||||||
def terminator
|
def terminator
|
||||||
@terminator ||= whodunnit
|
@terminator ||= whodunnit
|
||||||
end
|
end
|
||||||
|
@ -311,9 +325,9 @@ module PaperTrail
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Restore the `model`'s has_one associations as they were when this version was
|
# Restore the `model`'s has_one associations as they were when this
|
||||||
# superseded by the next (because that's what the user was looking at when they
|
# version was superseded by the next (because that's what the user was
|
||||||
# made the change).
|
# looking at when they made the change).
|
||||||
def reify_has_ones(model, options = {})
|
def reify_has_ones(model, options = {})
|
||||||
version_table_name = model.class.paper_trail_version_class.table_name
|
version_table_name = model.class.paper_trail_version_class.table_name
|
||||||
model.class.reflect_on_all_associations(:has_one).each do |assoc|
|
model.class.reflect_on_all_associations(:has_one).each do |assoc|
|
||||||
|
@ -344,8 +358,9 @@ module PaperTrail
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Restore the `model`'s has_many associations as they were at version_at timestamp
|
# Restore the `model`'s has_many associations as they were at version_at
|
||||||
# We lookup the first child versions after version_at timestamp or in same transaction.
|
# timestamp We lookup the first child versions after version_at timestamp or
|
||||||
|
# in same transaction.
|
||||||
def reify_has_manys(model, options = {})
|
def reify_has_manys(model, options = {})
|
||||||
assoc_has_many_through, assoc_has_many_directly =
|
assoc_has_many_through, assoc_has_many_directly =
|
||||||
model.class.reflect_on_all_associations(:has_many).
|
model.class.reflect_on_all_associations(:has_many).
|
||||||
|
@ -354,7 +369,8 @@ module PaperTrail
|
||||||
reify_has_many_through(assoc_has_many_through, model, options)
|
reify_has_many_through(assoc_has_many_through, model, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Restore the `model`'s has_many associations not associated through another association
|
# Restore the `model`'s has_many associations not associated through
|
||||||
|
# another association.
|
||||||
def reify_has_many_directly(associations, model, options = {})
|
def reify_has_many_directly(associations, model, options = {})
|
||||||
version_table_name = model.class.paper_trail_version_class.table_name
|
version_table_name = model.class.paper_trail_version_class.table_name
|
||||||
associations.each do |assoc|
|
associations.each do |assoc|
|
||||||
|
@ -370,10 +386,11 @@ module PaperTrail
|
||||||
acc.merge!(v.item_id => v)
|
acc.merge!(v.item_id => v)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Pass true to force the model to load
|
# Pass true to force the model to load.
|
||||||
collection = Array.new model.send(assoc.name, true)
|
collection = Array.new model.send(assoc.name, true)
|
||||||
|
|
||||||
# Iterate each child to replace it with the previous value if there is a version after the timestamp
|
# Iterate each child to replace it with the previous value if there is
|
||||||
|
# a version after the timestamp.
|
||||||
collection.map! do |c|
|
collection.map! do |c|
|
||||||
if (version = versions.delete(c.id)).nil?
|
if (version = versions.delete(c.id)).nil?
|
||||||
c
|
c
|
||||||
|
@ -384,16 +401,18 @@ module PaperTrail
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Reify the rest of the versions and add them to the collection, these versions are for those that
|
# Reify the rest of the versions and add them to the collection, these
|
||||||
# have been removed from the live associations
|
# versions are for those that have been removed from the live
|
||||||
|
# associations.
|
||||||
collection += versions.values.map { |version| version.reify(options.merge(:has_many => false, :has_one => false)) }
|
collection += versions.values.map { |version| version.reify(options.merge(:has_many => false, :has_one => false)) }
|
||||||
|
|
||||||
model.send(assoc.name).proxy_association.target = collection.compact
|
model.send(assoc.name).proxy_association.target = collection.compact
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Restore the `model`'s has_many associations through another association
|
# Restore the `model`'s has_many associations through another association.
|
||||||
# This must be called after the direct has_manys have been reified (reify_has_many_directly)
|
# This must be called after the direct has_manys have been reified
|
||||||
|
# (reify_has_many_directly).
|
||||||
def reify_has_many_through(associations, model, options = {})
|
def reify_has_many_through(associations, model, options = {})
|
||||||
associations.each do |assoc|
|
associations.each do |assoc|
|
||||||
next unless assoc.klass.paper_trail_enabled_for_model?
|
next unless assoc.klass.paper_trail_enabled_for_model?
|
||||||
|
@ -412,7 +431,8 @@ module PaperTrail
|
||||||
|
|
||||||
collection = Array.new assoc.klass.where(assoc.klass.primary_key => collection_keys)
|
collection = Array.new assoc.klass.where(assoc.klass.primary_key => collection_keys)
|
||||||
|
|
||||||
# Iterate each child to replace it with the previous value if there is a version after the timestamp
|
# Iterate each child to replace it with the previous value if there is
|
||||||
|
# a version after the timestamp.
|
||||||
collection.map! do |c|
|
collection.map! do |c|
|
||||||
if (version = versions.delete(c.id)).nil?
|
if (version = versions.delete(c.id)).nil?
|
||||||
c
|
c
|
||||||
|
@ -423,15 +443,17 @@ module PaperTrail
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Reify the rest of the versions and add them to the collection, these versions are for those that
|
# Reify the rest of the versions and add them to the collection, these
|
||||||
# have been removed from the live associations
|
# versions are for those that have been removed from the live
|
||||||
|
# associations.
|
||||||
collection += versions.values.map { |version| version.reify(options.merge(:has_many => false, :has_one => false)) }
|
collection += versions.values.map { |version| version.reify(options.merge(:has_many => false, :has_one => false)) }
|
||||||
|
|
||||||
model.send(assoc.name).proxy_association.target = collection.compact
|
model.send(assoc.name).proxy_association.target = collection.compact
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# checks to see if a value has been set for the `version_limit` config option, and if so enforces it
|
# Checks that a value has been set for the `version_limit` config
|
||||||
|
# option, and if so enforces it.
|
||||||
def enforce_version_limit!
|
def enforce_version_limit!
|
||||||
return unless PaperTrail.config.version_limit.is_a? Numeric
|
return unless PaperTrail.config.version_limit.is_a? Numeric
|
||||||
previous_versions = sibling_versions.not_creates
|
previous_versions = sibling_versions.not_creates
|
||||||
|
|
Loading…
Reference in New Issue