paper_trail 4.2.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +28 -9
 - data/.github/ISSUE_TEMPLATE.md +13 -0
 - data/.gitignore +2 -1
 - data/.rubocop.yml +100 -0
 - data/.rubocop_todo.yml +14 -0
 - data/.travis.yml +8 -9
 - data/Appraisals +41 -0
 - data/CHANGELOG.md +49 -9
 - data/Gemfile +1 -1
 - data/README.md +130 -109
 - data/Rakefile +19 -19
 - data/doc/bug_report_template.rb +20 -14
 - data/gemfiles/ar3.gemfile +10 -53
 - data/gemfiles/ar4.gemfile +7 -0
 - data/gemfiles/ar5.gemfile +13 -0
 - data/lib/generators/paper_trail/install_generator.rb +26 -18
 - data/lib/generators/paper_trail/templates/add_object_changes_to_versions.rb +4 -2
 - data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb +2 -0
 - data/lib/generators/paper_trail/templates/create_version_associations.rb +9 -4
 - data/lib/generators/paper_trail/templates/create_versions.rb +39 -5
 - data/lib/paper_trail.rb +169 -146
 - data/lib/paper_trail/attributes_serialization.rb +89 -17
 - data/lib/paper_trail/cleaner.rb +15 -9
 - data/lib/paper_trail/config.rb +28 -11
 - data/lib/paper_trail/frameworks/active_record.rb +4 -0
 - data/lib/paper_trail/frameworks/active_record/models/paper_trail/version.rb +5 -1
 - data/lib/paper_trail/frameworks/active_record/models/paper_trail/version_association.rb +6 -2
 - data/lib/paper_trail/frameworks/cucumber.rb +1 -0
 - data/lib/paper_trail/frameworks/rails.rb +2 -7
 - data/lib/paper_trail/frameworks/rails/controller.rb +29 -9
 - data/lib/paper_trail/frameworks/rails/engine.rb +7 -1
 - data/lib/paper_trail/frameworks/rspec.rb +5 -5
 - data/lib/paper_trail/frameworks/rspec/helpers.rb +3 -1
 - data/lib/paper_trail/frameworks/sinatra.rb +6 -4
 - data/lib/paper_trail/has_paper_trail.rb +199 -106
 - data/lib/paper_trail/record_history.rb +1 -3
 - data/lib/paper_trail/reifier.rb +297 -118
 - data/lib/paper_trail/serializers/json.rb +3 -3
 - data/lib/paper_trail/serializers/yaml.rb +27 -8
 - data/lib/paper_trail/version_association_concern.rb +3 -1
 - data/lib/paper_trail/version_concern.rb +75 -35
 - data/lib/paper_trail/version_number.rb +6 -9
 - data/paper_trail.gemspec +44 -51
 - data/spec/generators/install_generator_spec.rb +24 -25
 - data/spec/generators/paper_trail/templates/create_versions_spec.rb +51 -0
 - data/spec/models/animal_spec.rb +12 -12
 - data/spec/models/boolit_spec.rb +8 -8
 - data/spec/models/callback_modifier_spec.rb +47 -47
 - data/spec/models/car_spec.rb +13 -0
 - data/spec/models/fluxor_spec.rb +3 -3
 - data/spec/models/gadget_spec.rb +19 -19
 - data/spec/models/joined_version_spec.rb +3 -3
 - data/spec/models/json_version_spec.rb +23 -24
 - data/spec/models/kitchen/banana_spec.rb +3 -3
 - data/spec/models/not_on_update_spec.rb +7 -4
 - data/spec/models/post_with_status_spec.rb +13 -3
 - data/spec/models/skipper_spec.rb +10 -10
 - data/spec/models/thing_spec.rb +4 -4
 - data/spec/models/truck_spec.rb +5 -0
 - data/spec/models/vehicle_spec.rb +5 -0
 - data/spec/models/version_spec.rb +103 -59
 - data/spec/models/widget_spec.rb +82 -52
 - data/spec/modules/paper_trail_spec.rb +2 -2
 - data/spec/modules/version_concern_spec.rb +11 -12
 - data/spec/modules/version_number_spec.rb +2 -4
 - data/spec/paper_trail/config_spec.rb +10 -29
 - data/spec/paper_trail_spec.rb +16 -14
 - data/spec/rails_helper.rb +10 -9
 - data/spec/requests/articles_spec.rb +11 -7
 - data/spec/spec_helper.rb +41 -22
 - data/spec/support/alt_db_init.rb +8 -13
 - data/test/custom_json_serializer.rb +3 -3
 - data/test/dummy/Rakefile +2 -2
 - data/test/dummy/app/controllers/application_controller.rb +21 -8
 - data/test/dummy/app/controllers/articles_controller.rb +11 -8
 - data/test/dummy/app/controllers/widgets_controller.rb +13 -12
 - data/test/dummy/app/models/animal.rb +1 -1
 - data/test/dummy/app/models/article.rb +19 -11
 - data/test/dummy/app/models/authorship.rb +1 -1
 - data/test/dummy/app/models/bar_habtm.rb +4 -0
 - data/test/dummy/app/models/book.rb +4 -4
 - data/test/dummy/app/models/boolit.rb +1 -1
 - data/test/dummy/app/models/callback_modifier.rb +6 -6
 - data/test/dummy/app/models/car.rb +3 -0
 - data/test/dummy/app/models/chapter.rb +4 -4
 - data/test/dummy/app/models/customer.rb +1 -1
 - data/test/dummy/app/models/document.rb +2 -2
 - data/test/dummy/app/models/editor.rb +1 -1
 - data/test/dummy/app/models/foo_habtm.rb +4 -0
 - data/test/dummy/app/models/fruit.rb +2 -2
 - data/test/dummy/app/models/gadget.rb +1 -1
 - data/test/dummy/app/models/kitchen/banana.rb +1 -1
 - data/test/dummy/app/models/legacy_widget.rb +2 -2
 - data/test/dummy/app/models/line_item.rb +1 -1
 - data/test/dummy/app/models/not_on_update.rb +1 -1
 - data/test/dummy/app/models/person.rb +6 -6
 - data/test/dummy/app/models/post.rb +1 -1
 - data/test/dummy/app/models/post_with_status.rb +1 -1
 - data/test/dummy/app/models/quotation.rb +1 -1
 - data/test/dummy/app/models/section.rb +1 -1
 - data/test/dummy/app/models/skipper.rb +2 -2
 - data/test/dummy/app/models/song.rb +13 -4
 - data/test/dummy/app/models/thing.rb +2 -2
 - data/test/dummy/app/models/translation.rb +2 -2
 - data/test/dummy/app/models/truck.rb +4 -0
 - data/test/dummy/app/models/vehicle.rb +4 -0
 - data/test/dummy/app/models/whatchamajigger.rb +1 -1
 - data/test/dummy/app/models/widget.rb +7 -6
 - data/test/dummy/app/versions/joined_version.rb +4 -3
 - data/test/dummy/app/versions/json_version.rb +1 -1
 - data/test/dummy/app/versions/kitchen/banana_version.rb +1 -1
 - data/test/dummy/app/versions/post_version.rb +2 -2
 - data/test/dummy/config.ru +1 -1
 - data/test/dummy/config/application.rb +20 -9
 - data/test/dummy/config/boot.rb +5 -5
 - data/test/dummy/config/environment.rb +1 -1
 - data/test/dummy/config/environments/development.rb +4 -3
 - data/test/dummy/config/environments/production.rb +3 -2
 - data/test/dummy/config/environments/test.rb +15 -5
 - data/test/dummy/config/initializers/backtrace_silencers.rb +4 -2
 - data/test/dummy/config/initializers/paper_trail.rb +1 -2
 - data/test/dummy/config/initializers/secret_token.rb +3 -1
 - data/test/dummy/config/initializers/session_store.rb +1 -1
 - data/test/dummy/config/routes.rb +2 -2
 - data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +120 -74
 - data/test/dummy/db/schema.rb +29 -6
 - data/test/dummy/script/rails +6 -4
 - data/test/functional/controller_test.rb +34 -35
 - data/test/functional/enabled_for_controller_test.rb +6 -7
 - data/test/functional/modular_sinatra_test.rb +43 -38
 - data/test/functional/sinatra_test.rb +49 -40
 - data/test/functional/thread_safety_test.rb +4 -6
 - data/test/paper_trail_test.rb +15 -14
 - data/test/test_helper.rb +68 -44
 - data/test/time_travel_helper.rb +1 -15
 - data/test/unit/associations_test.rb +517 -251
 - data/test/unit/cleaner_test.rb +66 -60
 - data/test/unit/inheritance_column_test.rb +17 -17
 - data/test/unit/model_test.rb +611 -504
 - data/test/unit/protected_attrs_test.rb +16 -12
 - data/test/unit/serializer_test.rb +44 -43
 - data/test/unit/serializers/json_test.rb +17 -18
 - data/test/unit/serializers/mixin_json_test.rb +15 -14
 - data/test/unit/serializers/mixin_yaml_test.rb +20 -16
 - data/test/unit/serializers/yaml_test.rb +12 -13
 - data/test/unit/timestamp_test.rb +10 -12
 - data/test/unit/version_test.rb +7 -7
 - metadata +92 -40
 
| 
         @@ -1,8 +1,8 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require  
     | 
| 
      
 1 
     | 
    
         
            +
            require "active_support/core_ext/object" # provides the `try` method
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module PaperTrail
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Extensions to `Sinatra`.
         
     | 
| 
       4 
5 
     | 
    
         
             
              module Sinatra
         
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
6 
     | 
    
         
             
                # Register this module inside your Sinatra application to gain access to
         
     | 
| 
       7 
7 
     | 
    
         
             
                # controller-level methods used by PaperTrail.
         
     | 
| 
       8 
8 
     | 
    
         
             
                def self.registered(app)
         
     | 
| 
         @@ -29,10 +29,12 @@ module PaperTrail 
     | 
|
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
                # Tells PaperTrail who is responsible for any changes that occur.
         
     | 
| 
       31 
31 
     | 
    
         
             
                def set_paper_trail_whodunnit
         
     | 
| 
      
 32 
     | 
    
         
            +
                  @set_paper_trail_whodunnit_called = true
         
     | 
| 
       32 
33 
     | 
    
         
             
                  ::PaperTrail.whodunnit = user_for_paper_trail if ::PaperTrail.enabled?
         
     | 
| 
       33 
34 
     | 
    
         
             
                end
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
35 
     | 
    
         
             
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
| 
       36 
37 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
      
 38 
     | 
    
         
            +
            if defined?(::Sinatra)
         
     | 
| 
      
 39 
     | 
    
         
            +
              ::Sinatra.register(::PaperTrail::Sinatra)
         
     | 
| 
       38 
40 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,13 +1,15 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require  
     | 
| 
       2 
     | 
    
         
            -
            require  
     | 
| 
      
 1 
     | 
    
         
            +
            require "active_support/core_ext/object" # provides the `try` method
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "paper_trail/attributes_serialization"
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            module PaperTrail
         
     | 
| 
      
 5 
     | 
    
         
            +
              # Extensions to `ActiveRecord::Base`.  See `frameworks/active_record.rb`.
         
     | 
| 
       5 
6 
     | 
    
         
             
              module Model
         
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
7 
     | 
    
         
             
                def self.included(base)
         
     | 
| 
       8 
8 
     | 
    
         
             
                  base.send :extend, ClassMethods
         
     | 
| 
      
 9 
     | 
    
         
            +
                  base.send :attr_accessor, :paper_trail_habtm
         
     | 
| 
       9 
10 
     | 
    
         
             
                end
         
     | 
| 
       10 
11 
     | 
    
         | 
| 
      
 12 
     | 
    
         
            +
                # :nodoc:
         
     | 
| 
       11 
13 
     | 
    
         
             
                module ClassMethods
         
     | 
| 
       12 
14 
     | 
    
         
             
                  # Declare this in your model to track every create, update, and destroy.
         
     | 
| 
       13 
15 
     | 
    
         
             
                  # Each version of the model is available in the `versions` association.
         
     | 
| 
         @@ -45,7 +47,14 @@ module PaperTrail 
     | 
|
| 
       45 
47 
     | 
    
         
             
                  #   the instance was reified from. Default is `:version`.
         
     | 
| 
       46 
48 
     | 
    
         
             
                  # - :save_changes - Whether or not to save changes to the object_changes
         
     | 
| 
       47 
49 
     | 
    
         
             
                  #   column if it exists. Default is true
         
     | 
| 
      
 50 
     | 
    
         
            +
                  # - :join_tables - If the model has a has_and_belongs_to_many relation
         
     | 
| 
      
 51 
     | 
    
         
            +
                  #   with an unpapertrailed model, passing the name of the association to
         
     | 
| 
      
 52 
     | 
    
         
            +
                  #   the join_tables option will paper trail the join table but not save
         
     | 
| 
      
 53 
     | 
    
         
            +
                  #   the other model, allowing reification of the association but with the
         
     | 
| 
      
 54 
     | 
    
         
            +
                  #   other models latest state (if the other model is paper trailed, this
         
     | 
| 
      
 55 
     | 
    
         
            +
                  #   option does nothing)
         
     | 
| 
       48 
56 
     | 
    
         
             
                  #
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # @api public
         
     | 
| 
       49 
58 
     | 
    
         
             
                  def has_paper_trail(options = {})
         
     | 
| 
       50 
59 
     | 
    
         
             
                    options[:on] ||= [:create, :update, :destroy]
         
     | 
| 
       51 
60 
     | 
    
         | 
| 
         @@ -56,8 +65,47 @@ module PaperTrail 
     | 
|
| 
       56 
65 
     | 
    
         
             
                    setup_model_for_paper_trail(options)
         
     | 
| 
       57 
66 
     | 
    
         | 
| 
       58 
67 
     | 
    
         
             
                    setup_callbacks_from_options options[:on]
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                    setup_callbacks_for_habtm options[:join_tables]
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  def update_for_callback(name, callback, model, assoc)
         
     | 
| 
      
 73 
     | 
    
         
            +
                    model.paper_trail_habtm ||= {}
         
     | 
| 
      
 74 
     | 
    
         
            +
                    model.paper_trail_habtm.reverse_merge!(name => { removed: [], added: [] })
         
     | 
| 
      
 75 
     | 
    
         
            +
                    case callback
         
     | 
| 
      
 76 
     | 
    
         
            +
                    when :before_add
         
     | 
| 
      
 77 
     | 
    
         
            +
                      model.paper_trail_habtm[name][:added] |= [assoc.id]
         
     | 
| 
      
 78 
     | 
    
         
            +
                      model.paper_trail_habtm[name][:removed] -= [assoc.id]
         
     | 
| 
      
 79 
     | 
    
         
            +
                    when :before_remove
         
     | 
| 
      
 80 
     | 
    
         
            +
                      model.paper_trail_habtm[name][:removed] |= [assoc.id]
         
     | 
| 
      
 81 
     | 
    
         
            +
                      model.paper_trail_habtm[name][:added] -= [assoc.id]
         
     | 
| 
      
 82 
     | 
    
         
            +
                    end
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  attr_reader :paper_trail_save_join_tables
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                  def setup_callbacks_for_habtm(join_tables)
         
     | 
| 
      
 88 
     | 
    
         
            +
                    @paper_trail_save_join_tables = Array.wrap(join_tables)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    # Adds callbacks to record changes to habtm associations such that on
         
     | 
| 
      
 90 
     | 
    
         
            +
                    # save the previous version of the association (if changed) can be
         
     | 
| 
      
 91 
     | 
    
         
            +
                    # interpreted
         
     | 
| 
      
 92 
     | 
    
         
            +
                    reflect_on_all_associations(:has_and_belongs_to_many).
         
     | 
| 
      
 93 
     | 
    
         
            +
                      reject { |a| paper_trail_options[:skip].include?(a.name.to_s) }.
         
     | 
| 
      
 94 
     | 
    
         
            +
                      each do |a|
         
     | 
| 
      
 95 
     | 
    
         
            +
                        added_callback = lambda do |*args|
         
     | 
| 
      
 96 
     | 
    
         
            +
                          update_for_callback(a.name, :before_add, args[-2], args.last)
         
     | 
| 
      
 97 
     | 
    
         
            +
                        end
         
     | 
| 
      
 98 
     | 
    
         
            +
                        removed_callback = lambda do |*args|
         
     | 
| 
      
 99 
     | 
    
         
            +
                          update_for_callback(a.name, :before_remove, args[-2], args.last)
         
     | 
| 
      
 100 
     | 
    
         
            +
                        end
         
     | 
| 
      
 101 
     | 
    
         
            +
                        send(:"before_add_for_#{a.name}").send(:<<, added_callback)
         
     | 
| 
      
 102 
     | 
    
         
            +
                        send(:"before_remove_for_#{a.name}").send(:<<, removed_callback)
         
     | 
| 
      
 103 
     | 
    
         
            +
                      end
         
     | 
| 
       59 
104 
     | 
    
         
             
                  end
         
     | 
| 
       60 
105 
     | 
    
         | 
| 
      
 106 
     | 
    
         
            +
                  # Installs callbacks, associations, "class attributes", and more.
         
     | 
| 
      
 107 
     | 
    
         
            +
                  # For details of how "class attributes" work, see the activesupport docs.
         
     | 
| 
      
 108 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       61 
109 
     | 
    
         
             
                  def setup_model_for_paper_trail(options = {})
         
     | 
| 
       62 
110 
     | 
    
         
             
                    # Lazily include the instance methods so we don't clutter up
         
     | 
| 
       63 
111 
     | 
    
         
             
                    # any more ActiveRecord models than we have to.
         
     | 
| 
         @@ -68,22 +116,12 @@ module PaperTrail 
     | 
|
| 
       68 
116 
     | 
    
         
             
                    self.version_association_name = options[:version] || :version
         
     | 
| 
       69 
117 
     | 
    
         | 
| 
       70 
118 
     | 
    
         
             
                    # The version this instance was reified from.
         
     | 
| 
       71 
     | 
    
         
            -
                    attr_accessor  
     | 
| 
      
 119 
     | 
    
         
            +
                    attr_accessor version_association_name
         
     | 
| 
       72 
120 
     | 
    
         | 
| 
       73 
121 
     | 
    
         
             
                    class_attribute :version_class_name
         
     | 
| 
       74 
     | 
    
         
            -
                    self.version_class_name = options[:class_name] ||  
     | 
| 
      
 122 
     | 
    
         
            +
                    self.version_class_name = options[:class_name] || "PaperTrail::Version"
         
     | 
| 
       75 
123 
     | 
    
         | 
| 
       76 
     | 
    
         
            -
                     
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
                    self.paper_trail_options = options.dup
         
     | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
       80 
     | 
    
         
            -
                    [:ignore, :skip, :only].each do |k|
         
     | 
| 
       81 
     | 
    
         
            -
                      paper_trail_options[k] =
         
     | 
| 
       82 
     | 
    
         
            -
                        [paper_trail_options[k]].flatten.compact.map { |attr| attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s }
         
     | 
| 
       83 
     | 
    
         
            -
                    end
         
     | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
       85 
     | 
    
         
            -
                    paper_trail_options[:meta] ||= {}
         
     | 
| 
       86 
     | 
    
         
            -
                    paper_trail_options[:save_changes] = true if paper_trail_options[:save_changes].nil?
         
     | 
| 
      
 124 
     | 
    
         
            +
                    setup_paper_trail_options(options)
         
     | 
| 
       87 
125 
     | 
    
         | 
| 
       88 
126 
     | 
    
         
             
                    class_attribute :versions_association_name
         
     | 
| 
       89 
127 
     | 
    
         
             
                    self.versions_association_name = options[:versions] || :versions
         
     | 
| 
         @@ -92,14 +130,14 @@ module PaperTrail 
     | 
|
| 
       92 
130 
     | 
    
         | 
| 
       93 
131 
     | 
    
         
             
                    # `has_many` syntax for specifying order uses a lambda in Rails 4
         
     | 
| 
       94 
132 
     | 
    
         
             
                    if ::ActiveRecord::VERSION::MAJOR >= 4
         
     | 
| 
       95 
     | 
    
         
            -
                      has_many  
     | 
| 
       96 
     | 
    
         
            -
                         
     | 
| 
       97 
     | 
    
         
            -
                        : 
     | 
| 
      
 133 
     | 
    
         
            +
                      has_many versions_association_name,
         
     | 
| 
      
 134 
     | 
    
         
            +
                        -> { order(model.timestamp_sort_order) },
         
     | 
| 
      
 135 
     | 
    
         
            +
                        class_name: version_class_name, as: :item
         
     | 
| 
       98 
136 
     | 
    
         
             
                    else
         
     | 
| 
       99 
     | 
    
         
            -
                      has_many  
     | 
| 
       100 
     | 
    
         
            -
                        : 
     | 
| 
       101 
     | 
    
         
            -
                        : 
     | 
| 
       102 
     | 
    
         
            -
                        : 
     | 
| 
      
 137 
     | 
    
         
            +
                      has_many versions_association_name,
         
     | 
| 
      
 138 
     | 
    
         
            +
                        class_name: version_class_name,
         
     | 
| 
      
 139 
     | 
    
         
            +
                        as: :item,
         
     | 
| 
      
 140 
     | 
    
         
            +
                        order: paper_trail_version_class.timestamp_sort_order
         
     | 
| 
       103 
141 
     | 
    
         
             
                    end
         
     | 
| 
       104 
142 
     | 
    
         | 
| 
       105 
143 
     | 
    
         
             
                    # Reset the transaction id when the transaction is closed.
         
     | 
| 
         @@ -108,6 +146,22 @@ module PaperTrail 
     | 
|
| 
       108 
146 
     | 
    
         
             
                    after_rollback :clear_rolled_back_versions
         
     | 
| 
       109 
147 
     | 
    
         
             
                  end
         
     | 
| 
       110 
148 
     | 
    
         | 
| 
      
 149 
     | 
    
         
            +
                  # Given `options`, populates `paper_trail_options`.
         
     | 
| 
      
 150 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 151 
     | 
    
         
            +
                  def setup_paper_trail_options(options)
         
     | 
| 
      
 152 
     | 
    
         
            +
                    class_attribute :paper_trail_options
         
     | 
| 
      
 153 
     | 
    
         
            +
                    self.paper_trail_options = options.dup
         
     | 
| 
      
 154 
     | 
    
         
            +
                    [:ignore, :skip, :only].each do |k|
         
     | 
| 
      
 155 
     | 
    
         
            +
                      paper_trail_options[k] = [paper_trail_options[k]].flatten.compact.map { |attr|
         
     | 
| 
      
 156 
     | 
    
         
            +
                        attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s
         
     | 
| 
      
 157 
     | 
    
         
            +
                      }
         
     | 
| 
      
 158 
     | 
    
         
            +
                    end
         
     | 
| 
      
 159 
     | 
    
         
            +
                    paper_trail_options[:meta] ||= {}
         
     | 
| 
      
 160 
     | 
    
         
            +
                    if paper_trail_options[:save_changes].nil?
         
     | 
| 
      
 161 
     | 
    
         
            +
                      paper_trail_options[:save_changes] = true
         
     | 
| 
      
 162 
     | 
    
         
            +
                    end
         
     | 
| 
      
 163 
     | 
    
         
            +
                  end
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
       111 
165 
     | 
    
         
             
                  def setup_callbacks_from_options(options_on = [])
         
     | 
| 
       112 
166 
     | 
    
         
             
                    options_on.each do |option|
         
     | 
| 
       113 
167 
     | 
    
         
             
                      send "paper_trail_on_#{option}"
         
     | 
| 
         @@ -115,13 +169,13 @@ module PaperTrail 
     | 
|
| 
       115 
169 
     | 
    
         
             
                  end
         
     | 
| 
       116 
170 
     | 
    
         | 
| 
       117 
171 
     | 
    
         
             
                  # Record version before or after "destroy" event
         
     | 
| 
       118 
     | 
    
         
            -
                  def paper_trail_on_destroy(recording_order =  
     | 
| 
       119 
     | 
    
         
            -
                    unless %w 
     | 
| 
       120 
     | 
    
         
            -
                       
     | 
| 
      
 172 
     | 
    
         
            +
                  def paper_trail_on_destroy(recording_order = "before")
         
     | 
| 
      
 173 
     | 
    
         
            +
                    unless %w(after before).include?(recording_order.to_s)
         
     | 
| 
      
 174 
     | 
    
         
            +
                      raise ArgumentError, 'recording order can only be "after" or "before"'
         
     | 
| 
       121 
175 
     | 
    
         
             
                    end
         
     | 
| 
       122 
176 
     | 
    
         | 
| 
       123 
     | 
    
         
            -
                    if recording_order 
     | 
| 
       124 
     | 
    
         
            -
             
     | 
| 
      
 177 
     | 
    
         
            +
                    if recording_order == "after" &&
         
     | 
| 
      
 178 
     | 
    
         
            +
                        Gem::Version.new(ActiveRecord::VERSION::STRING) >= Gem::Version.new("5")
         
     | 
| 
       125 
179 
     | 
    
         
             
                      if ::ActiveRecord::Base.belongs_to_required_by_default
         
     | 
| 
       126 
180 
     | 
    
         
             
                        ::ActiveSupport::Deprecation.warn(
         
     | 
| 
       127 
181 
     | 
    
         
             
                          "paper_trail_on_destroy(:after) is incompatible with ActiveRecord " +
         
     | 
| 
         @@ -131,7 +185,7 @@ module PaperTrail 
     | 
|
| 
       131 
185 
     | 
    
         
             
                      end
         
     | 
| 
       132 
186 
     | 
    
         
             
                    end
         
     | 
| 
       133 
187 
     | 
    
         | 
| 
       134 
     | 
    
         
            -
                    send "#{recording_order}_destroy", :record_destroy, : 
     | 
| 
      
 188 
     | 
    
         
            +
                    send "#{recording_order}_destroy", :record_destroy, if: :save_version?
         
     | 
| 
       135 
189 
     | 
    
         | 
| 
       136 
190 
     | 
    
         
             
                    return if paper_trail_options[:on].include?(:destroy)
         
     | 
| 
       137 
191 
     | 
    
         
             
                    paper_trail_options[:on] << :destroy
         
     | 
| 
         @@ -139,10 +193,8 @@ module PaperTrail 
     | 
|
| 
       139 
193 
     | 
    
         | 
| 
       140 
194 
     | 
    
         
             
                  # Record version after "update" event
         
     | 
| 
       141 
195 
     | 
    
         
             
                  def paper_trail_on_update
         
     | 
| 
       142 
     | 
    
         
            -
                    before_save 
     | 
| 
       143 
     | 
    
         
            -
             
     | 
| 
       144 
     | 
    
         
            -
                    after_update :record_update,
         
     | 
| 
       145 
     | 
    
         
            -
                                 :if => :save_version?
         
     | 
| 
      
 196 
     | 
    
         
            +
                    before_save :reset_timestamp_attrs_for_update_if_needed!, on: :update
         
     | 
| 
      
 197 
     | 
    
         
            +
                    after_update :record_update, if: :save_version?
         
     | 
| 
       146 
198 
     | 
    
         
             
                    after_update :clear_version_instance!
         
     | 
| 
       147 
199 
     | 
    
         | 
| 
       148 
200 
     | 
    
         
             
                    return if paper_trail_options[:on].include?(:update)
         
     | 
| 
         @@ -151,8 +203,7 @@ module PaperTrail 
     | 
|
| 
       151 
203 
     | 
    
         | 
| 
       152 
204 
     | 
    
         
             
                  # Record version after "create" event
         
     | 
| 
       153 
205 
     | 
    
         
             
                  def paper_trail_on_create
         
     | 
| 
       154 
     | 
    
         
            -
                    after_create :record_create,
         
     | 
| 
       155 
     | 
    
         
            -
                                 :if => :save_version?
         
     | 
| 
      
 206 
     | 
    
         
            +
                    after_create :record_create, if: :save_version?
         
     | 
| 
       156 
207 
     | 
    
         | 
| 
       157 
208 
     | 
    
         
             
                    return if paper_trail_options[:on].include?(:create)
         
     | 
| 
       158 
209 
     | 
    
         
             
                    paper_trail_options[:on] << :create
         
     | 
| 
         @@ -169,7 +220,7 @@ module PaperTrail 
     | 
|
| 
       169 
220 
     | 
    
         
             
                  end
         
     | 
| 
       170 
221 
     | 
    
         | 
| 
       171 
222 
     | 
    
         
             
                  def paper_trail_enabled_for_model?
         
     | 
| 
       172 
     | 
    
         
            -
                    return false unless  
     | 
| 
      
 223 
     | 
    
         
            +
                    return false unless include?(PaperTrail::Model::InstanceMethods)
         
     | 
| 
       173 
224 
     | 
    
         
             
                    PaperTrail.enabled_for_model?(self)
         
     | 
| 
       174 
225 
     | 
    
         
             
                  end
         
     | 
| 
       175 
226 
     | 
    
         | 
| 
         @@ -194,34 +245,43 @@ module PaperTrail 
     | 
|
| 
       194 
245 
     | 
    
         | 
| 
       195 
246 
     | 
    
         
             
                  def originator
         
     | 
| 
       196 
247 
     | 
    
         
             
                    ::ActiveSupport::Deprecation.warn "Use paper_trail_originator instead of originator."
         
     | 
| 
       197 
     | 
    
         
            -
                     
     | 
| 
      
 248 
     | 
    
         
            +
                    paper_trail_originator
         
     | 
| 
       198 
249 
     | 
    
         
             
                  end
         
     | 
| 
       199 
250 
     | 
    
         | 
| 
       200 
251 
     | 
    
         
             
                  # Invoked after rollbacks to ensure versions records are not created
         
     | 
| 
       201 
     | 
    
         
            -
                  # for changes that never actually took place
         
     | 
| 
      
 252 
     | 
    
         
            +
                  # for changes that never actually took place.
         
     | 
| 
      
 253 
     | 
    
         
            +
                  # Optimization: Use lazy `reset` instead of eager `reload` because, in
         
     | 
| 
      
 254 
     | 
    
         
            +
                  # many use cases, the association will not be used.
         
     | 
| 
       202 
255 
     | 
    
         
             
                  def clear_rolled_back_versions
         
     | 
| 
       203 
     | 
    
         
            -
                    send(self.class.versions_association_name). 
     | 
| 
      
 256 
     | 
    
         
            +
                    send(self.class.versions_association_name).reset
         
     | 
| 
       204 
257 
     | 
    
         
             
                  end
         
     | 
| 
       205 
258 
     | 
    
         | 
| 
       206 
259 
     | 
    
         
             
                  # Returns the object (not a Version) as it was at the given timestamp.
         
     | 
| 
       207 
     | 
    
         
            -
                  def version_at(timestamp, reify_options={})
         
     | 
| 
      
 260 
     | 
    
         
            +
                  def version_at(timestamp, reify_options = {})
         
     | 
| 
       208 
261 
     | 
    
         
             
                    # Because a version stores how its object looked *before* the change,
         
     | 
| 
       209 
262 
     | 
    
         
             
                    # we need to look for the first version created *after* the timestamp.
         
     | 
| 
       210 
263 
     | 
    
         
             
                    v = send(self.class.versions_association_name).subsequent(timestamp, true).first
         
     | 
| 
       211 
264 
     | 
    
         
             
                    return v.reify(reify_options) if v
         
     | 
| 
       212 
     | 
    
         
            -
                    self unless  
     | 
| 
      
 265 
     | 
    
         
            +
                    self unless destroyed?
         
     | 
| 
       213 
266 
     | 
    
         
             
                  end
         
     | 
| 
       214 
267 
     | 
    
         | 
| 
       215 
268 
     | 
    
         
             
                  # Returns the objects (not Versions) as they were between the given times.
         
     | 
| 
       216 
     | 
    
         
            -
                   
     | 
| 
      
 269 
     | 
    
         
            +
                  # TODO: Either add support for the third argument, `_reify_options`, or
         
     | 
| 
      
 270 
     | 
    
         
            +
                  # add a deprecation warning if someone tries to use it.
         
     | 
| 
      
 271 
     | 
    
         
            +
                  def versions_between(start_time, end_time, _reify_options = {})
         
     | 
| 
       217 
272 
     | 
    
         
             
                    versions = send(self.class.versions_association_name).between(start_time, end_time)
         
     | 
| 
       218 
     | 
    
         
            -
                    versions.collect { |version| version_at(version.send 
     | 
| 
      
 273 
     | 
    
         
            +
                    versions.collect { |version| version_at(version.send(PaperTrail.timestamp_field)) }
         
     | 
| 
       219 
274 
     | 
    
         
             
                  end
         
     | 
| 
       220 
275 
     | 
    
         | 
| 
       221 
276 
     | 
    
         
             
                  # Returns the object (not a Version) as it was most recently.
         
     | 
| 
       222 
277 
     | 
    
         
             
                  def previous_version
         
     | 
| 
       223 
     | 
    
         
            -
                     
     | 
| 
       224 
     | 
    
         
            -
             
     | 
| 
      
 278 
     | 
    
         
            +
                    previous =
         
     | 
| 
      
 279 
     | 
    
         
            +
                      if source_version
         
     | 
| 
      
 280 
     | 
    
         
            +
                        source_version.previous
         
     | 
| 
      
 281 
     | 
    
         
            +
                      else
         
     | 
| 
      
 282 
     | 
    
         
            +
                        send(self.class.versions_association_name).last
         
     | 
| 
      
 283 
     | 
    
         
            +
                      end
         
     | 
| 
      
 284 
     | 
    
         
            +
                    previous.try(:reify)
         
     | 
| 
       225 
285 
     | 
    
         
             
                  end
         
     | 
| 
       226 
286 
     | 
    
         | 
| 
       227 
287 
     | 
    
         
             
                  # Returns the object (not a Version) as it became next.
         
     | 
| 
         @@ -229,7 +289,7 @@ module PaperTrail 
     | 
|
| 
       229 
289 
     | 
    
         
             
                  #  "live" item, we return nil.  Perhaps we should return self instead?
         
     | 
| 
       230 
290 
     | 
    
         
             
                  def next_version
         
     | 
| 
       231 
291 
     | 
    
         
             
                    subsequent_version = source_version.next
         
     | 
| 
       232 
     | 
    
         
            -
                    subsequent_version ? subsequent_version.reify : self.class.find( 
     | 
| 
      
 292 
     | 
    
         
            +
                    subsequent_version ? subsequent_version.reify : self.class.find(id)
         
     | 
| 
       233 
293 
     | 
    
         
             
                  rescue
         
     | 
| 
       234 
294 
     | 
    
         
             
                    nil
         
     | 
| 
       235 
295 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -240,7 +300,7 @@ module PaperTrail 
     | 
|
| 
       240 
300 
     | 
    
         | 
| 
       241 
301 
     | 
    
         
             
                  # Executes the given method or block without creating a new version.
         
     | 
| 
       242 
302 
     | 
    
         
             
                  def without_versioning(method = nil)
         
     | 
| 
       243 
     | 
    
         
            -
                    paper_trail_was_enabled =  
     | 
| 
      
 303 
     | 
    
         
            +
                    paper_trail_was_enabled = paper_trail_enabled_for_model?
         
     | 
| 
       244 
304 
     | 
    
         
             
                    self.class.paper_trail_off!
         
     | 
| 
       245 
305 
     | 
    
         
             
                    method ? method.to_proc.call(self) : yield(self)
         
     | 
| 
       246 
306 
     | 
    
         
             
                  ensure
         
     | 
| 
         @@ -249,6 +309,7 @@ module PaperTrail 
     | 
|
| 
       249 
309 
     | 
    
         | 
| 
       250 
310 
     | 
    
         
             
                  # Utility method for reifying. Anything executed inside the block will
         
     | 
| 
       251 
311 
     | 
    
         
             
                  # appear like a new record.
         
     | 
| 
      
 312 
     | 
    
         
            +
                  # rubocop: disable Style/Alias
         
     | 
| 
       252 
313 
     | 
    
         
             
                  def appear_as_new_record
         
     | 
| 
       253 
314 
     | 
    
         
             
                    instance_eval {
         
     | 
| 
       254 
315 
     | 
    
         
             
                      alias :old_new_record? :new_record?
         
     | 
| 
         @@ -257,11 +318,12 @@ module PaperTrail 
     | 
|
| 
       257 
318 
     | 
    
         
             
                    yield
         
     | 
| 
       258 
319 
     | 
    
         
             
                    instance_eval { alias :new_record? :old_new_record? }
         
     | 
| 
       259 
320 
     | 
    
         
             
                  end
         
     | 
| 
      
 321 
     | 
    
         
            +
                  # rubocop: enable Style/Alias
         
     | 
| 
       260 
322 
     | 
    
         | 
| 
       261 
323 
     | 
    
         
             
                  # Temporarily overwrites the value of whodunnit and then executes the
         
     | 
| 
       262 
324 
     | 
    
         
             
                  # provided block.
         
     | 
| 
       263 
325 
     | 
    
         
             
                  def whodunnit(value)
         
     | 
| 
       264 
     | 
    
         
            -
                    raise ArgumentError,  
     | 
| 
      
 326 
     | 
    
         
            +
                    raise ArgumentError, "expected to receive a block" unless block_given?
         
     | 
| 
       265 
327 
     | 
    
         
             
                    current_whodunnit = PaperTrail.whodunnit
         
     | 
| 
       266 
328 
     | 
    
         
             
                    PaperTrail.whodunnit = value
         
     | 
| 
       267 
329 
     | 
    
         
             
                    yield self
         
     | 
| 
         @@ -287,7 +349,7 @@ module PaperTrail 
     | 
|
| 
       287 
349 
     | 
    
         
             
                    attributes.each { |column| write_attribute(column, current_time) }
         
     | 
| 
       288 
350 
     | 
    
         | 
| 
       289 
351 
     | 
    
         
             
                    record_update(true) unless will_record_after_update?
         
     | 
| 
       290 
     | 
    
         
            -
                    save!(: 
     | 
| 
      
 352 
     | 
    
         
            +
                    save!(validate: false)
         
     | 
| 
       291 
353 
     | 
    
         
             
                  end
         
     | 
| 
       292 
354 
     | 
    
         | 
| 
       293 
355 
     | 
    
         
             
                  private
         
     | 
| 
         @@ -306,8 +368,8 @@ module PaperTrail 
     | 
|
| 
       306 
368 
     | 
    
         
             
                  def record_create
         
     | 
| 
       307 
369 
     | 
    
         
             
                    if paper_trail_switched_on?
         
     | 
| 
       308 
370 
     | 
    
         
             
                      data = {
         
     | 
| 
       309 
     | 
    
         
            -
                        : 
     | 
| 
       310 
     | 
    
         
            -
                        : 
     | 
| 
      
 371 
     | 
    
         
            +
                        event: paper_trail_event || "create",
         
     | 
| 
      
 372 
     | 
    
         
            +
                        whodunnit: PaperTrail.whodunnit
         
     | 
| 
       311 
373 
     | 
    
         
             
                      }
         
     | 
| 
       312 
374 
     | 
    
         
             
                      if respond_to?(:updated_at)
         
     | 
| 
       313 
375 
     | 
    
         
             
                        data[PaperTrail.timestamp_field] = updated_at
         
     | 
| 
         @@ -315,11 +377,11 @@ module PaperTrail 
     | 
|
| 
       315 
377 
     | 
    
         
             
                      if pt_record_object_changes? && changed_notably?
         
     | 
| 
       316 
378 
     | 
    
         
             
                        data[:object_changes] = pt_recordable_object_changes
         
     | 
| 
       317 
379 
     | 
    
         
             
                      end
         
     | 
| 
       318 
     | 
    
         
            -
                      if self.class.paper_trail_version_class.column_names.include?( 
     | 
| 
      
 380 
     | 
    
         
            +
                      if self.class.paper_trail_version_class.column_names.include?("transaction_id")
         
     | 
| 
       319 
381 
     | 
    
         
             
                        data[:transaction_id] = PaperTrail.transaction_id
         
     | 
| 
       320 
382 
     | 
    
         
             
                      end
         
     | 
| 
       321 
383 
     | 
    
         
             
                      version = send(self.class.versions_association_name).create! merge_metadata(data)
         
     | 
| 
       322 
     | 
    
         
            -
                       
     | 
| 
      
 384 
     | 
    
         
            +
                      update_transaction_id(version)
         
     | 
| 
       323 
385 
     | 
    
         
             
                      save_associations(version)
         
     | 
| 
       324 
386 
     | 
    
         
             
                    end
         
     | 
| 
       325 
387 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -327,9 +389,9 @@ module PaperTrail 
     | 
|
| 
       327 
389 
     | 
    
         
             
                  def record_update(force = nil)
         
     | 
| 
       328 
390 
     | 
    
         
             
                    if paper_trail_switched_on? && (force || changed_notably?)
         
     | 
| 
       329 
391 
     | 
    
         
             
                      data = {
         
     | 
| 
       330 
     | 
    
         
            -
                        : 
     | 
| 
       331 
     | 
    
         
            -
                        : 
     | 
| 
       332 
     | 
    
         
            -
                        : 
     | 
| 
      
 392 
     | 
    
         
            +
                        event: paper_trail_event || "update",
         
     | 
| 
      
 393 
     | 
    
         
            +
                        object: pt_recordable_object,
         
     | 
| 
      
 394 
     | 
    
         
            +
                        whodunnit: PaperTrail.whodunnit
         
     | 
| 
       333 
395 
     | 
    
         
             
                      }
         
     | 
| 
       334 
396 
     | 
    
         
             
                      if respond_to?(:updated_at)
         
     | 
| 
       335 
397 
     | 
    
         
             
                        data[PaperTrail.timestamp_field] = updated_at
         
     | 
| 
         @@ -337,11 +399,11 @@ module PaperTrail 
     | 
|
| 
       337 
399 
     | 
    
         
             
                      if pt_record_object_changes?
         
     | 
| 
       338 
400 
     | 
    
         
             
                        data[:object_changes] = pt_recordable_object_changes
         
     | 
| 
       339 
401 
     | 
    
         
             
                      end
         
     | 
| 
       340 
     | 
    
         
            -
                      if self.class.paper_trail_version_class.column_names.include?( 
     | 
| 
      
 402 
     | 
    
         
            +
                      if self.class.paper_trail_version_class.column_names.include?("transaction_id")
         
     | 
| 
       341 
403 
     | 
    
         
             
                        data[:transaction_id] = PaperTrail.transaction_id
         
     | 
| 
       342 
404 
     | 
    
         
             
                      end
         
     | 
| 
       343 
405 
     | 
    
         
             
                      version = send(self.class.versions_association_name).create merge_metadata(data)
         
     | 
| 
       344 
     | 
    
         
            -
                       
     | 
| 
      
 406 
     | 
    
         
            +
                      update_transaction_id(version)
         
     | 
| 
       345 
407 
     | 
    
         
             
                      save_associations(version)
         
     | 
| 
       346 
408 
     | 
    
         
             
                    end
         
     | 
| 
       347 
409 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -351,7 +413,7 @@ module PaperTrail 
     | 
|
| 
       351 
413 
     | 
    
         
             
                  # @api private
         
     | 
| 
       352 
414 
     | 
    
         
             
                  def pt_record_object_changes?
         
     | 
| 
       353 
415 
     | 
    
         
             
                    paper_trail_options[:save_changes] &&
         
     | 
| 
       354 
     | 
    
         
            -
                      self.class.paper_trail_version_class.column_names.include?( 
     | 
| 
      
 416 
     | 
    
         
            +
                      self.class.paper_trail_version_class.column_names.include?("object_changes")
         
     | 
| 
       355 
417 
     | 
    
         
             
                  end
         
     | 
| 
       356 
418 
     | 
    
         | 
| 
       357 
419 
     | 
    
         
             
                  # Returns an object which can be assigned to the `object` attribute of a
         
     | 
| 
         @@ -361,11 +423,10 @@ module PaperTrail 
     | 
|
| 
       361 
423 
     | 
    
         
             
                  # `PaperTrail.serializer`.
         
     | 
| 
       362 
424 
     | 
    
         
             
                  # @api private
         
     | 
| 
       363 
425 
     | 
    
         
             
                  def pt_recordable_object
         
     | 
| 
       364 
     | 
    
         
            -
                    object_attrs = object_attrs_for_paper_trail(attributes_before_change)
         
     | 
| 
       365 
426 
     | 
    
         
             
                    if self.class.paper_trail_version_class.object_col_is_json?
         
     | 
| 
       366 
     | 
    
         
            -
                       
     | 
| 
      
 427 
     | 
    
         
            +
                      object_attrs_for_paper_trail
         
     | 
| 
       367 
428 
     | 
    
         
             
                    else
         
     | 
| 
       368 
     | 
    
         
            -
                      PaperTrail.serializer.dump( 
     | 
| 
      
 429 
     | 
    
         
            +
                      PaperTrail.serializer.dump(object_attrs_for_paper_trail)
         
     | 
| 
       369 
430 
     | 
    
         
             
                    end
         
     | 
| 
       370 
431 
     | 
    
         
             
                  end
         
     | 
| 
       371 
432 
     | 
    
         | 
| 
         @@ -384,9 +445,9 @@ module PaperTrail 
     | 
|
| 
       384 
445 
     | 
    
         
             
                  end
         
     | 
| 
       385 
446 
     | 
    
         | 
| 
       386 
447 
     | 
    
         
             
                  def changes_for_paper_trail
         
     | 
| 
       387 
     | 
    
         
            -
                     
     | 
| 
       388 
     | 
    
         
            -
                    self.class.serialize_attribute_changes_for_paper_trail!( 
     | 
| 
       389 
     | 
    
         
            -
                     
     | 
| 
      
 448 
     | 
    
         
            +
                    notable_changes = changes.delete_if { |k, _v| !notably_changed.include?(k) }
         
     | 
| 
      
 449 
     | 
    
         
            +
                    self.class.serialize_attribute_changes_for_paper_trail!(notable_changes)
         
     | 
| 
      
 450 
     | 
    
         
            +
                    notable_changes.to_hash
         
     | 
| 
       390 
451 
     | 
    
         
             
                  end
         
     | 
| 
       391 
452 
     | 
    
         | 
| 
       392 
453 
     | 
    
         
             
                  # Invoked via`after_update` callback for when a previous version is
         
     | 
| 
         @@ -398,7 +459,7 @@ module PaperTrail 
     | 
|
| 
       398 
459 
     | 
    
         
             
                  # Invoked via callback when a user attempts to persist a reified
         
     | 
| 
       399 
460 
     | 
    
         
             
                  # `Version`.
         
     | 
| 
       400 
461 
     | 
    
         
             
                  def reset_timestamp_attrs_for_update_if_needed!
         
     | 
| 
       401 
     | 
    
         
            -
                    return if  
     | 
| 
      
 462 
     | 
    
         
            +
                    return if live?
         
     | 
| 
       402 
463 
     | 
    
         
             
                    timestamp_attributes_for_update_in_model.each do |column|
         
     | 
| 
       403 
464 
     | 
    
         
             
                      # ActiveRecord 4.2 deprecated `reset_column!` in favor of
         
     | 
| 
       404 
465 
     | 
    
         
             
                      # `restore_column!`.
         
     | 
| 
         @@ -411,21 +472,21 @@ module PaperTrail 
     | 
|
| 
       411 
472 
     | 
    
         
             
                  end
         
     | 
| 
       412 
473 
     | 
    
         | 
| 
       413 
474 
     | 
    
         
             
                  def record_destroy
         
     | 
| 
       414 
     | 
    
         
            -
                    if paper_trail_switched_on?  
     | 
| 
      
 475 
     | 
    
         
            +
                    if paper_trail_switched_on? && !new_record?
         
     | 
| 
       415 
476 
     | 
    
         
             
                      data = {
         
     | 
| 
       416 
     | 
    
         
            -
                        : 
     | 
| 
       417 
     | 
    
         
            -
                        : 
     | 
| 
       418 
     | 
    
         
            -
                        : 
     | 
| 
       419 
     | 
    
         
            -
                        : 
     | 
| 
       420 
     | 
    
         
            -
                        : 
     | 
| 
      
 477 
     | 
    
         
            +
                        item_id: id,
         
     | 
| 
      
 478 
     | 
    
         
            +
                        item_type: self.class.base_class.name,
         
     | 
| 
      
 479 
     | 
    
         
            +
                        event: paper_trail_event || "destroy",
         
     | 
| 
      
 480 
     | 
    
         
            +
                        object: pt_recordable_object,
         
     | 
| 
      
 481 
     | 
    
         
            +
                        whodunnit: PaperTrail.whodunnit
         
     | 
| 
       421 
482 
     | 
    
         
             
                      }
         
     | 
| 
       422 
     | 
    
         
            -
                      if self.class.paper_trail_version_class.column_names.include?( 
     | 
| 
      
 483 
     | 
    
         
            +
                      if self.class.paper_trail_version_class.column_names.include?("transaction_id")
         
     | 
| 
       423 
484 
     | 
    
         
             
                        data[:transaction_id] = PaperTrail.transaction_id
         
     | 
| 
       424 
485 
     | 
    
         
             
                      end
         
     | 
| 
       425 
486 
     | 
    
         
             
                      version = self.class.paper_trail_version_class.create(merge_metadata(data))
         
     | 
| 
       426 
487 
     | 
    
         
             
                      send("#{self.class.version_association_name}=", version)
         
     | 
| 
       427 
488 
     | 
    
         
             
                      send(self.class.versions_association_name).send :load_target
         
     | 
| 
       428 
     | 
    
         
            -
                       
     | 
| 
      
 489 
     | 
    
         
            +
                      update_transaction_id(version)
         
     | 
| 
       429 
490 
     | 
    
         
             
                      save_associations(version)
         
     | 
| 
       430 
491 
     | 
    
         
             
                    end
         
     | 
| 
       431 
492 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -433,31 +494,50 @@ module PaperTrail 
     | 
|
| 
       433 
494 
     | 
    
         
             
                  # Saves associations if the join table for `VersionAssociation` exists.
         
     | 
| 
       434 
495 
     | 
    
         
             
                  def save_associations(version)
         
     | 
| 
       435 
496 
     | 
    
         
             
                    return unless PaperTrail.config.track_associations?
         
     | 
| 
      
 497 
     | 
    
         
            +
                    save_associations_belongs_to(version)
         
     | 
| 
      
 498 
     | 
    
         
            +
                    save_associations_has_and_belongs_to_many(version)
         
     | 
| 
      
 499 
     | 
    
         
            +
                  end
         
     | 
| 
      
 500 
     | 
    
         
            +
             
     | 
| 
      
 501 
     | 
    
         
            +
                  def save_associations_belongs_to(version)
         
     | 
| 
       436 
502 
     | 
    
         
             
                    self.class.reflect_on_all_associations(:belongs_to).each do |assoc|
         
     | 
| 
       437 
503 
     | 
    
         
             
                      assoc_version_args = {
         
     | 
| 
       438 
     | 
    
         
            -
             
     | 
| 
       439 
     | 
    
         
            -
             
     | 
| 
      
 504 
     | 
    
         
            +
                        version_id: version.id,
         
     | 
| 
      
 505 
     | 
    
         
            +
                        foreign_key_name: assoc.foreign_key
         
     | 
| 
       440 
506 
     | 
    
         
             
                      }
         
     | 
| 
       441 
507 
     | 
    
         | 
| 
       442 
508 
     | 
    
         
             
                      if assoc.options[:polymorphic]
         
     | 
| 
       443 
509 
     | 
    
         
             
                        associated_record = send(assoc.name) if send(assoc.foreign_type)
         
     | 
| 
       444 
510 
     | 
    
         
             
                        if associated_record && associated_record.class.paper_trail_enabled_for_model?
         
     | 
| 
       445 
     | 
    
         
            -
                          assoc_version_args 
     | 
| 
      
 511 
     | 
    
         
            +
                          assoc_version_args[:foreign_key_id] = associated_record.id
         
     | 
| 
       446 
512 
     | 
    
         
             
                        end
         
     | 
| 
       447 
513 
     | 
    
         
             
                      elsif assoc.klass.paper_trail_enabled_for_model?
         
     | 
| 
       448 
     | 
    
         
            -
                        assoc_version_args 
     | 
| 
      
 514 
     | 
    
         
            +
                        assoc_version_args[:foreign_key_id] = send(assoc.foreign_key)
         
     | 
| 
       449 
515 
     | 
    
         
             
                      end
         
     | 
| 
       450 
516 
     | 
    
         | 
| 
       451 
     | 
    
         
            -
                       
     | 
| 
      
 517 
     | 
    
         
            +
                      if assoc_version_args.key?(:foreign_key_id)
         
     | 
| 
      
 518 
     | 
    
         
            +
                        PaperTrail::VersionAssociation.create(assoc_version_args)
         
     | 
| 
      
 519 
     | 
    
         
            +
                      end
         
     | 
| 
       452 
520 
     | 
    
         
             
                    end
         
     | 
| 
       453 
521 
     | 
    
         
             
                  end
         
     | 
| 
       454 
522 
     | 
    
         | 
| 
       455 
     | 
    
         
            -
                  def  
     | 
| 
       456 
     | 
    
         
            -
                     
     | 
| 
       457 
     | 
    
         
            -
                     
     | 
| 
       458 
     | 
    
         
            -
             
     | 
| 
       459 
     | 
    
         
            -
             
     | 
| 
       460 
     | 
    
         
            -
             
     | 
| 
      
 523 
     | 
    
         
            +
                  def save_associations_has_and_belongs_to_many(version)
         
     | 
| 
      
 524 
     | 
    
         
            +
                    # Use the :added and :removed keys to extrapolate the HABTM associations
         
     | 
| 
      
 525 
     | 
    
         
            +
                    # to before any changes were made
         
     | 
| 
      
 526 
     | 
    
         
            +
                    self.class.reflect_on_all_associations(:has_and_belongs_to_many).each do |a|
         
     | 
| 
      
 527 
     | 
    
         
            +
                      next unless
         
     | 
| 
      
 528 
     | 
    
         
            +
                        self.class.paper_trail_save_join_tables.include?(a.name) ||
         
     | 
| 
      
 529 
     | 
    
         
            +
                            a.klass.paper_trail_enabled_for_model?
         
     | 
| 
      
 530 
     | 
    
         
            +
                      assoc_version_args = {
         
     | 
| 
      
 531 
     | 
    
         
            +
                        version_id: version.id,
         
     | 
| 
      
 532 
     | 
    
         
            +
                        foreign_key_name: a.name
         
     | 
| 
      
 533 
     | 
    
         
            +
                      }
         
     | 
| 
      
 534 
     | 
    
         
            +
                      assoc_ids =
         
     | 
| 
      
 535 
     | 
    
         
            +
                        send(a.name).to_a.map(&:id) +
         
     | 
| 
      
 536 
     | 
    
         
            +
                        (@paper_trail_habtm.try(:[], a.name).try(:[], :removed) || []) -
         
     | 
| 
      
 537 
     | 
    
         
            +
                        (@paper_trail_habtm.try(:[], a.name).try(:[], :added) || [])
         
     | 
| 
      
 538 
     | 
    
         
            +
                      assoc_ids.each do |id|
         
     | 
| 
      
 539 
     | 
    
         
            +
                        PaperTrail::VersionAssociation.create(assoc_version_args.merge(foreign_key_id: id))
         
     | 
| 
      
 540 
     | 
    
         
            +
                      end
         
     | 
| 
       461 
541 
     | 
    
         
             
                    end
         
     | 
| 
       462 
542 
     | 
    
         
             
                  end
         
     | 
| 
       463 
543 
     | 
    
         | 
| 
         @@ -467,14 +547,14 @@ module PaperTrail 
     | 
|
| 
       467 
547 
     | 
    
         | 
| 
       468 
548 
     | 
    
         
             
                  def merge_metadata(data)
         
     | 
| 
       469 
549 
     | 
    
         
             
                    # First we merge the model-level metadata in `meta`.
         
     | 
| 
       470 
     | 
    
         
            -
                    paper_trail_options[:meta].each do |k,v|
         
     | 
| 
      
 550 
     | 
    
         
            +
                    paper_trail_options[:meta].each do |k, v|
         
     | 
| 
       471 
551 
     | 
    
         
             
                      data[k] =
         
     | 
| 
       472 
552 
     | 
    
         
             
                        if v.respond_to?(:call)
         
     | 
| 
       473 
553 
     | 
    
         
             
                          v.call(self)
         
     | 
| 
       474 
     | 
    
         
            -
                        elsif v.is_a?(Symbol) && respond_to?(v)
         
     | 
| 
      
 554 
     | 
    
         
            +
                        elsif v.is_a?(Symbol) && respond_to?(v, true)
         
     | 
| 
       475 
555 
     | 
    
         
             
                          # If it is an attribute that is changing in an existing object,
         
     | 
| 
       476 
556 
     | 
    
         
             
                          # be sure to grab the current version.
         
     | 
| 
       477 
     | 
    
         
            -
                          if has_attribute?(v) && send("#{v}_changed?".to_sym) && data[:event] !=  
     | 
| 
      
 557 
     | 
    
         
            +
                          if has_attribute?(v) && send("#{v}_changed?".to_sym) && data[:event] != "create"
         
     | 
| 
       478 
558 
     | 
    
         
             
                            send("#{v}_was".to_sym)
         
     | 
| 
       479 
559 
     | 
    
         
             
                          else
         
     | 
| 
       480 
560 
     | 
    
         
             
                            send(v)
         
     | 
| 
         @@ -489,19 +569,14 @@ module PaperTrail 
     | 
|
| 
       489 
569 
     | 
    
         
             
                  end
         
     | 
| 
       490 
570 
     | 
    
         | 
| 
       491 
571 
     | 
    
         
             
                  def attributes_before_change
         
     | 
| 
       492 
     | 
    
         
            -
                     
     | 
| 
       493 
     | 
    
         
            -
             
     | 
| 
       494 
     | 
    
         
            -
                      changed_attributes.select { |k,v| self.class.column_names.include?(k) }.each do |attr, before|
         
     | 
| 
       495 
     | 
    
         
            -
                        before = enums[attr][before] if enums[attr]
         
     | 
| 
       496 
     | 
    
         
            -
                        prev[attr] = before
         
     | 
| 
       497 
     | 
    
         
            -
                      end
         
     | 
| 
       498 
     | 
    
         
            -
                    end
         
     | 
| 
      
 572 
     | 
    
         
            +
                    changed = changed_attributes.select { |k, _v| self.class.column_names.include?(k) }
         
     | 
| 
      
 573 
     | 
    
         
            +
                    attributes.merge(changed)
         
     | 
| 
       499 
574 
     | 
    
         
             
                  end
         
     | 
| 
       500 
575 
     | 
    
         | 
| 
       501 
576 
     | 
    
         
             
                  # Returns hash of attributes (with appropriate attributes serialized),
         
     | 
| 
       502 
577 
     | 
    
         
             
                  # ommitting attributes to be skipped.
         
     | 
| 
       503 
     | 
    
         
            -
                  def object_attrs_for_paper_trail 
     | 
| 
       504 
     | 
    
         
            -
                    attrs =  
     | 
| 
      
 578 
     | 
    
         
            +
                  def object_attrs_for_paper_trail
         
     | 
| 
      
 579 
     | 
    
         
            +
                    attrs = attributes_before_change.except(*paper_trail_options[:skip])
         
     | 
| 
       505 
580 
     | 
    
         
             
                    self.class.serialize_attributes_for_paper_trail!(attrs)
         
     | 
| 
       506 
581 
     | 
    
         
             
                    attrs
         
     | 
| 
       507 
582 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -522,40 +597,58 @@ module PaperTrail 
     | 
|
| 
       522 
597 
     | 
    
         
             
                  # and/or the `:skip` option.  Returns true if an ignored attribute has
         
     | 
| 
       523 
598 
     | 
    
         
             
                  # changed.
         
     | 
| 
       524 
599 
     | 
    
         
             
                  def ignored_attr_has_changed?
         
     | 
| 
       525 
     | 
    
         
            -
                    ignored =  
     | 
| 
      
 600 
     | 
    
         
            +
                    ignored = paper_trail_options[:ignore] + paper_trail_options[:skip]
         
     | 
| 
       526 
601 
     | 
    
         
             
                    ignored.any? && (changed & ignored).any?
         
     | 
| 
       527 
602 
     | 
    
         
             
                  end
         
     | 
| 
       528 
603 
     | 
    
         | 
| 
       529 
604 
     | 
    
         
             
                  def notably_changed
         
     | 
| 
       530 
     | 
    
         
            -
                    only =  
     | 
| 
      
 605 
     | 
    
         
            +
                    only = paper_trail_options[:only].dup
         
     | 
| 
       531 
606 
     | 
    
         
             
                    # Remove Hash arguments and then evaluate whether the attributes (the
         
     | 
| 
       532 
607 
     | 
    
         
             
                    # keys of the hash) should also get pushed into the collection.
         
     | 
| 
       533 
608 
     | 
    
         
             
                    only.delete_if do |obj|
         
     | 
| 
       534 
     | 
    
         
            -
                      obj.is_a?(Hash) && 
     | 
| 
      
 609 
     | 
    
         
            +
                      obj.is_a?(Hash) &&
         
     | 
| 
      
 610 
     | 
    
         
            +
                        obj.each { |attr, condition|
         
     | 
| 
      
 611 
     | 
    
         
            +
                          only << attr if condition.respond_to?(:call) && condition.call(self)
         
     | 
| 
      
 612 
     | 
    
         
            +
                        }
         
     | 
| 
       535 
613 
     | 
    
         
             
                    end
         
     | 
| 
       536 
614 
     | 
    
         
             
                    only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & only)
         
     | 
| 
       537 
615 
     | 
    
         
             
                  end
         
     | 
| 
       538 
616 
     | 
    
         | 
| 
       539 
617 
     | 
    
         
             
                  def changed_and_not_ignored
         
     | 
| 
       540 
     | 
    
         
            -
                    ignore =  
     | 
| 
      
 618 
     | 
    
         
            +
                    ignore = paper_trail_options[:ignore].dup
         
     | 
| 
       541 
619 
     | 
    
         
             
                    # Remove Hash arguments and then evaluate whether the attributes (the
         
     | 
| 
       542 
620 
     | 
    
         
             
                    # keys of the hash) should also get pushed into the collection.
         
     | 
| 
       543 
621 
     | 
    
         
             
                    ignore.delete_if do |obj|
         
     | 
| 
       544 
     | 
    
         
            -
                      obj.is_a?(Hash) && 
     | 
| 
      
 622 
     | 
    
         
            +
                      obj.is_a?(Hash) &&
         
     | 
| 
      
 623 
     | 
    
         
            +
                        obj.each { |attr, condition|
         
     | 
| 
      
 624 
     | 
    
         
            +
                          ignore << attr if condition.respond_to?(:call) && condition.call(self)
         
     | 
| 
      
 625 
     | 
    
         
            +
                        }
         
     | 
| 
       545 
626 
     | 
    
         
             
                    end
         
     | 
| 
       546 
     | 
    
         
            -
                    skip =  
     | 
| 
      
 627 
     | 
    
         
            +
                    skip = paper_trail_options[:skip]
         
     | 
| 
       547 
628 
     | 
    
         
             
                    changed - ignore - skip
         
     | 
| 
       548 
629 
     | 
    
         
             
                  end
         
     | 
| 
       549 
630 
     | 
    
         | 
| 
       550 
631 
     | 
    
         
             
                  def paper_trail_switched_on?
         
     | 
| 
       551 
     | 
    
         
            -
                    PaperTrail.enabled? && 
     | 
| 
      
 632 
     | 
    
         
            +
                    PaperTrail.enabled? &&
         
     | 
| 
      
 633 
     | 
    
         
            +
                      PaperTrail.enabled_for_controller? &&
         
     | 
| 
      
 634 
     | 
    
         
            +
                      paper_trail_enabled_for_model?
         
     | 
| 
       552 
635 
     | 
    
         
             
                  end
         
     | 
| 
       553 
636 
     | 
    
         | 
| 
       554 
637 
     | 
    
         
             
                  def save_version?
         
     | 
| 
       555 
     | 
    
         
            -
                    if_condition 
     | 
| 
       556 
     | 
    
         
            -
                    unless_condition =  
     | 
| 
      
 638 
     | 
    
         
            +
                    if_condition = paper_trail_options[:if]
         
     | 
| 
      
 639 
     | 
    
         
            +
                    unless_condition = paper_trail_options[:unless]
         
     | 
| 
       557 
640 
     | 
    
         
             
                    (if_condition.blank? || if_condition.call(self)) && !unless_condition.try(:call, self)
         
     | 
| 
       558 
641 
     | 
    
         
             
                  end
         
     | 
| 
      
 642 
     | 
    
         
            +
             
     | 
| 
      
 643 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 644 
     | 
    
         
            +
                  def update_transaction_id(version)
         
     | 
| 
      
 645 
     | 
    
         
            +
                    return unless self.class.paper_trail_version_class.column_names.include?("transaction_id")
         
     | 
| 
      
 646 
     | 
    
         
            +
                    if PaperTrail.transaction? && PaperTrail.transaction_id.nil?
         
     | 
| 
      
 647 
     | 
    
         
            +
                      PaperTrail.transaction_id = version.id
         
     | 
| 
      
 648 
     | 
    
         
            +
                      version.transaction_id = version.id
         
     | 
| 
      
 649 
     | 
    
         
            +
                      version.save
         
     | 
| 
      
 650 
     | 
    
         
            +
                    end
         
     | 
| 
      
 651 
     | 
    
         
            +
                  end
         
     | 
| 
       559 
652 
     | 
    
         
             
                end
         
     | 
| 
       560 
653 
     | 
    
         
             
              end
         
     | 
| 
       561 
654 
     | 
    
         
             
            end
         
     |