paper_trail 4.2.0 → 7.1.3

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