From 68a0ab79df6774031a83ade9745ab94be42e93d1 Mon Sep 17 00:00:00 2001 From: Jared Beck Date: Mon, 3 Aug 2015 16:45:42 -0400 Subject: [PATCH] Format comments - Wrap comments at 80 chars - Fix English grammar - Start sentances with capital letters - End sentances with a period - Use markdown for lists, e.g. method arguments [ci skip] --- lib/paper_trail.rb | 30 ++-- lib/paper_trail/cleaner.rb | 32 ++-- lib/paper_trail/frameworks/active_record.rb | 4 +- .../frameworks/rails/controller.rb | 20 +-- lib/paper_trail/frameworks/sinatra.rb | 3 +- lib/paper_trail/has_paper_trail.rb | 140 +++++++++++------- lib/paper_trail/version_concern.rb | 140 ++++++++++-------- 7 files changed, 222 insertions(+), 147 deletions(-) diff --git a/lib/paper_trail.rb b/lib/paper_trail.rb index de635fd2..b4fc45c5 100644 --- a/lib/paper_trail.rb +++ b/lib/paper_trail.rb @@ -25,7 +25,7 @@ module PaperTrail end # ActiveRecord 5 drops support for serialized attributes; for previous - # versions of ActiveRecord it is supported, we have a config option + # versions of ActiveRecord it is supported, we have a config option # to enable it within PaperTrail. def self.serialized_attributes? !!PaperTrail.config.serialized_attributes && ::ActiveRecord::VERSION::MAJOR < 5 @@ -43,12 +43,14 @@ module PaperTrail !!paper_trail_store[:request_enabled_for_controller] 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) paper_trail_store[:"enabled_for_#{model}"] = value 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) !!paper_trail_store.fetch(:"enabled_for_#{model}", true) end @@ -63,10 +65,9 @@ module PaperTrail PaperTrail.config.timestamp_field end - # Sets who is responsible for any changes that occur. - # You would normally use this in a migration or on the console, - # when working with models directly. In a controller it is set - # automatically to the `current_user`. + # Sets who is responsible for any changes that occur. You would normally use + # this in a migration or on the console, when working with models directly. + # In a controller it is set automatically to the `current_user`. def self.whodunnit=(value) paper_trail_store[:whodunnit] = value end @@ -76,8 +77,8 @@ module PaperTrail paper_trail_store[:whodunnit] end - # Sets any information from the controller that you want PaperTrail - # to store. By default this is set automatically by a before filter. + # Sets any information from the controller that you want PaperTrail to + # store. By default this is set automatically by a before filter. def self.controller_info=(value) paper_trail_store[:controller_info] = value end @@ -117,8 +118,8 @@ module PaperTrail private - # Thread-safe hash to hold PaperTrail's data. - # Initializing with needed default values. + # Thread-safe hash to hold PaperTrail's data. Initializing with needed + # default values. def self.paper_trail_store RequestStore.store[:paper_trail] ||= { :request_enabled_for_controller => true } end @@ -135,12 +136,15 @@ module PaperTrail 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? PaperTrail.send(:remove_instance_variable, :@active_record_protected_attributes) begin 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 ActiveSupport.on_load(:active_record) do diff --git a/lib/paper_trail/cleaner.rb b/lib/paper_trail/cleaner.rb index 35de9254..9db98bfa 100644 --- a/lib/paper_trail/cleaner.rb +++ b/lib/paper_trail/cleaner.rb @@ -1,19 +1,24 @@ module PaperTrail 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: - # :keeping An `integer` indicating the number of versions to be kept for each item per date. - # Defaults to `1`. - # :date Should either be a `Date` object specifying which date to destroy versions for or `:all`, - # which will specify that all dates 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`. + # + # - :keeping - An `integer` indicating the number of versions to be kept for + # each item per date. Defaults to `1`. + # - :date - Should either be a `Date` object specifying which date to + # destroy versions for or `:all`, which will specify that all dates + # 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 = {}) options = {:keeping => 1, :date => :all}.merge(options) 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| - # 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.map(&:destroy) end @@ -22,13 +27,18 @@ module PaperTrail private - # Returns a hash of versions grouped by the `item_id` attribute formatted 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. + # Returns a hash of versions grouped by the `item_id` attribute formatted + # 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) 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 = 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) end end diff --git a/lib/paper_trail/frameworks/active_record.rb b/lib/paper_trail/frameworks/active_record.rb index 1dfc9c54..d79e7c10 100644 --- a/lib/paper_trail/frameworks/active_record.rb +++ b/lib/paper_trail/frameworks/active_record.rb @@ -1,4 +1,4 @@ -# This file only needs to be loaded if the gem is being used outside of Rails, since otherwise -# the model(s) will get loaded in via the `Rails::Engine` +# This file only needs to be loaded if the gem is being used outside of Rails, +# 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" diff --git a/lib/paper_trail/frameworks/rails/controller.rb b/lib/paper_trail/frameworks/rails/controller.rb index a366f3c4..e1569f0c 100644 --- a/lib/paper_trail/frameworks/rails/controller.rb +++ b/lib/paper_trail/frameworks/rails/controller.rb @@ -36,24 +36,26 @@ module PaperTrail # # The columns `ip` and `user_agent` must exist in your `versions` # table. # - # Use the `:meta` option to `PaperTrail::Model::ClassMethods.has_paper_trail` - # to store any extra model-level data you need. + # Use the `:meta` option to + # `PaperTrail::Model::ClassMethods.has_paper_trail` to store any extra + # model-level data you need. def info_for_paper_trail {} end - # Returns `true` (default) or `false` depending on whether PaperTrail should - # be active for the current request. + # Returns `true` (default) or `false` depending on whether PaperTrail + # should be active for the current request. # - # Override this method in your controller to specify when PaperTrail should - # be off. + # Override this method in your controller to specify when PaperTrail + # should be off. def paper_trail_enabled_for_controller ::PaperTrail.enabled? end 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 ::PaperTrail.enabled_for_controller = paper_trail_enabled_for_controller end @@ -63,8 +65,8 @@ module PaperTrail ::PaperTrail.whodunnit = user_for_paper_trail if ::PaperTrail.enabled_for_controller? end - # Tells PaperTrail any information from the controller you want - # to store alongside any changes that occur. + # Tells PaperTrail any information from the controller you want to store + # alongside any changes that occur. def set_paper_trail_controller_info ::PaperTrail.controller_info = info_for_paper_trail if ::PaperTrail.enabled_for_controller? end diff --git a/lib/paper_trail/frameworks/sinatra.rb b/lib/paper_trail/frameworks/sinatra.rb index 10477951..e12bce4a 100644 --- a/lib/paper_trail/frameworks/sinatra.rb +++ b/lib/paper_trail/frameworks/sinatra.rb @@ -3,7 +3,8 @@ require 'active_support/core_ext/object' # provides the `try` method module PaperTrail 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) app.use RequestStore::Middleware app.helpers self diff --git a/lib/paper_trail/has_paper_trail.rb b/lib/paper_trail/has_paper_trail.rb index 89f2cda5..173ad6a6 100644 --- a/lib/paper_trail/has_paper_trail.rb +++ b/lib/paper_trail/has_paper_trail.rb @@ -8,31 +8,42 @@ module PaperTrail end module ClassMethods - # Declare this in your model to track every create, update, and destroy. Each version of - # the model is available in the `versions` association. + # Declare this in your model to track every create, update, and destroy. + # Each version of the model is available in the `versions` association. # # Options: - # :on the events to track (optional; defaults to all of them). Set to an array of - # `:create`, `:update`, `:destroy` as desired. - # :class_name the name of a custom Version class. This class should inherit from `PaperTrail::Version`. - # :ignore an array of attributes for which a new `Version` will not be created if only they change. - # it can also aceept a Hash as an argument where the key is the attribute to ignore (a `String` or `Symbol`), - # which will only be ignored if the value is a `Proc` which returns truthily. - # :if, :unless Procs that allow to specify conditions when to save versions for an object - # :only inverse of `ignore` - a new `Version` will be created only for these attributes if supplied - # it can also aceept a Hash as an argument where the key is the attribute to track (a `String` or `Symbol`), - # which will only be counted if the value is a `Proc` which returns truthily. - # :skip fields to ignore completely. As with `ignore`, updates to these fields will not create - # a new `Version`. In addition, these fields will not be included in the serialized versions - # of the object 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 + # + # - :on - The events to track (optional; defaults to all of them). Set + # to an array of `:create`, `:update`, `:destroy` as desired. + # - :class_name - The name of a custom Version class. This class should + # inherit from `PaperTrail::Version`. + # - :ignore - An array of attributes for which a new `Version` will not be + # created if only they change. It can also aceept a Hash as an + # argument where the key is the attribute to ignore (a `String` or + # `Symbol`), which will only be ignored if the value is a `Proc` which + # returns truthily. + # - :if, :unless - Procs that allow to specify conditions when to save + # versions for an object. + # - :only - Inverse of `ignore`. A new `Version` will be created only + # for these attributes if supplied it can also aceept a Hash as an + # argument where the key is the attribute to track (a `String` or + # `Symbol`), which will only be counted if the value is a `Proc` which + # returns truthily. + # - :skip - Fields to ignore completely. As with `ignore`, updates to + # these fields will not create a new `Version`. In addition, these + # fields will not be included in the serialized versions of the object + # 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 = {}) # Lazily include the instance methods so we don't clutter up @@ -64,7 +75,8 @@ module PaperTrail 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, lambda { order(model.timestamp_sort_order) }, :class_name => self.version_class_name, :as => :item @@ -76,7 +88,11 @@ module PaperTrail end 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) if options_on.include?(:update) before_save :reset_timestamp_attrs_for_update_if_needed!, :on => :update @@ -85,7 +101,7 @@ module PaperTrail end 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_rollback :reset_transaction_id after_rollback :clear_rolled_back_versions @@ -110,40 +126,47 @@ module PaperTrail @paper_trail_version_class ||= version_class_name.constantize end - # Used for Version#object attribute + # Used for `Version#object` attribute. 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? serialized_attributes.each do |key, coder| 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) attributes[key] = coder.dump(attributes[key]) 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) - # 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? serialized_attributes.each do |key, coder| 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) attributes[key] = coder.load(attributes[key]) end end end - # Used for Version#object_changes attribute + # Used for Version#object_changes attribute. 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? serialized_attributes.each do |key, coder| 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) old_value, new_value = changes[key] changes[key] = [coder.dump(old_value), @@ -152,12 +175,17 @@ module PaperTrail 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) - # 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? serialized_attributes.each do |key, coder| 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) old_value, new_value = changes[key] changes[key] = [coder.load(old_value), @@ -236,7 +264,8 @@ module PaperTrail self.class.paper_trail_on! if paper_trail_was_enabled 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 instance_eval { alias :old_new_record? :new_record? @@ -246,7 +275,8 @@ module PaperTrail instance_eval { alias :new_record? :old_new_record? } 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) raise ArgumentError, 'expected to receive a block' unless block_given? current_whodunnit = PaperTrail.whodunnit @@ -344,15 +374,19 @@ module PaperTrail _changes.to_hash 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! send("#{self.class.version_association_name}=", nil) end + # Invoked via callback when a user attempts to persist a reified + # `Version`. 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| - # 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}!") send("restore_#{column}!") else @@ -382,7 +416,7 @@ module PaperTrail end end - # saves associations if the join table for `VersionAssociation` exists + # Saves associations if the join table for `VersionAssociation` exists. def save_associations(version) return unless PaperTrail.config.track_associations? self.class.reflect_on_all_associations(:belongs_to).each do |assoc| @@ -424,8 +458,8 @@ module PaperTrail if v.respond_to?(:call) v.call(self) elsif v.is_a?(Symbol) && respond_to?(v) - # if it is an attribute that is changing in an existing object, - # be sure to grab the current version + # If it is an attribute that is changing in an existing object, + # be sure to grab the current version. if has_attribute?(v) && send("#{v}_changed?".to_sym) && data[:event] != 'create' send("#{v}_was".to_sym) else @@ -435,6 +469,7 @@ module PaperTrail v end end + # Second we merge any extra data from the controller (if available). data.merge(PaperTrail.controller_info || {}) end @@ -449,8 +484,8 @@ module PaperTrail end end - # returns hash of attributes (with appropriate attributes serialized), - # ommitting attributes to be skipped + # Returns hash of attributes (with appropriate attributes serialized), + # ommitting attributes to be skipped. def object_attrs_for_paper_trail(attributes_hash) attrs = attributes_hash.except(*self.paper_trail_options[:skip]) if PaperTrail.serialized_attributes? @@ -459,10 +494,9 @@ module PaperTrail attrs end - # This method determines whether it is appropriate to generate a new - # version instance. A timestamp-only update (e.g. only `updated_at` - # changed) is considered notable unless an ignored attribute was also - # changed. + # Determines whether it is appropriate to generate a new version + # instance. A timestamp-only update (e.g. only `updated_at` changed) is + # considered notable unless an ignored attribute was also changed. def changed_notably? if ignored_attr_has_changed? timestamps = timestamp_attributes_for_update_in_model.map(&:to_s) @@ -473,8 +507,8 @@ module PaperTrail end # An attributed is "ignored" if it is listed in the `:ignore` option - # and/or the `:skip` option. Returns true if an ignored attribute - # has changed. + # and/or the `:skip` option. Returns true if an ignored attribute has + # changed. def ignored_attr_has_changed? ignored = self.paper_trail_options[:ignore] + self.paper_trail_options[:skip] ignored.any? && (changed & ignored).any? @@ -482,7 +516,8 @@ module PaperTrail def notably_changed 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| obj.is_a?(Hash) && obj.each { |attr, condition| only << attr if condition.respond_to?(:call) && condition.call(self) } end @@ -491,7 +526,8 @@ module PaperTrail def changed_and_not_ignored 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| obj.is_a?(Hash) && obj.each { |attr, condition| ignore << attr if condition.respond_to?(:call) && condition.call(self) } end diff --git a/lib/paper_trail/version_concern.rb b/lib/paper_trail/version_concern.rb index 73a085e5..cb7b51e3 100644 --- a/lib/paper_trail/version_concern.rb +++ b/lib/paper_trail/version_concern.rb @@ -7,10 +7,10 @@ module PaperTrail included do belongs_to :item, :polymorphic => true - # Since the test suite has test coverage for this, we want to declare the - # association when the test suite is running. This makes it pass - # when DB is not initialized prior to test runs such as when we run on - # Travis CI (there won't be a db in `test/dummy/db/`) + # Since the test suite has test coverage for this, we want to declare + # the association when the test suite is running. This makes it pass when + # DB is not initialized prior to test runs such as when we run on Travis + # CI (there won't be a db in `test/dummy/db/`). if PaperTrail.config.track_associations? has_many :version_associations, :dependent => :destroy end @@ -47,8 +47,8 @@ module PaperTrail where 'event <> ?', 'create' end - # Expects `obj` to be an instance of `PaperTrail::Version` by default, but can accept a timestamp if - # `timestamp_arg` receives `true` + # Expects `obj` to be an instance of `PaperTrail::Version` by default, + # but can accept a timestamp if `timestamp_arg` receives `true` def subsequent(obj, timestamp_arg = false) if timestamp_arg != true && self.primary_key_is_int? 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) 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') [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? @@ -137,12 +138,14 @@ module PaperTrail true 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? [:json, :jsonb].include?(columns_hash['object'].type) 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? [:json, :jsonb].include?(columns_hash['object_changes'].try(:type)) end @@ -150,22 +153,31 @@ module PaperTrail # Restore the item from this version. # - # Optionally this can also restore all :has_one and :has_many (including has_many :through) associations as - # they were "at the time", if they are also being versioned by PaperTrail. + # Optionally this can also restore all :has_one and :has_many (including + # has_many :through) associations as they were "at the time", if they are + # also being versioned by PaperTrail. # # 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. - # Default is `false`. - # :mark_for_destruction set to `true` to mark the has_one/has_many associations that did not exist in the - # reified version for destruction, instead of remove them. Default is `false`. - # This option is handy for people who want to persist the reified version. - # :dup `false` default behavior - # `true` it always create a new object instance. It is 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 + # + # - :has_one + # - `true` - Also reify has_one associations. + # - `false - Default. + # - :has_many + # - `true` - Also reify has_many and has_many :through associations. + # - `false` - Default. + # - :mark_for_destruction + # - `true` - Mark the has_one/has_many associations that did not exist in + # the reified version for destruction, instead of removing them. + # - `false` - Default. Useful for persisting the reified version. + # - :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 = {}) return nil if object.nil? @@ -180,22 +192,22 @@ module PaperTrail attrs = self.class.object_col_is_json? ? object : PaperTrail.serializer.load(object) - # Normally a polymorphic belongs_to relationship allows us - # to get the object we belong to by calling, in this case, - # `item`. However this returns nil if `item` has been - # destroyed, and we need to be able to retrieve destroyed - # objects. + # Normally a polymorphic belongs_to relationship allows us to get the + # object we belong to by calling, in this case, `item`. However this + # returns nil if `item` has been destroyed, and we need to be able to + # retrieve destroyed objects. # - # In this situation we constantize the `item_type` to get hold of - # the class...except when the stored object's attributes - # include a `type` key. If this is the case, the object - # we belong to is using single table inheritance and the - # `item_type` will be the base class, not the actual subclass. - # If `type` is present but empty, the class is the base class. + # In this situation we constantize the `item_type` to get hold of the + # class...except when the stored object's attributes include a `type` + # key. If this is the case, the object we belong to is using single + # table inheritance and the `item_type` will be the base class, not the + # actual subclass. If `type` is present but empty, the class is the base + # class. if options[:dup] != true && 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 (model.attribute_names - attrs.keys).each { |k| attrs[k] = nil } end @@ -203,13 +215,14 @@ module PaperTrail inheritance_column_name = item_type.constantize.inheritance_column class_name = attrs[inheritance_column_name].blank? ? item_type : attrs[inheritance_column_name] klass = class_name.constantize - # the `dup` option always returns a new object, otherwise we should attempt - # to look for the item outside of default scope(s) + # The `dup` option always returns a new object, otherwise we should + # attempt to look for the item outside of default scope(s). if options[:dup] || (_item = klass.unscoped.find_by_id(item_id)).nil? model = klass.new elsif options[:unversioned_attributes] == :nil 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 } end end @@ -218,7 +231,7 @@ module PaperTrail model.class.unserialize_attributes_for_paper_trail! attrs 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| if model.has_attribute?(k) model[k.to_sym] = v @@ -243,8 +256,9 @@ module PaperTrail end end - # Returns what changed in this version of the item. `ActiveModel::Dirty#changes`. - # returns `nil` if your `versions` table does not have an `object_changes` text column. + # Returns what changed in this version of the item. + # `ActiveModel::Dirty#changes`. returns `nil` if your `versions` table does + # not have an `object_changes` text column. def changeset return nil unless self.class.column_names.include? 'object_changes' @@ -268,8 +282,8 @@ module PaperTrail self.paper_trail_originator end - # Returns who changed the item from the state it had in this version. - # This is an alias for `whodunnit`. + # Returns who changed the item from the state it had in this version. This + # is an alias for `whodunnit`. def terminator @terminator ||= whodunnit end @@ -311,9 +325,9 @@ module PaperTrail end end - # Restore the `model`'s has_one associations as they were when this version was - # superseded by the next (because that's what the user was looking at when they - # made the change). + # Restore the `model`'s has_one associations as they were when this + # version was superseded by the next (because that's what the user was + # looking at when they made the change). def reify_has_ones(model, options = {}) version_table_name = model.class.paper_trail_version_class.table_name model.class.reflect_on_all_associations(:has_one).each do |assoc| @@ -344,8 +358,9 @@ module PaperTrail end end - # Restore the `model`'s has_many associations as they were at version_at timestamp - # We lookup the first child versions after version_at timestamp or in same transaction. + # Restore the `model`'s has_many associations as they were at version_at + # timestamp We lookup the first child versions after version_at timestamp or + # in same transaction. def reify_has_manys(model, options = {}) assoc_has_many_through, assoc_has_many_directly = model.class.reflect_on_all_associations(:has_many). @@ -354,7 +369,8 @@ module PaperTrail reify_has_many_through(assoc_has_many_through, model, options) 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 = {}) version_table_name = model.class.paper_trail_version_class.table_name associations.each do |assoc| @@ -370,10 +386,11 @@ module PaperTrail acc.merge!(v.item_id => v) 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) - # 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| if (version = versions.delete(c.id)).nil? c @@ -384,16 +401,18 @@ module PaperTrail end end - # Reify the rest of the versions and add them to the collection, these versions are for those that - # have been removed from the live associations + # Reify the rest of the versions and add them to the collection, these + # 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)) } model.send(assoc.name).proxy_association.target = collection.compact end end - # 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) + # 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). def reify_has_many_through(associations, model, options = {}) associations.each do |assoc| 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) - # 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| if (version = versions.delete(c.id)).nil? c @@ -423,15 +443,17 @@ module PaperTrail end end - # Reify the rest of the versions and add them to the collection, these versions are for those that - # have been removed from the live associations + # Reify the rest of the versions and add them to the collection, these + # 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)) } model.send(assoc.name).proxy_association.target = collection.compact 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! return unless PaperTrail.config.version_limit.is_a? Numeric previous_versions = sibling_versions.not_creates