paper_trail_without_deprecated 3.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +3 -0
- data/.travis.yml +21 -0
- data/CHANGELOG.md +68 -0
- data/Gemfile +2 -0
- data/MIT-LICENSE +20 -0
- data/README.md +979 -0
- data/Rakefile +18 -0
- data/gemfiles/3.0.gemfile +31 -0
- data/lib/generators/paper_trail/USAGE +2 -0
- data/lib/generators/paper_trail/install_generator.rb +23 -0
- data/lib/generators/paper_trail/templates/add_object_changes_column_to_versions.rb +9 -0
- data/lib/generators/paper_trail/templates/create_versions.rb +18 -0
- data/lib/paper_trail.rb +115 -0
- data/lib/paper_trail/cleaner.rb +34 -0
- data/lib/paper_trail/config.rb +14 -0
- data/lib/paper_trail/frameworks/cucumber.rb +31 -0
- data/lib/paper_trail/frameworks/rails.rb +79 -0
- data/lib/paper_trail/frameworks/rspec.rb +24 -0
- data/lib/paper_trail/frameworks/rspec/extensions.rb +20 -0
- data/lib/paper_trail/frameworks/sinatra.rb +31 -0
- data/lib/paper_trail/has_paper_trail.rb +308 -0
- data/lib/paper_trail/serializers/json.rb +17 -0
- data/lib/paper_trail/serializers/yaml.rb +17 -0
- data/lib/paper_trail/version.rb +200 -0
- data/lib/paper_trail/version_number.rb +3 -0
- data/paper_trail.gemspec +36 -0
- data/spec/models/widget_spec.rb +13 -0
- data/spec/paper_trail_spec.rb +47 -0
- data/spec/spec_helper.rb +41 -0
- data/test/custom_json_serializer.rb +13 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/controllers/application_controller.rb +17 -0
- data/test/dummy/app/controllers/test_controller.rb +5 -0
- data/test/dummy/app/controllers/widgets_controller.rb +31 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/animal.rb +4 -0
- data/test/dummy/app/models/article.rb +16 -0
- data/test/dummy/app/models/authorship.rb +5 -0
- data/test/dummy/app/models/book.rb +5 -0
- data/test/dummy/app/models/cat.rb +2 -0
- data/test/dummy/app/models/document.rb +4 -0
- data/test/dummy/app/models/dog.rb +2 -0
- data/test/dummy/app/models/elephant.rb +3 -0
- data/test/dummy/app/models/fluxor.rb +3 -0
- data/test/dummy/app/models/foo_widget.rb +2 -0
- data/test/dummy/app/models/legacy_widget.rb +4 -0
- data/test/dummy/app/models/person.rb +28 -0
- data/test/dummy/app/models/post.rb +4 -0
- data/test/dummy/app/models/protected_widget.rb +3 -0
- data/test/dummy/app/models/song.rb +12 -0
- data/test/dummy/app/models/translation.rb +4 -0
- data/test/dummy/app/models/widget.rb +10 -0
- data/test/dummy/app/models/wotsit.rb +4 -0
- data/test/dummy/app/versions/post_version.rb +3 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +63 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +22 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +40 -0
- data/test/dummy/config/environments/production.rb +73 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/paper_trail.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +3 -0
- data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +136 -0
- data/test/dummy/db/schema.rb +101 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/javascripts/application.js +2 -0
- data/test/dummy/public/javascripts/controls.js +965 -0
- data/test/dummy/public/javascripts/dragdrop.js +974 -0
- data/test/dummy/public/javascripts/effects.js +1123 -0
- data/test/dummy/public/javascripts/prototype.js +6001 -0
- data/test/dummy/public/javascripts/rails.js +175 -0
- data/test/dummy/public/stylesheets/.gitkeep +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/functional/controller_test.rb +90 -0
- data/test/functional/modular_sinatra_test.rb +44 -0
- data/test/functional/sinatra_test.rb +45 -0
- data/test/functional/thread_safety_test.rb +26 -0
- data/test/paper_trail_test.rb +27 -0
- data/test/test_helper.rb +40 -0
- data/test/unit/cleaner_test.rb +143 -0
- data/test/unit/inheritance_column_test.rb +43 -0
- data/test/unit/model_test.rb +1314 -0
- data/test/unit/protected_attrs_test.rb +46 -0
- data/test/unit/serializer_test.rb +117 -0
- data/test/unit/serializers/json_test.rb +40 -0
- data/test/unit/serializers/mixin_json_test.rb +36 -0
- data/test/unit/serializers/mixin_yaml_test.rb +49 -0
- data/test/unit/serializers/yaml_test.rb +40 -0
- data/test/unit/timestamp_test.rb +44 -0
- data/test/unit/version_test.rb +74 -0
- metadata +286 -0
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class PaperTrailCleanerTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
setup do
|
6
|
+
@animals = [@animal = Animal.new, @dog = Dog.new, @cat = Cat.new]
|
7
|
+
@animals.each do |animal|
|
8
|
+
3.times { animal.update_attribute(:name, Faker::Name.name) }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
test 'Baseline' do
|
13
|
+
assert_equal 9, PaperTrail::Version.count
|
14
|
+
@animals.each { |animal| assert_equal 3, animal.versions.size }
|
15
|
+
end
|
16
|
+
|
17
|
+
context '`clean_versions!` method' do
|
18
|
+
should 'be extended by `PaperTrail` module' do
|
19
|
+
assert_respond_to PaperTrail, :clean_versions!
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'No options provided' do
|
23
|
+
should 'removes extra versions for each item' do
|
24
|
+
PaperTrail.clean_versions!
|
25
|
+
assert_equal 3, PaperTrail::Version.count
|
26
|
+
@animals.each { |animal| assert_equal 1, animal.versions.size }
|
27
|
+
end
|
28
|
+
|
29
|
+
should 'removes the earliest version(s)' do
|
30
|
+
most_recent_version_names = @animals.map { |animal| animal.versions.last.reify.name }
|
31
|
+
PaperTrail.clean_versions!
|
32
|
+
assert_equal most_recent_version_names, @animals.map { |animal| animal.versions.last.reify.name }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context '`:keeping` option' do
|
37
|
+
should 'modifies the number of versions ommitted from destruction' do
|
38
|
+
PaperTrail.clean_versions!(:keeping => 2)
|
39
|
+
assert_equal 6, PaperTrail::Version.all.count
|
40
|
+
@animals.each { |animal| assert_equal 2, animal.versions.size }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context '`:date` option' do
|
45
|
+
setup do
|
46
|
+
@animal.versions.each { |ver| ver.update_attribute(:created_at, ver.created_at - 1.day) }
|
47
|
+
@date = @animal.versions.first.created_at.to_date
|
48
|
+
@animal.update_attribute(:name, Faker::Name.name)
|
49
|
+
end
|
50
|
+
|
51
|
+
should 'restrict the versions destroyed to those that were created on the date provided' do
|
52
|
+
assert_equal 10, PaperTrail::Version.count
|
53
|
+
assert_equal 4, @animal.versions.size
|
54
|
+
assert_equal 3, @animal.versions_between(@date, @date + 1.day).size
|
55
|
+
PaperTrail.clean_versions!(:date => @date)
|
56
|
+
assert_equal 8, PaperTrail::Version.count
|
57
|
+
assert_equal 2, @animal.versions(true).size
|
58
|
+
assert_equal @date, @animal.versions.first.created_at.to_date
|
59
|
+
assert_not_same @date, @animal.versions.last.created_at.to_date
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context '`:item_id` option' do
|
64
|
+
context 'single ID received' do
|
65
|
+
should 'restrict the versions destroyed to the versions for the Item with that ID' do
|
66
|
+
PaperTrail.clean_versions!(:item_id => @animal.id)
|
67
|
+
assert_equal 1, @animal.versions.size
|
68
|
+
assert_equal 7, PaperTrail::Version.count
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "collection of ID's received" do
|
73
|
+
should "restrict the versions destroyed to the versions for the Item with those ID's" do
|
74
|
+
PaperTrail.clean_versions!(:item_id => [@animal.id, @dog.id])
|
75
|
+
assert_equal 1, @animal.versions.size
|
76
|
+
assert_equal 1, @dog.versions.size
|
77
|
+
assert_equal 5, PaperTrail::Version.count
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'options combinations' do # additional tests to cover combinations of options
|
83
|
+
context '`:date`' do
|
84
|
+
setup do
|
85
|
+
[@animal, @dog].each do |animal|
|
86
|
+
animal.versions.each { |ver| ver.update_attribute(:created_at, ver.created_at - 1.day) }
|
87
|
+
animal.update_attribute(:name, Faker::Name.name)
|
88
|
+
end
|
89
|
+
@date = @animal.versions.first.created_at.to_date
|
90
|
+
end
|
91
|
+
|
92
|
+
should 'Baseline' do
|
93
|
+
assert_equal 11, PaperTrail::Version.count
|
94
|
+
[@animal, @dog].each do |animal|
|
95
|
+
assert_equal 4, animal.versions.size
|
96
|
+
assert_equal 3, animal.versions.between(@date, @date+1.day).size
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'and `:keeping`' do
|
101
|
+
should 'restrict cleaning properly' do
|
102
|
+
PaperTrail.clean_versions!(:date => @date, :keeping => 2)
|
103
|
+
[@animal, @dog].each do |animal|
|
104
|
+
animal.versions.reload # reload the association to pick up the destructions made by the `Cleaner`
|
105
|
+
assert_equal 3, animal.versions.size
|
106
|
+
assert_equal 2, animal.versions.between(@date, @date+1.day).size
|
107
|
+
end
|
108
|
+
assert_equal 9, PaperTrail::Version.count # ensure that the versions for the `@cat` instance wasn't touched
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'and `:item_id`' do
|
113
|
+
should 'restrict cleaning properly' do
|
114
|
+
PaperTrail.clean_versions!(:date => @date, :item_id => @dog.id)
|
115
|
+
@dog.versions.reload # reload the association to pick up the destructions made by the `Cleaner`
|
116
|
+
assert_equal 2, @dog.versions.size
|
117
|
+
assert_equal 1, @dog.versions.between(@date, @date+1.day).size
|
118
|
+
assert_equal 9, PaperTrail::Version.count # ensure the versions for other animals besides `@animal` weren't touched
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context ', `:item_id`, and `:keeping`' do
|
123
|
+
should 'restrict cleaning properly' do
|
124
|
+
PaperTrail.clean_versions!(:date => @date, :item_id => @dog.id, :keeping => 2)
|
125
|
+
@dog.versions.reload # reload the association to pick up the destructions made by the `Cleaner`
|
126
|
+
assert_equal 3, @dog.versions.size
|
127
|
+
assert_equal 2, @dog.versions.between(@date, @date+1.day).size
|
128
|
+
assert_equal 10, PaperTrail::Version.count # ensure the versions for other animals besides `@animal` weren't touched
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context '`:keeping` and `:item_id`' do
|
134
|
+
should 'restrict cleaning properly' do
|
135
|
+
PaperTrail.clean_versions!(:keeping => 2, :item_id => @animal.id)
|
136
|
+
assert_equal 2, @animal.versions.size
|
137
|
+
assert_equal 8, PaperTrail::Version.count # ensure the versions for other animals besides `@animal` weren't touched
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end # clean_versions! method
|
143
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class InheritanceColumnTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
context 'STI models' do
|
6
|
+
setup do
|
7
|
+
@animal = Animal.create :name => 'Animal'
|
8
|
+
@animal.update_attributes :name => 'Animal from the Muppets'
|
9
|
+
@animal.update_attributes :name => 'Animal Muppet'
|
10
|
+
@animal.destroy
|
11
|
+
|
12
|
+
@dog = Dog.create :name => 'Snoopy'
|
13
|
+
@dog.update_attributes :name => 'Scooby'
|
14
|
+
@dog.update_attributes :name => 'Scooby Doo'
|
15
|
+
@dog.destroy
|
16
|
+
|
17
|
+
@cat = Cat.create :name => 'Garfield'
|
18
|
+
@cat.update_attributes :name => 'Garfield (I hate Mondays)'
|
19
|
+
@cat.update_attributes :name => 'Garfield The Cat'
|
20
|
+
@cat.destroy
|
21
|
+
end
|
22
|
+
|
23
|
+
should 'work with custom STI inheritance column' do
|
24
|
+
assert_equal 12, PaperTrail::Version.count
|
25
|
+
assert_equal 4, @animal.versions.count
|
26
|
+
assert @animal.versions.first.reify.nil?
|
27
|
+
@animal.versions[1..-1].each { |v| assert_equal 'Animal', v.reify.class.name }
|
28
|
+
|
29
|
+
# For some reason `@dog.versions` doesn't include the final `destroy` version.
|
30
|
+
# Neither do `@dog.versions.scoped` nor `@dog.versions(true)` nor `@dog.versions.reload`.
|
31
|
+
dog_versions = PaperTrail::Version.where(:item_id => @dog.id)
|
32
|
+
assert_equal 4, dog_versions.count
|
33
|
+
assert dog_versions.first.reify.nil?
|
34
|
+
dog_versions[1..-1].each { |v| assert_equal 'Dog', v.reify.class.name }
|
35
|
+
|
36
|
+
cat_versions = PaperTrail::Version.where(:item_id => @cat.id)
|
37
|
+
assert_equal 4, cat_versions.count
|
38
|
+
assert cat_versions.first.reify.nil?
|
39
|
+
cat_versions[1..-1].each { |v| assert_equal 'Cat', v.reify.class.name }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,1314 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class HasPaperTrailModelTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
context "A record with defined 'only' and 'ignore' attributes" do
|
6
|
+
setup { @article = Article.create }
|
7
|
+
should 'creation should change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
|
8
|
+
|
9
|
+
context 'which updates an ignored column' do
|
10
|
+
setup { @article.update_attributes :title => 'My first title' }
|
11
|
+
should 'not change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'which updates an ignored column and a selected column' do
|
15
|
+
setup { @article.update_attributes :title => 'My first title', :content => 'Some text here.' }
|
16
|
+
should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
|
17
|
+
|
18
|
+
should "show the new version in the model's `versions` association" do
|
19
|
+
assert_equal(2, @article.versions.size)
|
20
|
+
end
|
21
|
+
|
22
|
+
should 'have stored only non-ignored attributes' do
|
23
|
+
assert_equal ({'content' => [nil, 'Some text here.']}), @article.versions.last.changeset
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'which updates a selected column' do
|
28
|
+
setup { @article.update_attributes :content => 'Some text here.' }
|
29
|
+
should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
|
30
|
+
|
31
|
+
should "show the new version in the model's `versions` association" do
|
32
|
+
assert_equal(2, @article.versions.size)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'which updates a non-ignored and non-selected column' do
|
37
|
+
setup { @article.update_attributes :abstract => 'Other abstract'}
|
38
|
+
should 'not change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'which updates a skipped column' do
|
42
|
+
setup { @article.update_attributes :file_upload => 'Your data goes here' }
|
43
|
+
should 'not change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'which updates a skipped column and a selected column' do
|
47
|
+
setup { @article.update_attributes :file_upload => 'Your data goes here', :content => 'Some text here.' }
|
48
|
+
should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
|
49
|
+
|
50
|
+
should "show the new version in the model's `versions` association" do
|
51
|
+
assert_equal(2, @article.versions.size)
|
52
|
+
end
|
53
|
+
|
54
|
+
should 'have stored only non-skipped attributes' do
|
55
|
+
assert_equal ({'content' => [nil, 'Some text here.']}), @article.versions.last.changeset
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'and when updated again' do
|
59
|
+
setup do
|
60
|
+
@article.update_attributes :file_upload => 'More data goes here', :content => 'More text here.'
|
61
|
+
@old_article = @article.versions.last
|
62
|
+
end
|
63
|
+
|
64
|
+
should 'have removed the skipped attributes when saving the previous version' do
|
65
|
+
assert_equal nil, PaperTrail.serializer.load(@old_article.object)['file_upload']
|
66
|
+
end
|
67
|
+
|
68
|
+
should 'have kept the non-skipped attributes in the previous version' do
|
69
|
+
assert_equal 'Some text here.', PaperTrail.serializer.load(@old_article.object)['content']
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'which gets destroyed' do
|
75
|
+
setup { @article.destroy }
|
76
|
+
should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
|
77
|
+
|
78
|
+
should "show the new version in the model's `versions` association" do
|
79
|
+
assert_equal(2, @article.versions.size)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "A record with defined 'ignore' attribute" do
|
85
|
+
setup { @legacy_widget = LegacyWidget.create }
|
86
|
+
|
87
|
+
context 'which updates an ignored column' do
|
88
|
+
setup { @legacy_widget.update_attributes :version => 1 }
|
89
|
+
should 'not change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'A record with defined "if" and "unless" attributes' do
|
94
|
+
setup { @translation = Translation.new :headline => 'Headline' }
|
95
|
+
|
96
|
+
context 'for non-US translations' do
|
97
|
+
setup { @translation.save }
|
98
|
+
should 'not change the number of versions' do assert_equal(0, PaperTrail::Version.count) end
|
99
|
+
|
100
|
+
context 'after update' do
|
101
|
+
setup { @translation.update_attributes :content => 'Content' }
|
102
|
+
should 'not change the number of versions' do assert_equal(0, PaperTrail::Version.count) end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'after destroy' do
|
106
|
+
setup { @translation.destroy }
|
107
|
+
should 'not change the number of versions' do assert_equal(0, PaperTrail::Version.count) end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'for US translations' do
|
112
|
+
setup { @translation.language_code = "US" }
|
113
|
+
|
114
|
+
context 'that are drafts' do
|
115
|
+
setup do
|
116
|
+
@translation.type = 'DRAFT'
|
117
|
+
@translation.save
|
118
|
+
end
|
119
|
+
|
120
|
+
should 'not change the number of versions' do assert_equal(0, PaperTrail::Version.count) end
|
121
|
+
|
122
|
+
context 'after update' do
|
123
|
+
setup { @translation.update_attributes :content => 'Content' }
|
124
|
+
should 'not change the number of versions' do assert_equal(0, PaperTrail::Version.count) end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'that are not drafts' do
|
129
|
+
setup { @translation.save }
|
130
|
+
|
131
|
+
should 'change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
|
132
|
+
|
133
|
+
context 'after update' do
|
134
|
+
setup { @translation.update_attributes :content => 'Content' }
|
135
|
+
should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
|
136
|
+
|
137
|
+
should "show the new version in the model's `versions` association" do
|
138
|
+
assert_equal(2, @translation.versions.size)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'after destroy' do
|
143
|
+
setup { @translation.destroy }
|
144
|
+
should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
|
145
|
+
|
146
|
+
should "show the new version in the model's `versions` association" do
|
147
|
+
assert_equal(2, @translation.versions.size)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context 'A new record' do
|
155
|
+
setup { @widget = Widget.new }
|
156
|
+
|
157
|
+
should 'not have any previous versions' do
|
158
|
+
assert_equal [], @widget.versions
|
159
|
+
end
|
160
|
+
|
161
|
+
should 'be live' do
|
162
|
+
assert @widget.live?
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
context 'which is then created' do
|
167
|
+
setup { @widget.update_attributes :name => 'Henry' }
|
168
|
+
|
169
|
+
should 'have one previous version' do
|
170
|
+
assert_equal 1, @widget.versions.length
|
171
|
+
end
|
172
|
+
|
173
|
+
should 'be nil in its previous version' do
|
174
|
+
assert_nil @widget.versions.first.object
|
175
|
+
assert_nil @widget.versions.first.reify
|
176
|
+
end
|
177
|
+
|
178
|
+
should 'record the correct event' do
|
179
|
+
assert_match /create/i, @widget.versions.first.event
|
180
|
+
end
|
181
|
+
|
182
|
+
should 'be live' do
|
183
|
+
assert @widget.live?
|
184
|
+
end
|
185
|
+
|
186
|
+
should 'have changes' do
|
187
|
+
changes = {
|
188
|
+
'name' => [nil, 'Henry'],
|
189
|
+
'created_at' => [nil, @widget.created_at],
|
190
|
+
'updated_at' => [nil, @widget.updated_at],
|
191
|
+
'id' => [nil, 1]
|
192
|
+
}
|
193
|
+
|
194
|
+
assert_equal changes, @widget.versions.last.changeset
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'and then updated without any changes' do
|
198
|
+
setup { @widget.touch }
|
199
|
+
|
200
|
+
should 'not have a new version' do
|
201
|
+
assert_equal 1, @widget.versions.length
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
context 'and then updated with changes' do
|
207
|
+
setup { @widget.update_attributes :name => 'Harry' }
|
208
|
+
|
209
|
+
should 'have two previous versions' do
|
210
|
+
assert_equal 2, @widget.versions.length
|
211
|
+
end
|
212
|
+
|
213
|
+
should 'be available in its previous version' do
|
214
|
+
assert_equal 'Harry', @widget.name
|
215
|
+
assert_not_nil @widget.versions.last.object
|
216
|
+
widget = @widget.versions.last.reify
|
217
|
+
assert_equal 'Henry', widget.name
|
218
|
+
assert_equal 'Harry', @widget.name
|
219
|
+
end
|
220
|
+
|
221
|
+
should 'have the same ID in its previous version' do
|
222
|
+
assert_equal @widget.id, @widget.versions.last.reify.id
|
223
|
+
end
|
224
|
+
|
225
|
+
should 'record the correct event' do
|
226
|
+
assert_match /update/i, @widget.versions.last.event
|
227
|
+
end
|
228
|
+
|
229
|
+
should 'have versions that are not live' do
|
230
|
+
assert @widget.versions.map(&:reify).compact.all? { |w| !w.live? }
|
231
|
+
end
|
232
|
+
|
233
|
+
should 'have stored changes' do
|
234
|
+
# Behavior for ActiveRecord 4 is different than ActiveRecord 3;
|
235
|
+
# AR4 includes the `updated_at` column in changes for updates, which is why we reject it from the right side of this assertion.
|
236
|
+
assert_equal ({'name' => ['Henry', 'Harry']}), PaperTrail.serializer.load(@widget.versions.last.object_changes).reject { |k,v| k.to_sym == :updated_at }
|
237
|
+
assert_equal ({'name' => ['Henry', 'Harry']}), @widget.versions.last.changeset.reject { |k,v| k.to_sym == :updated_at }
|
238
|
+
end
|
239
|
+
|
240
|
+
should 'return changes with indifferent access' do
|
241
|
+
assert_equal ['Henry', 'Harry'], @widget.versions.last.changeset[:name]
|
242
|
+
assert_equal ['Henry', 'Harry'], @widget.versions.last.changeset['name']
|
243
|
+
end
|
244
|
+
|
245
|
+
if defined?(ActiveRecord::IdentityMap) && ActiveRecord::IdentityMap.respond_to?(:without)
|
246
|
+
should 'not clobber the IdentityMap when reifying' do
|
247
|
+
module ActiveRecord::IdentityMap
|
248
|
+
class << self
|
249
|
+
alias :__without :without
|
250
|
+
def without(&block)
|
251
|
+
@unclobbered = true
|
252
|
+
__without(&block)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
@widget.versions.last.reify
|
258
|
+
assert ActiveRecord::IdentityMap.instance_variable_get("@unclobbered")
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context 'and has one associated object' do
|
263
|
+
setup do
|
264
|
+
@wotsit = @widget.create_wotsit :name => 'John'
|
265
|
+
end
|
266
|
+
|
267
|
+
should 'not copy the has_one association by default when reifying' do
|
268
|
+
reified_widget = @widget.versions.last.reify
|
269
|
+
assert_equal @wotsit, reified_widget.wotsit # association hasn't been affected by reifying
|
270
|
+
assert_equal @wotsit, @widget.wotsit # confirm that the association is correct
|
271
|
+
end
|
272
|
+
|
273
|
+
should 'copy the has_one association when reifying with :has_one => true' do
|
274
|
+
reified_widget = @widget.versions.last.reify(:has_one => true)
|
275
|
+
assert_nil reified_widget.wotsit # wotsit wasn't there at the last version
|
276
|
+
assert_equal @wotsit, @widget.wotsit # wotsit came into being on the live object
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
context 'and has many associated objects' do
|
282
|
+
setup do
|
283
|
+
@f0 = @widget.fluxors.create :name => 'f-zero'
|
284
|
+
@f1 = @widget.fluxors.create :name => 'f-one'
|
285
|
+
@reified_widget = @widget.versions.last.reify
|
286
|
+
end
|
287
|
+
|
288
|
+
should 'copy the has_many associations when reifying' do
|
289
|
+
assert_equal @widget.fluxors.length, @reified_widget.fluxors.length
|
290
|
+
assert_same_elements @widget.fluxors, @reified_widget.fluxors
|
291
|
+
|
292
|
+
assert_equal @widget.versions.length, @reified_widget.versions.length
|
293
|
+
assert_same_elements @widget.versions, @reified_widget.versions
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
|
298
|
+
context 'and then destroyed' do
|
299
|
+
setup do
|
300
|
+
@fluxor = @widget.fluxors.create :name => 'flux'
|
301
|
+
@widget.destroy
|
302
|
+
@reified_widget = PaperTrail::Version.last.reify
|
303
|
+
end
|
304
|
+
|
305
|
+
should 'record the correct event' do
|
306
|
+
assert_match /destroy/i, PaperTrail::Version.last.event
|
307
|
+
end
|
308
|
+
|
309
|
+
should 'have three previous versions' do
|
310
|
+
assert_equal 3, PaperTrail::Version.with_item_keys('Widget', @widget.id).length
|
311
|
+
end
|
312
|
+
|
313
|
+
should 'be available in its previous version' do
|
314
|
+
assert_equal @widget.id, @reified_widget.id
|
315
|
+
assert_equal @widget.attributes, @reified_widget.attributes
|
316
|
+
end
|
317
|
+
|
318
|
+
should 'be re-creatable from its previous version' do
|
319
|
+
assert @reified_widget.save
|
320
|
+
end
|
321
|
+
|
322
|
+
should 'restore its associations on its previous version' do
|
323
|
+
@reified_widget.save
|
324
|
+
assert_equal 1, @reified_widget.fluxors.length
|
325
|
+
end
|
326
|
+
|
327
|
+
should 'not have changes' do
|
328
|
+
assert_equal Hash.new, @widget.versions.last.changeset
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
|
336
|
+
# Test the serialisation and deserialisation.
|
337
|
+
# TODO: binary
|
338
|
+
context "A record's papertrail" do
|
339
|
+
setup do
|
340
|
+
@date_time = DateTime.now.utc
|
341
|
+
@time = Time.now
|
342
|
+
@date = Date.new 2009, 5, 29
|
343
|
+
@widget = Widget.create :name => 'Warble',
|
344
|
+
:a_text => 'The quick brown fox',
|
345
|
+
:an_integer => 42,
|
346
|
+
:a_float => 153.01,
|
347
|
+
:a_decimal => 2.71828,
|
348
|
+
:a_datetime => @date_time,
|
349
|
+
:a_time => @time,
|
350
|
+
:a_date => @date,
|
351
|
+
:a_boolean => true
|
352
|
+
|
353
|
+
@widget.update_attributes :name => nil,
|
354
|
+
:a_text => nil,
|
355
|
+
:an_integer => nil,
|
356
|
+
:a_float => nil,
|
357
|
+
:a_decimal => nil,
|
358
|
+
:a_datetime => nil,
|
359
|
+
:a_time => nil,
|
360
|
+
:a_date => nil,
|
361
|
+
:a_boolean => false
|
362
|
+
@previous = @widget.versions.last.reify
|
363
|
+
end
|
364
|
+
|
365
|
+
should 'handle strings' do
|
366
|
+
assert_equal 'Warble', @previous.name
|
367
|
+
end
|
368
|
+
|
369
|
+
should 'handle text' do
|
370
|
+
assert_equal 'The quick brown fox', @previous.a_text
|
371
|
+
end
|
372
|
+
|
373
|
+
should 'handle integers' do
|
374
|
+
assert_equal 42, @previous.an_integer
|
375
|
+
end
|
376
|
+
|
377
|
+
should 'handle floats' do
|
378
|
+
assert_in_delta 153.01, @previous.a_float, 0.001
|
379
|
+
end
|
380
|
+
|
381
|
+
should 'handle decimals' do
|
382
|
+
assert_in_delta 2.71828, @previous.a_decimal, 0.00001
|
383
|
+
end
|
384
|
+
|
385
|
+
should 'handle datetimes' do
|
386
|
+
assert_equal @date_time.to_time.utc.to_i, @previous.a_datetime.to_time.utc.to_i
|
387
|
+
end
|
388
|
+
|
389
|
+
should 'handle times' do
|
390
|
+
assert_equal @time.utc.to_i, @previous.a_time.utc.to_i
|
391
|
+
end
|
392
|
+
|
393
|
+
should 'handle dates' do
|
394
|
+
assert_equal @date, @previous.a_date
|
395
|
+
end
|
396
|
+
|
397
|
+
should 'handle booleans' do
|
398
|
+
assert @previous.a_boolean
|
399
|
+
end
|
400
|
+
|
401
|
+
|
402
|
+
context "after a column is removed from the record's schema" do
|
403
|
+
setup do
|
404
|
+
change_schema
|
405
|
+
Widget.connection.schema_cache.clear!
|
406
|
+
Widget.reset_column_information
|
407
|
+
assert_raise(NoMethodError) { Widget.new.sacrificial_column }
|
408
|
+
@last = @widget.versions.last
|
409
|
+
end
|
410
|
+
|
411
|
+
should 'reify previous version' do
|
412
|
+
assert_kind_of Widget, @last.reify
|
413
|
+
end
|
414
|
+
|
415
|
+
should 'restore all forward-compatible attributes' do
|
416
|
+
assert_equal 'Warble', @last.reify.name
|
417
|
+
assert_equal 'The quick brown fox', @last.reify.a_text
|
418
|
+
assert_equal 42, @last.reify.an_integer
|
419
|
+
assert_in_delta 153.01, @last.reify.a_float, 0.001
|
420
|
+
assert_in_delta 2.71828, @last.reify.a_decimal, 0.00001
|
421
|
+
assert_equal @date_time.to_time.utc.to_i, @last.reify.a_datetime.to_time.utc.to_i
|
422
|
+
assert_equal @time.utc.to_i, @last.reify.a_time.utc.to_i
|
423
|
+
assert_equal @date, @last.reify.a_date
|
424
|
+
assert @last.reify.a_boolean
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
|
430
|
+
context 'A record' do
|
431
|
+
setup { @widget = Widget.create :name => 'Zaphod' }
|
432
|
+
|
433
|
+
context 'with PaperTrail globally disabled' do
|
434
|
+
setup do
|
435
|
+
PaperTrail.enabled = false
|
436
|
+
@count = @widget.versions.length
|
437
|
+
end
|
438
|
+
|
439
|
+
teardown { PaperTrail.enabled = true }
|
440
|
+
|
441
|
+
context 'when updated' do
|
442
|
+
setup { @widget.update_attributes :name => 'Beeblebrox' }
|
443
|
+
|
444
|
+
should 'not add to its trail' do
|
445
|
+
assert_equal @count, @widget.versions.length
|
446
|
+
end
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
context 'with its paper trail turned off' do
|
451
|
+
setup do
|
452
|
+
Widget.paper_trail_off
|
453
|
+
@count = @widget.versions.length
|
454
|
+
end
|
455
|
+
|
456
|
+
teardown { Widget.paper_trail_on }
|
457
|
+
|
458
|
+
context 'when updated' do
|
459
|
+
setup { @widget.update_attributes :name => 'Beeblebrox' }
|
460
|
+
|
461
|
+
should 'not add to its trail' do
|
462
|
+
assert_equal @count, @widget.versions.length
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
context 'when destroyed "without versioning"' do
|
467
|
+
should 'leave paper trail off after call' do
|
468
|
+
@widget.without_versioning :destroy
|
469
|
+
assert !Widget.paper_trail_enabled_for_model
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
context 'and then its paper trail turned on' do
|
474
|
+
setup { Widget.paper_trail_on }
|
475
|
+
|
476
|
+
context 'when updated' do
|
477
|
+
setup { @widget.update_attributes :name => 'Ford' }
|
478
|
+
|
479
|
+
should 'add to its trail' do
|
480
|
+
assert_equal @count + 1, @widget.versions.length
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
context 'when updated "without versioning"' do
|
485
|
+
setup do
|
486
|
+
@widget.without_versioning do
|
487
|
+
@widget.update_attributes :name => 'Ford'
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
should 'not create new version' do
|
492
|
+
assert_equal 1, @widget.versions.length
|
493
|
+
end
|
494
|
+
|
495
|
+
should 'enable paper trail after call' do
|
496
|
+
assert Widget.paper_trail_enabled_for_model
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
|
504
|
+
context 'A papertrail with somebody making changes' do
|
505
|
+
setup do
|
506
|
+
@widget = Widget.new :name => 'Fidget'
|
507
|
+
end
|
508
|
+
|
509
|
+
context 'when a record is created' do
|
510
|
+
setup do
|
511
|
+
PaperTrail.whodunnit = 'Alice'
|
512
|
+
@widget.save
|
513
|
+
@version = @widget.versions.last # only 1 version
|
514
|
+
end
|
515
|
+
|
516
|
+
should 'track who made the change' do
|
517
|
+
assert_equal 'Alice', @version.whodunnit
|
518
|
+
assert_nil @version.originator
|
519
|
+
assert_equal 'Alice', @version.terminator
|
520
|
+
assert_equal 'Alice', @widget.originator
|
521
|
+
end
|
522
|
+
|
523
|
+
context 'when a record is updated' do
|
524
|
+
setup do
|
525
|
+
PaperTrail.whodunnit = 'Bob'
|
526
|
+
@widget.update_attributes :name => 'Rivet'
|
527
|
+
@version = @widget.versions.last
|
528
|
+
end
|
529
|
+
|
530
|
+
should 'track who made the change' do
|
531
|
+
assert_equal 'Bob', @version.whodunnit
|
532
|
+
assert_equal 'Alice', @version.originator
|
533
|
+
assert_equal 'Bob', @version.terminator
|
534
|
+
assert_equal 'Bob', @widget.originator
|
535
|
+
end
|
536
|
+
|
537
|
+
context 'when a record is destroyed' do
|
538
|
+
setup do
|
539
|
+
PaperTrail.whodunnit = 'Charlie'
|
540
|
+
@widget.destroy
|
541
|
+
@version = PaperTrail::Version.last
|
542
|
+
end
|
543
|
+
|
544
|
+
should 'track who made the change' do
|
545
|
+
assert_equal 'Charlie', @version.whodunnit
|
546
|
+
assert_equal 'Bob', @version.originator
|
547
|
+
assert_equal 'Charlie', @version.terminator
|
548
|
+
assert_equal 'Charlie', @widget.originator
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
|
556
|
+
context 'A subclass' do
|
557
|
+
setup do
|
558
|
+
@foo = FooWidget.create
|
559
|
+
@foo.update_attributes! :name => 'Foo'
|
560
|
+
end
|
561
|
+
|
562
|
+
should 'reify with the correct type' do
|
563
|
+
# For some reason this test appears to be broken on AR4 in the test env. Executing it manually in the Rails console seems to work.. not sure what the issues is here.
|
564
|
+
assert_kind_of FooWidget, @foo.versions.last.reify if ActiveRecord::VERSION::STRING.to_f < 4.0
|
565
|
+
assert_equal @foo.versions.first, PaperTrail::Version.last.previous
|
566
|
+
assert_nil PaperTrail::Version.last.next
|
567
|
+
end
|
568
|
+
|
569
|
+
should 'should return the correct originator' do
|
570
|
+
PaperTrail.whodunnit = 'Ben'
|
571
|
+
@foo.update_attribute(:name, 'Geoffrey')
|
572
|
+
assert_equal PaperTrail.whodunnit, @foo.originator
|
573
|
+
end
|
574
|
+
|
575
|
+
context 'when destroyed' do
|
576
|
+
setup { @foo.destroy }
|
577
|
+
|
578
|
+
should 'reify with the correct type' do
|
579
|
+
assert_kind_of FooWidget, @foo.versions.last.reify
|
580
|
+
assert_equal @foo.versions[1], PaperTrail::Version.last.previous
|
581
|
+
assert_nil PaperTrail::Version.last.next
|
582
|
+
end
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
|
587
|
+
context 'An item with versions' do
|
588
|
+
setup do
|
589
|
+
@widget = Widget.create :name => 'Widget'
|
590
|
+
@widget.update_attributes :name => 'Fidget'
|
591
|
+
@widget.update_attributes :name => 'Digit'
|
592
|
+
end
|
593
|
+
|
594
|
+
context 'which were created over time' do
|
595
|
+
setup do
|
596
|
+
@created = 2.days.ago
|
597
|
+
@first_update = 1.day.ago
|
598
|
+
@second_update = 1.hour.ago
|
599
|
+
@widget.versions[0].update_attributes :created_at => @created
|
600
|
+
@widget.versions[1].update_attributes :created_at => @first_update
|
601
|
+
@widget.versions[2].update_attributes :created_at => @second_update
|
602
|
+
@widget.update_attribute :updated_at, @second_update
|
603
|
+
end
|
604
|
+
|
605
|
+
should 'return nil for version_at before it was created' do
|
606
|
+
assert_nil @widget.version_at(@created - 1)
|
607
|
+
end
|
608
|
+
|
609
|
+
should 'return how it looked when created for version_at its creation' do
|
610
|
+
assert_equal 'Widget', @widget.version_at(@created).name
|
611
|
+
end
|
612
|
+
|
613
|
+
should "return how it looked when created for version_at just before its first update" do
|
614
|
+
assert_equal 'Widget', @widget.version_at(@first_update - 1).name
|
615
|
+
end
|
616
|
+
|
617
|
+
should "return how it looked when first updated for version_at its first update" do
|
618
|
+
assert_equal 'Fidget', @widget.version_at(@first_update).name
|
619
|
+
end
|
620
|
+
|
621
|
+
should 'return how it looked when first updated for version_at just before its second update' do
|
622
|
+
assert_equal 'Fidget', @widget.version_at(@second_update - 1).name
|
623
|
+
end
|
624
|
+
|
625
|
+
should 'return how it looked when subsequently updated for version_at its second update' do
|
626
|
+
assert_equal 'Digit', @widget.version_at(@second_update).name
|
627
|
+
end
|
628
|
+
|
629
|
+
should 'return the current object for version_at after latest update' do
|
630
|
+
assert_equal 'Digit', @widget.version_at(1.day.from_now).name
|
631
|
+
end
|
632
|
+
|
633
|
+
context 'passing in a string representation of a timestamp' do
|
634
|
+
should 'still return a widget when appropriate' do
|
635
|
+
# need to add 1 second onto the timestamps before casting to a string, since casting a Time to a string drops the microseconds
|
636
|
+
assert_equal 'Widget', @widget.version_at((@created + 1.second).to_s).name
|
637
|
+
assert_equal 'Fidget', @widget.version_at((@first_update + 1.second).to_s).name
|
638
|
+
assert_equal 'Digit', @widget.version_at((@second_update + 1.second).to_s).name
|
639
|
+
end
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
context '.versions_between' do
|
644
|
+
setup do
|
645
|
+
@created = 30.days.ago
|
646
|
+
@first_update = 15.days.ago
|
647
|
+
@second_update = 1.day.ago
|
648
|
+
@widget.versions[0].update_attributes :created_at => @created
|
649
|
+
@widget.versions[1].update_attributes :created_at => @first_update
|
650
|
+
@widget.versions[2].update_attributes :created_at => @second_update
|
651
|
+
@widget.update_attribute :updated_at, @second_update
|
652
|
+
end
|
653
|
+
|
654
|
+
should 'return versions in the time period' do
|
655
|
+
assert_equal ['Fidget'], @widget.versions_between(20.days.ago, 10.days.ago).map(&:name)
|
656
|
+
assert_equal ['Widget', 'Fidget'], @widget.versions_between(45.days.ago, 10.days.ago).map(&:name)
|
657
|
+
assert_equal ['Fidget', 'Digit'], @widget.versions_between(16.days.ago, 1.minute.ago).map(&:name)
|
658
|
+
assert_equal [], @widget.versions_between(60.days.ago, 45.days.ago).map(&:name)
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
662
|
+
context 'on the first version' do
|
663
|
+
setup { @version = @widget.versions.first }
|
664
|
+
|
665
|
+
should 'have a nil previous version' do
|
666
|
+
assert_nil @version.previous
|
667
|
+
end
|
668
|
+
|
669
|
+
should 'return the next version' do
|
670
|
+
assert_equal @widget.versions[1], @version.next
|
671
|
+
end
|
672
|
+
|
673
|
+
should 'return the correct index' do
|
674
|
+
assert_equal 0, @version.index
|
675
|
+
end
|
676
|
+
end
|
677
|
+
|
678
|
+
context 'on the last version' do
|
679
|
+
setup { @version = @widget.versions.last }
|
680
|
+
|
681
|
+
should 'return the previous version' do
|
682
|
+
assert_equal @widget.versions[@widget.versions.length - 2], @version.previous
|
683
|
+
end
|
684
|
+
|
685
|
+
should 'have a nil next version' do
|
686
|
+
assert_nil @version.next
|
687
|
+
end
|
688
|
+
|
689
|
+
should 'return the correct index' do
|
690
|
+
assert_equal @widget.versions.length - 1, @version.index
|
691
|
+
end
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
|
696
|
+
context 'An item' do
|
697
|
+
setup do
|
698
|
+
@initial_title = 'Foobar'
|
699
|
+
@article = Article.new :title => @initial_title
|
700
|
+
end
|
701
|
+
|
702
|
+
context 'which is created' do
|
703
|
+
setup { @article.save }
|
704
|
+
|
705
|
+
should 'store fixed meta data' do
|
706
|
+
assert_equal 42, @article.versions.last.answer
|
707
|
+
end
|
708
|
+
|
709
|
+
should 'store dynamic meta data which is independent of the item' do
|
710
|
+
assert_equal '31 + 11 = 42', @article.versions.last.question
|
711
|
+
end
|
712
|
+
|
713
|
+
should 'store dynamic meta data which depends on the item' do
|
714
|
+
assert_equal @article.id, @article.versions.last.article_id
|
715
|
+
end
|
716
|
+
|
717
|
+
should 'store dynamic meta data based on a method of the item' do
|
718
|
+
assert_equal @article.action_data_provider_method, @article.versions.last.action
|
719
|
+
end
|
720
|
+
|
721
|
+
should 'store dynamic meta data based on an attribute of the item prior to creation' do
|
722
|
+
assert_equal nil, @article.versions.last.title
|
723
|
+
end
|
724
|
+
|
725
|
+
|
726
|
+
context 'and updated' do
|
727
|
+
setup do
|
728
|
+
@article.update_attributes! :content => 'Better text.', :title => 'Rhubarb'
|
729
|
+
end
|
730
|
+
|
731
|
+
should 'store fixed meta data' do
|
732
|
+
assert_equal 42, @article.versions.last.answer
|
733
|
+
end
|
734
|
+
|
735
|
+
should 'store dynamic meta data which is independent of the item' do
|
736
|
+
assert_equal '31 + 11 = 42', @article.versions.last.question
|
737
|
+
end
|
738
|
+
|
739
|
+
should 'store dynamic meta data which depends on the item' do
|
740
|
+
assert_equal @article.id, @article.versions.last.article_id
|
741
|
+
end
|
742
|
+
|
743
|
+
should 'store dynamic meta data based on an attribute of the item prior to the update' do
|
744
|
+
assert_equal @initial_title, @article.versions.last.title
|
745
|
+
end
|
746
|
+
end
|
747
|
+
|
748
|
+
|
749
|
+
context 'and destroyed' do
|
750
|
+
setup { @article.destroy }
|
751
|
+
|
752
|
+
should 'store fixed meta data' do
|
753
|
+
assert_equal 42, @article.versions.last.answer
|
754
|
+
end
|
755
|
+
|
756
|
+
should 'store dynamic meta data which is independent of the item' do
|
757
|
+
assert_equal '31 + 11 = 42', @article.versions.last.question
|
758
|
+
end
|
759
|
+
|
760
|
+
should 'store dynamic meta data which depends on the item' do
|
761
|
+
assert_equal @article.id, @article.versions.last.article_id
|
762
|
+
end
|
763
|
+
|
764
|
+
should 'store dynamic meta data based on an attribute of the item prior to the destruction' do
|
765
|
+
assert_equal @initial_title, @article.versions.last.title
|
766
|
+
end
|
767
|
+
end
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
context 'A reified item' do
|
772
|
+
setup do
|
773
|
+
widget = Widget.create :name => 'Bob'
|
774
|
+
%w( Tom Dick Jane ).each { |name| widget.update_attributes :name => name }
|
775
|
+
@version = widget.versions.last
|
776
|
+
@widget = @version.reify
|
777
|
+
end
|
778
|
+
|
779
|
+
should 'know which version it came from' do
|
780
|
+
assert_equal @version, @widget.version
|
781
|
+
end
|
782
|
+
|
783
|
+
should 'return its previous self' do
|
784
|
+
assert_equal @widget.versions[-2].reify, @widget.previous_version
|
785
|
+
end
|
786
|
+
|
787
|
+
end
|
788
|
+
|
789
|
+
|
790
|
+
context 'A non-reified item' do
|
791
|
+
setup { @widget = Widget.new }
|
792
|
+
|
793
|
+
should 'not have a previous version' do
|
794
|
+
assert_nil @widget.previous_version
|
795
|
+
end
|
796
|
+
|
797
|
+
should 'not have a next version' do
|
798
|
+
assert_nil @widget.next_version
|
799
|
+
end
|
800
|
+
|
801
|
+
context 'with versions' do
|
802
|
+
setup do
|
803
|
+
@widget.save
|
804
|
+
%w( Tom Dick Jane ).each { |name| @widget.update_attributes :name => name }
|
805
|
+
end
|
806
|
+
|
807
|
+
should 'have a previous version' do
|
808
|
+
assert_equal @widget.versions.last.reify.name, @widget.previous_version.name
|
809
|
+
end
|
810
|
+
|
811
|
+
should 'not have a next version' do
|
812
|
+
assert_nil @widget.next_version
|
813
|
+
end
|
814
|
+
end
|
815
|
+
end
|
816
|
+
|
817
|
+
context 'A reified item' do
|
818
|
+
setup do
|
819
|
+
@widget = Widget.create :name => 'Bob'
|
820
|
+
%w(Tom Dick Jane).each { |name| @widget.update_attributes :name => name }
|
821
|
+
@second_widget = @widget.versions[1].reify # first widget is `nil`
|
822
|
+
@last_widget = @widget.versions.last.reify
|
823
|
+
end
|
824
|
+
|
825
|
+
should 'have a previous version' do
|
826
|
+
assert_nil @second_widget.previous_version # `create` events return `nil` for `reify`
|
827
|
+
assert_equal @widget.versions[-2].reify.name, @last_widget.previous_version.name
|
828
|
+
end
|
829
|
+
|
830
|
+
should 'have a next version' do
|
831
|
+
assert_equal @widget.versions[2].reify.name, @second_widget.next_version.name
|
832
|
+
assert_equal @last_widget.next_version.name, @widget.name
|
833
|
+
end
|
834
|
+
end
|
835
|
+
|
836
|
+
context ":has_many :through" do
|
837
|
+
setup do
|
838
|
+
@book = Book.create :title => 'War and Peace'
|
839
|
+
@dostoyevsky = Person.create :name => 'Dostoyevsky'
|
840
|
+
@solzhenitsyn = Person.create :name => 'Solzhenitsyn'
|
841
|
+
end
|
842
|
+
|
843
|
+
should 'store version on source <<' do
|
844
|
+
count = PaperTrail::Version.count
|
845
|
+
@book.authors << @dostoyevsky
|
846
|
+
assert_equal 1, PaperTrail::Version.count - count
|
847
|
+
assert_equal PaperTrail::Version.last, @book.authorships.first.versions.first
|
848
|
+
end
|
849
|
+
|
850
|
+
should 'store version on source create' do
|
851
|
+
count = PaperTrail::Version.count
|
852
|
+
@book.authors.create :name => 'Tolstoy'
|
853
|
+
assert_equal 2, PaperTrail::Version.count - count
|
854
|
+
assert_same_elements [Person.last, Authorship.last], [PaperTrail::Version.all[-2].item, PaperTrail::Version.last.item]
|
855
|
+
end
|
856
|
+
|
857
|
+
should 'store version on join destroy' do
|
858
|
+
@book.authors << @dostoyevsky
|
859
|
+
count = PaperTrail::Version.count
|
860
|
+
@book.authorships(true).last.destroy
|
861
|
+
assert_equal 1, PaperTrail::Version.count - count
|
862
|
+
assert_equal @book, PaperTrail::Version.last.reify.book
|
863
|
+
assert_equal @dostoyevsky, PaperTrail::Version.last.reify.person
|
864
|
+
end
|
865
|
+
|
866
|
+
should 'store version on join clear' do
|
867
|
+
@book.authors << @dostoyevsky
|
868
|
+
count = PaperTrail::Version.count
|
869
|
+
@book.authorships(true).clear
|
870
|
+
assert_equal 1, PaperTrail::Version.count - count
|
871
|
+
assert_equal @book, PaperTrail::Version.last.reify.book
|
872
|
+
assert_equal @dostoyevsky, PaperTrail::Version.last.reify.person
|
873
|
+
end
|
874
|
+
end
|
875
|
+
|
876
|
+
|
877
|
+
context 'A model with a has_one association' do
|
878
|
+
setup { @widget = Widget.create :name => 'widget_0' }
|
879
|
+
|
880
|
+
context 'before the associated was created' do
|
881
|
+
setup do
|
882
|
+
@widget.update_attributes :name => 'widget_1'
|
883
|
+
@wotsit = @widget.create_wotsit :name => 'wotsit_0'
|
884
|
+
end
|
885
|
+
|
886
|
+
context 'when reified' do
|
887
|
+
setup { @widget_0 = @widget.versions.last.reify(:has_one => 1) }
|
888
|
+
|
889
|
+
should 'see the associated as it was at the time' do
|
890
|
+
assert_nil @widget_0.wotsit
|
891
|
+
end
|
892
|
+
end
|
893
|
+
end
|
894
|
+
|
895
|
+
context 'where the association is created between model versions' do
|
896
|
+
setup do
|
897
|
+
@wotsit = @widget.create_wotsit :name => 'wotsit_0'
|
898
|
+
make_last_version_earlier @wotsit
|
899
|
+
|
900
|
+
@widget.update_attributes :name => 'widget_1'
|
901
|
+
end
|
902
|
+
|
903
|
+
context 'when reified' do
|
904
|
+
setup { @widget_0 = @widget.versions.last.reify(:has_one => 1) }
|
905
|
+
|
906
|
+
should 'see the associated as it was at the time' do
|
907
|
+
assert_equal 'wotsit_0', @widget_0.wotsit.name
|
908
|
+
end
|
909
|
+
end
|
910
|
+
|
911
|
+
context 'and then the associated is updated between model versions' do
|
912
|
+
setup do
|
913
|
+
@wotsit.update_attributes :name => 'wotsit_1'
|
914
|
+
make_last_version_earlier @wotsit
|
915
|
+
@wotsit.update_attributes :name => 'wotsit_2'
|
916
|
+
make_last_version_earlier @wotsit
|
917
|
+
|
918
|
+
@widget.update_attributes :name => 'widget_2'
|
919
|
+
@wotsit.update_attributes :name => 'wotsit_3'
|
920
|
+
end
|
921
|
+
|
922
|
+
context 'when reified' do
|
923
|
+
setup { @widget_1 = @widget.versions.last.reify(:has_one => 1) }
|
924
|
+
|
925
|
+
should 'see the associated as it was at the time' do
|
926
|
+
assert_equal 'wotsit_2', @widget_1.wotsit.name
|
927
|
+
end
|
928
|
+
end
|
929
|
+
|
930
|
+
context 'when reified opting out of has_one reification' do
|
931
|
+
setup { @widget_1 = @widget.versions.last.reify(:has_one => false) }
|
932
|
+
|
933
|
+
should 'see the associated as it is live' do
|
934
|
+
assert_equal 'wotsit_3', @widget_1.wotsit.name
|
935
|
+
end
|
936
|
+
end
|
937
|
+
end
|
938
|
+
|
939
|
+
context 'and then the associated is destroyed between model versions' do
|
940
|
+
setup do
|
941
|
+
@wotsit.destroy
|
942
|
+
make_last_version_earlier @wotsit
|
943
|
+
|
944
|
+
@widget.update_attributes :name => 'widget_3'
|
945
|
+
end
|
946
|
+
|
947
|
+
context 'when reified' do
|
948
|
+
setup { @widget_2 = @widget.versions.last.reify(:has_one => 1) }
|
949
|
+
|
950
|
+
should 'see the associated as it was at the time' do
|
951
|
+
assert_nil @widget_2.wotsit
|
952
|
+
end
|
953
|
+
end
|
954
|
+
end
|
955
|
+
end
|
956
|
+
end
|
957
|
+
|
958
|
+
context 'When an attribute has a custom serializer' do
|
959
|
+
setup { @person = Person.new(:time_zone => "Samoa") }
|
960
|
+
|
961
|
+
should "be an instance of ActiveSupport::TimeZone" do
|
962
|
+
assert_equal ActiveSupport::TimeZone, @person.time_zone.class
|
963
|
+
end
|
964
|
+
|
965
|
+
context 'when the model is saved' do
|
966
|
+
setup do
|
967
|
+
@changes_before_save = @person.changes.dup
|
968
|
+
@person.save!
|
969
|
+
end
|
970
|
+
|
971
|
+
# Test for serialization:
|
972
|
+
should 'version.object_changes should not have stored the default, ridiculously long (to_yaml) serialization of the TimeZone object' do
|
973
|
+
assert @person.versions.last.object_changes.length < 105, "object_changes length was #{@person.versions.last.object_changes.length}"
|
974
|
+
end
|
975
|
+
# It should store the serialized value.
|
976
|
+
should 'version.object_changes attribute should have stored the value returned by the attribute serializer' do
|
977
|
+
as_stored_in_version = HashWithIndifferentAccess[YAML::load(@person.versions.last.object_changes)]
|
978
|
+
assert_equal [nil, 'Samoa'], as_stored_in_version[:time_zone]
|
979
|
+
assert_equal @person.instance_variable_get(:@attributes)['time_zone'].serialized_value, as_stored_in_version[:time_zone].last
|
980
|
+
end
|
981
|
+
|
982
|
+
# Tests for unserialization:
|
983
|
+
should 'version.changeset should convert the attribute value back to its original, unserialized value' do
|
984
|
+
assert_equal @person.instance_variable_get(:@attributes)['time_zone'].unserialized_value, @person.versions.last.changeset[:time_zone].last
|
985
|
+
end
|
986
|
+
should "record.changes (before save) returns the original, unserialized values" do
|
987
|
+
assert_equal [NilClass, ActiveSupport::TimeZone], @changes_before_save[:time_zone].map(&:class)
|
988
|
+
end
|
989
|
+
should 'version.changeset should be the same as record.changes was before the save' do
|
990
|
+
assert_equal @changes_before_save, @person.versions.last.changeset.delete_if { |key, val| key.to_sym == :id }
|
991
|
+
assert_equal [NilClass, ActiveSupport::TimeZone], @person.versions.last.changeset[:time_zone].map(&:class)
|
992
|
+
end
|
993
|
+
|
994
|
+
context 'when that attribute is updated' do
|
995
|
+
setup do
|
996
|
+
@attribute_value_before_change = @person.instance_variable_get(:@attributes)['time_zone']
|
997
|
+
@person.assign_attributes({ :time_zone => 'Pacific Time (US & Canada)' })
|
998
|
+
@changes_before_save = @person.changes.dup
|
999
|
+
@person.save!
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
# Tests for serialization:
|
1003
|
+
# Before the serialized attributes fix, the object/object_changes value that was stored was ridiculously long (58723).
|
1004
|
+
should 'version.object should not have stored the default, ridiculously long (to_yaml) serialization of the TimeZone object' do
|
1005
|
+
assert @person.versions.last.object. length < 105, "object length was #{@person.versions.last.object .length}"
|
1006
|
+
end
|
1007
|
+
# Need an additional clause to detect what version of ActiveRecord is being used for this test because AR4 injects the `updated_at` column into the changeset for updates to models
|
1008
|
+
should 'version.object_changes should not have stored the default, ridiculously long (to_yaml) serialization of the TimeZone object' do
|
1009
|
+
assert @person.versions.last.object_changes.length < (ActiveRecord::VERSION::STRING.to_f < 4.0 ? 105 : 118), "object_changes length was #{@person.versions.last.object_changes.length}"
|
1010
|
+
end
|
1011
|
+
# But now it stores the short, serialized value.
|
1012
|
+
should 'version.object attribute should have stored the value returned by the attribute serializer' do
|
1013
|
+
as_stored_in_version = HashWithIndifferentAccess[YAML::load(@person.versions.last.object)]
|
1014
|
+
assert_equal 'Samoa', as_stored_in_version[:time_zone]
|
1015
|
+
assert_equal @attribute_value_before_change.serialized_value, as_stored_in_version[:time_zone]
|
1016
|
+
end
|
1017
|
+
should 'version.object_changes attribute should have stored the value returned by the attribute serializer' do
|
1018
|
+
as_stored_in_version = HashWithIndifferentAccess[YAML::load(@person.versions.last.object_changes)]
|
1019
|
+
assert_equal ['Samoa', 'Pacific Time (US & Canada)'], as_stored_in_version[:time_zone]
|
1020
|
+
assert_equal @person.instance_variable_get(:@attributes)['time_zone'].serialized_value, as_stored_in_version[:time_zone].last
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
# Tests for unserialization:
|
1024
|
+
should 'version.reify should convert the attribute value back to its original, unserialized value' do
|
1025
|
+
assert_equal @attribute_value_before_change.unserialized_value, @person.versions.last.reify.time_zone
|
1026
|
+
end
|
1027
|
+
should 'version.changeset should convert the attribute value back to its original, unserialized value' do
|
1028
|
+
assert_equal @person.instance_variable_get(:@attributes)['time_zone'].unserialized_value, @person.versions.last.changeset[:time_zone].last
|
1029
|
+
end
|
1030
|
+
should "record.changes (before save) returns the original, unserialized values" do
|
1031
|
+
assert_equal [ActiveSupport::TimeZone, ActiveSupport::TimeZone], @changes_before_save[:time_zone].map(&:class)
|
1032
|
+
end
|
1033
|
+
should 'version.changeset should be the same as record.changes was before the save' do
|
1034
|
+
assert_equal @changes_before_save, @person.versions.last.changeset
|
1035
|
+
assert_equal [ActiveSupport::TimeZone, ActiveSupport::TimeZone], @person.versions.last.changeset[:time_zone].map(&:class)
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
end
|
1039
|
+
end
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
|
1043
|
+
context 'A new model instance which uses a custom PaperTrail::Version class' do
|
1044
|
+
setup { @post = Post.new }
|
1045
|
+
|
1046
|
+
context 'which is then saved' do
|
1047
|
+
setup { @post.save }
|
1048
|
+
should 'change the number of post versions' do assert_equal 1, PostVersion.count end
|
1049
|
+
should 'not change the number of versions' do assert_equal(0, PaperTrail::Version.count) end
|
1050
|
+
end
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
context 'An existing model instance which uses a custom PaperTrail::Version class' do
|
1054
|
+
setup { @post = Post.create }
|
1055
|
+
should 'have one post version' do assert_equal(1, PostVersion.count) end
|
1056
|
+
|
1057
|
+
context 'on the first version' do
|
1058
|
+
setup { @version = @post.versions.first }
|
1059
|
+
|
1060
|
+
should 'have the correct index' do
|
1061
|
+
assert_equal 0, @version.index
|
1062
|
+
end
|
1063
|
+
end
|
1064
|
+
|
1065
|
+
should 'have versions of the custom class' do
|
1066
|
+
assert_equal "PostVersion", @post.versions.first.class.name
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
context 'which is modified' do
|
1070
|
+
setup { @post.update_attributes({ :content => "Some new content" }) }
|
1071
|
+
should 'change the number of post versions' do assert_equal(2, PostVersion.count) end
|
1072
|
+
should 'not change the number of versions' do assert_equal(0, PaperTrail::Version.count) end
|
1073
|
+
should "not have stored changes when object_changes column doesn't exist" do
|
1074
|
+
assert_nil @post.versions.last.changeset
|
1075
|
+
end
|
1076
|
+
end
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
|
1080
|
+
context 'An overwritten default accessor' do
|
1081
|
+
setup do
|
1082
|
+
@song = Song.create :length => 4
|
1083
|
+
@song.update_attributes :length => 5
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
should 'return "overwritten" value on live instance' do
|
1087
|
+
assert_equal 5, @song.length
|
1088
|
+
end
|
1089
|
+
should 'return "overwritten" value on reified instance' do
|
1090
|
+
assert_equal 4, @song.versions.last.reify.length
|
1091
|
+
end
|
1092
|
+
end
|
1093
|
+
|
1094
|
+
|
1095
|
+
context 'An unsaved record' do
|
1096
|
+
setup do
|
1097
|
+
@widget = Widget.new
|
1098
|
+
@widget.destroy
|
1099
|
+
end
|
1100
|
+
should 'not have a version created on destroy' do
|
1101
|
+
assert @widget.versions.empty?
|
1102
|
+
end
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
context 'A model with a custom association' do
|
1106
|
+
setup do
|
1107
|
+
@doc = Document.create
|
1108
|
+
@doc.update_attributes :name => 'Doc 1'
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
should 'not respond to versions method' do
|
1112
|
+
assert !@doc.respond_to?(:versions)
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
should 'create a new version record' do
|
1116
|
+
assert_equal 2, @doc.paper_trail_versions.length
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
should 'respond to `next_version` as normal' do
|
1120
|
+
assert_equal @doc.paper_trail_versions.last.reify.next_version.name, @doc.name
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
should 'respond to `previous_version` as normal' do
|
1124
|
+
@doc.update_attributes :name => 'Doc 2'
|
1125
|
+
assert_equal 3, @doc.paper_trail_versions.length
|
1126
|
+
assert_equal 'Doc 1', @doc.previous_version.name
|
1127
|
+
end
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
context 'The `on` option' do
|
1131
|
+
context 'on create' do
|
1132
|
+
setup do
|
1133
|
+
Fluxor.instance_eval <<-END
|
1134
|
+
has_paper_trail :on => [:create]
|
1135
|
+
END
|
1136
|
+
@fluxor = Fluxor.create
|
1137
|
+
@fluxor.update_attributes :name => 'blah'
|
1138
|
+
@fluxor.destroy
|
1139
|
+
end
|
1140
|
+
should 'only have a version for the create event' do
|
1141
|
+
assert_equal 1, @fluxor.versions.length
|
1142
|
+
assert_equal 'create', @fluxor.versions.last.event
|
1143
|
+
end
|
1144
|
+
end
|
1145
|
+
context 'on update' do
|
1146
|
+
setup do
|
1147
|
+
Fluxor.reset_callbacks :create
|
1148
|
+
Fluxor.reset_callbacks :update
|
1149
|
+
Fluxor.reset_callbacks :destroy
|
1150
|
+
Fluxor.instance_eval <<-END
|
1151
|
+
has_paper_trail :on => [:update]
|
1152
|
+
END
|
1153
|
+
@fluxor = Fluxor.create
|
1154
|
+
@fluxor.update_attributes :name => 'blah'
|
1155
|
+
@fluxor.destroy
|
1156
|
+
end
|
1157
|
+
should 'only have a version for the update event' do
|
1158
|
+
assert_equal 1, @fluxor.versions.length
|
1159
|
+
assert_equal 'update', @fluxor.versions.last.event
|
1160
|
+
end
|
1161
|
+
end
|
1162
|
+
context 'on destroy' do
|
1163
|
+
setup do
|
1164
|
+
Fluxor.reset_callbacks :create
|
1165
|
+
Fluxor.reset_callbacks :update
|
1166
|
+
Fluxor.reset_callbacks :destroy
|
1167
|
+
Fluxor.instance_eval <<-END
|
1168
|
+
has_paper_trail :on => [:destroy]
|
1169
|
+
END
|
1170
|
+
@fluxor = Fluxor.create
|
1171
|
+
@fluxor.update_attributes :name => 'blah'
|
1172
|
+
@fluxor.destroy
|
1173
|
+
end
|
1174
|
+
should 'only have a version for the destroy event' do
|
1175
|
+
assert_equal 1, @fluxor.versions.length
|
1176
|
+
assert_equal 'destroy', @fluxor.versions.last.event
|
1177
|
+
end
|
1178
|
+
end
|
1179
|
+
context 'allows a symbol to be passed' do
|
1180
|
+
setup do
|
1181
|
+
Fluxor.reset_callbacks :create
|
1182
|
+
Fluxor.reset_callbacks :update
|
1183
|
+
Fluxor.reset_callbacks :destroy
|
1184
|
+
Fluxor.instance_eval <<-END
|
1185
|
+
has_paper_trail :on => :create
|
1186
|
+
END
|
1187
|
+
@fluxor = Fluxor.create
|
1188
|
+
@fluxor.update_attributes :name => 'blah'
|
1189
|
+
@fluxor.destroy
|
1190
|
+
end
|
1191
|
+
should 'only have a version for hte create event' do
|
1192
|
+
assert_equal 1, @fluxor.versions.length
|
1193
|
+
assert_equal 'create', @fluxor.versions.last.event
|
1194
|
+
end
|
1195
|
+
end
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
context 'A model with column version and custom version_method' do
|
1199
|
+
setup do
|
1200
|
+
@legacy_widget = LegacyWidget.create(:name => "foo", :version => 2)
|
1201
|
+
end
|
1202
|
+
|
1203
|
+
should 'set version on create' do
|
1204
|
+
assert_equal 2, @legacy_widget.version
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
should 'allow version updates' do
|
1208
|
+
@legacy_widget.update_attributes :version => 3
|
1209
|
+
assert_equal 3, @legacy_widget.version
|
1210
|
+
end
|
1211
|
+
|
1212
|
+
should 'create a new version record' do
|
1213
|
+
assert_equal 1, @legacy_widget.versions.size
|
1214
|
+
end
|
1215
|
+
end
|
1216
|
+
|
1217
|
+
context 'A reified item with a column -version- and custom version_method' do
|
1218
|
+
setup do
|
1219
|
+
widget = LegacyWidget.create(:name => "foo", :version => 2)
|
1220
|
+
%w( bar baz ).each { |name| widget.update_attributes :name => name }
|
1221
|
+
@version = widget.versions.last
|
1222
|
+
@widget = @version.reify
|
1223
|
+
end
|
1224
|
+
|
1225
|
+
should 'know which version it came from' do
|
1226
|
+
assert_equal @version, @widget.custom_version
|
1227
|
+
end
|
1228
|
+
|
1229
|
+
should 'return its previous self' do
|
1230
|
+
assert_equal @widget.versions[-2].reify, @widget.previous_version
|
1231
|
+
end
|
1232
|
+
end
|
1233
|
+
|
1234
|
+
context 'custom events' do
|
1235
|
+
context 'on create' do
|
1236
|
+
setup do
|
1237
|
+
Fluxor.reset_callbacks :create
|
1238
|
+
Fluxor.reset_callbacks :update
|
1239
|
+
Fluxor.reset_callbacks :destroy
|
1240
|
+
Fluxor.instance_eval <<-END
|
1241
|
+
has_paper_trail :on => [:create]
|
1242
|
+
END
|
1243
|
+
@fluxor = Fluxor.new.tap { |model| model.paper_trail_event = 'created' }
|
1244
|
+
@fluxor.update_attributes :name => 'blah'
|
1245
|
+
@fluxor.destroy
|
1246
|
+
end
|
1247
|
+
should 'only have a version for the created event' do
|
1248
|
+
assert_equal 1, @fluxor.versions.length
|
1249
|
+
assert_equal 'created', @fluxor.versions.last.event
|
1250
|
+
end
|
1251
|
+
end
|
1252
|
+
context 'on update' do
|
1253
|
+
setup do
|
1254
|
+
Fluxor.reset_callbacks :create
|
1255
|
+
Fluxor.reset_callbacks :update
|
1256
|
+
Fluxor.reset_callbacks :destroy
|
1257
|
+
Fluxor.instance_eval <<-END
|
1258
|
+
has_paper_trail :on => [:update]
|
1259
|
+
END
|
1260
|
+
@fluxor = Fluxor.create.tap { |model| model.paper_trail_event = 'name_updated' }
|
1261
|
+
@fluxor.update_attributes :name => 'blah'
|
1262
|
+
@fluxor.destroy
|
1263
|
+
end
|
1264
|
+
should 'only have a version for the name_updated event' do
|
1265
|
+
assert_equal 1, @fluxor.versions.length
|
1266
|
+
assert_equal 'name_updated', @fluxor.versions.last.event
|
1267
|
+
end
|
1268
|
+
end
|
1269
|
+
context 'on destroy' do
|
1270
|
+
setup do
|
1271
|
+
Fluxor.reset_callbacks :create
|
1272
|
+
Fluxor.reset_callbacks :update
|
1273
|
+
Fluxor.reset_callbacks :destroy
|
1274
|
+
Fluxor.instance_eval <<-END
|
1275
|
+
has_paper_trail :on => [:destroy]
|
1276
|
+
END
|
1277
|
+
@fluxor = Fluxor.create.tap { |model| model.paper_trail_event = 'destroyed' }
|
1278
|
+
@fluxor.update_attributes :name => 'blah'
|
1279
|
+
@fluxor.destroy
|
1280
|
+
end
|
1281
|
+
should 'only have a version for the destroy event' do
|
1282
|
+
assert_equal 1, @fluxor.versions.length
|
1283
|
+
assert_equal 'destroyed', @fluxor.versions.last.event
|
1284
|
+
end
|
1285
|
+
end
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
context '`PaperTrail::Config.version_limit` set' do
|
1289
|
+
setup do
|
1290
|
+
PaperTrail.config.version_limit = 2
|
1291
|
+
@widget = Widget.create! :name => 'Henry'
|
1292
|
+
6.times { @widget.update_attribute(:name, Faker::Lorem.word) }
|
1293
|
+
end
|
1294
|
+
|
1295
|
+
teardown { PaperTrail.config.version_limit = nil }
|
1296
|
+
|
1297
|
+
should "limit the number of versions to 3 (2 plus the created at event)" do
|
1298
|
+
assert_equal 'create', @widget.versions.first.event
|
1299
|
+
assert_equal 3, @widget.versions.size
|
1300
|
+
end
|
1301
|
+
end
|
1302
|
+
|
1303
|
+
|
1304
|
+
private
|
1305
|
+
|
1306
|
+
# Updates `model`'s last version so it looks like the version was
|
1307
|
+
# created 2 seconds ago.
|
1308
|
+
def make_last_version_earlier(model)
|
1309
|
+
PaperTrail::Version.record_timestamps = false
|
1310
|
+
model.versions.last.update_attributes :created_at => 2.seconds.ago
|
1311
|
+
PaperTrail::Version.record_timestamps = true
|
1312
|
+
end
|
1313
|
+
|
1314
|
+
end
|