Skip to content

Commit

Permalink
Respect :skip re: timestamp-only updates
Browse files Browse the repository at this point in the history
When attributes are ignored, either by `:ignore` or by `:skip`,
then during updates, timestamp attributes like `updated_at` should
not be considered notable changes.

[Fixes #569]
  • Loading branch information
jaredbeck committed Jul 16, 2015
1 parent 5bad080 commit 40bf20b
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 7 deletions.
21 changes: 15 additions & 6 deletions lib/paper_trail/has_paper_trail.rb
Expand Up @@ -469,18 +469,27 @@ def object_attrs_for_paper_trail(attributes_hash)
attrs
end

# This method is invoked in order to determine whether it is appropriate to generate a new version instance.
# Because we are now using `after_(create/update/etc)` callbacks, we need to go out of our way to
# ensure that during updates timestamp attributes are not acknowledged as a notable changes
# to raise false positives when attributes are ignored.
# 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.
def changed_notably?
if self.paper_trail_options[:ignore].any? && (changed & self.paper_trail_options[:ignore]).any?
(notably_changed - timestamp_attributes_for_update_in_model.map(&:to_s)).any?
if ignored_attr_has_changed?
timestamps = timestamp_attributes_for_update_in_model.map(&:to_s)
(notably_changed - timestamps).any?
else
notably_changed.any?
end
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.
def ignored_attr_has_changed?
ignored = self.paper_trail_options[:ignore] + self.paper_trail_options[:skip]
ignored.any? && (changed & ignored).any?
end

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
Expand Down
2 changes: 1 addition & 1 deletion spec/models/gadget_spec.rb
Expand Up @@ -45,7 +45,7 @@
expect(subject.send(:changed_notably?)).to be true
end

it "should not acknowledge ignored attrs and timestamps only" do
it "should not acknowledge ignored attr (brand)" do
subject.brand = 'Acme'
expect(subject.send(:changed_notably?)).to be false
end
Expand Down
17 changes: 17 additions & 0 deletions spec/models/skipper_spec.rb
@@ -0,0 +1,17 @@
require 'rails_helper'

describe Skipper, :type => :model do
describe "#update_attributes!", :versioning => true do
context "updating a skipped attribute" do
let(:t1) { Time.zone.local(2015, 7, 15, 20, 34, 0) }
let(:t2) { Time.zone.local(2015, 7, 15, 20, 34, 30) }

it "should not create a version" do
skipper = Skipper.create!(:another_timestamp => t1)
expect {
skipper.update_attributes!(:another_timestamp => t2)
}.to_not change { skipper.versions.length }
end
end
end
end
6 changes: 6 additions & 0 deletions test/dummy/app/models/skipper.rb
@@ -0,0 +1,6 @@
class Skipper < ActiveRecord::Base
has_paper_trail(
:ignore => [:created_at],
:skip => [:another_timestamp]
)
end
6 changes: 6 additions & 0 deletions test/dummy/db/migrate/20110208155312_set_up_test_tables.rb
@@ -1,5 +1,10 @@
class SetUpTestTables < ActiveRecord::Migration
def self.up
create_table :skippers, :force => true do |t|
t.datetime :another_timestamp
t.timestamps :null => true
end

create_table :widgets, :force => true do |t|
t.string :name
t.text :a_text
Expand Down Expand Up @@ -209,6 +214,7 @@ def self.up

def self.down
drop_table :animals
drop_table :skippers
drop_table :not_on_updates
drop_table :posts
drop_table :songs
Expand Down

0 comments on commit 40bf20b

Please sign in to comment.