paper_trail 4.0.0 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CONTRIBUTING.md +105 -0
  3. data/.github/ISSUE_TEMPLATE.md +13 -0
  4. data/.gitignore +2 -0
  5. data/.rubocop.yml +100 -0
  6. data/.rubocop_todo.yml +14 -0
  7. data/.travis.yml +11 -10
  8. data/Appraisals +37 -0
  9. data/CHANGELOG.md +173 -8
  10. data/Gemfile +1 -1
  11. data/README.md +641 -470
  12. data/Rakefile +19 -19
  13. data/doc/bug_report_template.rb +71 -0
  14. data/doc/warning_about_not_setting_whodunnit.md +32 -0
  15. data/gemfiles/ar3.gemfile +18 -0
  16. data/gemfiles/ar4.gemfile +7 -0
  17. data/gemfiles/ar5.gemfile +13 -0
  18. data/lib/generators/paper_trail/install_generator.rb +26 -18
  19. data/lib/generators/paper_trail/templates/add_object_changes_to_versions.rb +3 -1
  20. data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb +2 -0
  21. data/lib/generators/paper_trail/templates/create_version_associations.rb +9 -4
  22. data/lib/generators/paper_trail/templates/create_versions.rb +53 -5
  23. data/lib/paper_trail/attribute_serializers/README.md +10 -0
  24. data/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb +58 -0
  25. data/lib/paper_trail/attribute_serializers/legacy_active_record_shim.rb +48 -0
  26. data/lib/paper_trail/attribute_serializers/object_attribute.rb +39 -0
  27. data/lib/paper_trail/attribute_serializers/object_changes_attribute.rb +42 -0
  28. data/lib/paper_trail/cleaner.rb +41 -18
  29. data/lib/paper_trail/config.rb +42 -26
  30. data/lib/paper_trail/frameworks/active_record/models/paper_trail/version.rb +5 -1
  31. data/lib/paper_trail/frameworks/active_record/models/paper_trail/version_association.rb +6 -2
  32. data/lib/paper_trail/frameworks/active_record.rb +2 -2
  33. data/lib/paper_trail/frameworks/cucumber.rb +1 -0
  34. data/lib/paper_trail/frameworks/rails/controller.rb +50 -14
  35. data/lib/paper_trail/frameworks/rails/engine.rb +6 -1
  36. data/lib/paper_trail/frameworks/rails.rb +2 -7
  37. data/lib/paper_trail/frameworks/rspec/helpers.rb +3 -1
  38. data/lib/paper_trail/frameworks/rspec.rb +5 -5
  39. data/lib/paper_trail/frameworks/sinatra.rb +8 -5
  40. data/lib/paper_trail/has_paper_trail.rb +381 -221
  41. data/lib/paper_trail/record_history.rb +57 -0
  42. data/lib/paper_trail/reifier.rb +450 -0
  43. data/lib/paper_trail/serializers/json.rb +7 -7
  44. data/lib/paper_trail/serializers/yaml.rb +31 -12
  45. data/lib/paper_trail/version_association_concern.rb +6 -2
  46. data/lib/paper_trail/version_concern.rb +200 -287
  47. data/lib/paper_trail/version_number.rb +6 -9
  48. data/lib/paper_trail.rb +169 -137
  49. data/paper_trail.gemspec +41 -43
  50. data/spec/generators/install_generator_spec.rb +24 -25
  51. data/spec/generators/paper_trail/templates/create_versions_spec.rb +51 -0
  52. data/spec/models/animal_spec.rb +23 -6
  53. data/spec/models/boolit_spec.rb +8 -8
  54. data/spec/models/callback_modifier_spec.rb +96 -0
  55. data/spec/models/car_spec.rb +13 -0
  56. data/spec/models/fluxor_spec.rb +3 -3
  57. data/spec/models/gadget_spec.rb +19 -19
  58. data/spec/models/joined_version_spec.rb +3 -3
  59. data/spec/models/json_version_spec.rb +50 -28
  60. data/spec/models/kitchen/banana_spec.rb +3 -3
  61. data/spec/models/not_on_update_spec.rb +7 -4
  62. data/spec/models/post_with_status_spec.rb +13 -3
  63. data/spec/models/skipper_spec.rb +40 -11
  64. data/spec/models/thing_spec.rb +4 -4
  65. data/spec/models/truck_spec.rb +5 -0
  66. data/spec/models/vehicle_spec.rb +5 -0
  67. data/spec/models/version_spec.rb +103 -59
  68. data/spec/models/widget_spec.rb +86 -55
  69. data/spec/modules/paper_trail_spec.rb +2 -2
  70. data/spec/modules/version_concern_spec.rb +11 -12
  71. data/spec/modules/version_number_spec.rb +3 -4
  72. data/spec/paper_trail/config_spec.rb +33 -0
  73. data/spec/paper_trail_spec.rb +16 -14
  74. data/spec/rails_helper.rb +10 -9
  75. data/spec/requests/articles_spec.rb +11 -7
  76. data/spec/spec_helper.rb +42 -17
  77. data/spec/support/alt_db_init.rb +8 -13
  78. data/test/custom_json_serializer.rb +3 -3
  79. data/test/dummy/Rakefile +2 -2
  80. data/test/dummy/app/controllers/application_controller.rb +21 -8
  81. data/test/dummy/app/controllers/articles_controller.rb +11 -8
  82. data/test/dummy/app/controllers/widgets_controller.rb +13 -12
  83. data/test/dummy/app/models/animal.rb +1 -1
  84. data/test/dummy/app/models/article.rb +19 -11
  85. data/test/dummy/app/models/authorship.rb +1 -1
  86. data/test/dummy/app/models/bar_habtm.rb +4 -0
  87. data/test/dummy/app/models/book.rb +4 -4
  88. data/test/dummy/app/models/boolit.rb +1 -1
  89. data/test/dummy/app/models/callback_modifier.rb +45 -0
  90. data/test/dummy/app/models/car.rb +3 -0
  91. data/test/dummy/app/models/chapter.rb +9 -0
  92. data/test/dummy/app/models/citation.rb +5 -0
  93. data/test/dummy/app/models/customer.rb +1 -1
  94. data/test/dummy/app/models/document.rb +2 -2
  95. data/test/dummy/app/models/editor.rb +1 -1
  96. data/test/dummy/app/models/foo_habtm.rb +5 -0
  97. data/test/dummy/app/models/fruit.rb +2 -2
  98. data/test/dummy/app/models/gadget.rb +1 -1
  99. data/test/dummy/app/models/kitchen/banana.rb +1 -1
  100. data/test/dummy/app/models/legacy_widget.rb +2 -2
  101. data/test/dummy/app/models/line_item.rb +1 -1
  102. data/test/dummy/app/models/not_on_update.rb +1 -1
  103. data/test/dummy/app/models/paragraph.rb +5 -0
  104. data/test/dummy/app/models/person.rb +6 -6
  105. data/test/dummy/app/models/post.rb +1 -1
  106. data/test/dummy/app/models/post_with_status.rb +1 -1
  107. data/test/dummy/app/models/quotation.rb +5 -0
  108. data/test/dummy/app/models/section.rb +6 -0
  109. data/test/dummy/app/models/skipper.rb +2 -2
  110. data/test/dummy/app/models/song.rb +13 -4
  111. data/test/dummy/app/models/thing.rb +2 -2
  112. data/test/dummy/app/models/translation.rb +2 -2
  113. data/test/dummy/app/models/truck.rb +4 -0
  114. data/test/dummy/app/models/vehicle.rb +4 -0
  115. data/test/dummy/app/models/whatchamajigger.rb +1 -1
  116. data/test/dummy/app/models/widget.rb +7 -6
  117. data/test/dummy/app/versions/joined_version.rb +4 -3
  118. data/test/dummy/app/versions/json_version.rb +1 -1
  119. data/test/dummy/app/versions/kitchen/banana_version.rb +1 -1
  120. data/test/dummy/app/versions/post_version.rb +2 -2
  121. data/test/dummy/config/application.rb +20 -9
  122. data/test/dummy/config/boot.rb +5 -5
  123. data/test/dummy/config/database.postgres.yml +1 -1
  124. data/test/dummy/config/environment.rb +1 -1
  125. data/test/dummy/config/environments/development.rb +4 -3
  126. data/test/dummy/config/environments/production.rb +3 -2
  127. data/test/dummy/config/environments/test.rb +15 -5
  128. data/test/dummy/config/initializers/backtrace_silencers.rb +4 -2
  129. data/test/dummy/config/initializers/paper_trail.rb +4 -3
  130. data/test/dummy/config/initializers/secret_token.rb +3 -1
  131. data/test/dummy/config/initializers/session_store.rb +1 -1
  132. data/test/dummy/config/routes.rb +2 -2
  133. data/test/dummy/config.ru +1 -1
  134. data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +148 -68
  135. data/test/dummy/db/schema.rb +119 -31
  136. data/test/dummy/script/rails +6 -4
  137. data/test/functional/controller_test.rb +34 -35
  138. data/test/functional/enabled_for_controller_test.rb +6 -7
  139. data/test/functional/modular_sinatra_test.rb +43 -38
  140. data/test/functional/sinatra_test.rb +49 -40
  141. data/test/functional/thread_safety_test.rb +4 -6
  142. data/test/paper_trail_test.rb +15 -14
  143. data/test/test_helper.rb +78 -18
  144. data/test/time_travel_helper.rb +1 -15
  145. data/test/unit/associations_test.rb +1016 -0
  146. data/test/unit/cleaner_test.rb +66 -60
  147. data/test/unit/inheritance_column_test.rb +19 -19
  148. data/test/unit/model_test.rb +646 -1071
  149. data/test/unit/protected_attrs_test.rb +19 -14
  150. data/test/unit/serializer_test.rb +44 -43
  151. data/test/unit/serializers/json_test.rb +28 -21
  152. data/test/unit/serializers/mixin_json_test.rb +15 -14
  153. data/test/unit/serializers/mixin_yaml_test.rb +20 -16
  154. data/test/unit/serializers/yaml_test.rb +16 -14
  155. data/test/unit/timestamp_test.rb +10 -12
  156. data/test/unit/version_test.rb +88 -70
  157. metadata +166 -72
  158. data/gemfiles/3.0.gemfile +0 -52
@@ -0,0 +1,96 @@
1
+ require "rails_helper"
2
+
3
+ describe CallbackModifier, type: :model do
4
+ with_versioning do
5
+ describe "callback-methods", versioning: true do
6
+ describe "paper_trail_on_destroy" do
7
+ it "should add :destroy to paper_trail_options[:on]" do
8
+ modifier = NoArgDestroyModifier.create!(some_content: FFaker::Lorem.sentence)
9
+ expect(modifier.paper_trail_options[:on]).to eq [:destroy]
10
+ end
11
+
12
+ context "when :before" do
13
+ it "should create the version before destroy" do
14
+ modifier = BeforeDestroyModifier.create!(some_content: FFaker::Lorem.sentence)
15
+ modifier.test_destroy
16
+ expect(modifier.versions.last.reify).not_to be_flagged_deleted
17
+ end
18
+ end
19
+
20
+ context "when :after" do
21
+ it "should create the version after destroy" do
22
+ modifier = AfterDestroyModifier.create!(some_content: FFaker::Lorem.sentence)
23
+ modifier.test_destroy
24
+ expect(modifier.versions.last.reify).to be_flagged_deleted
25
+ end
26
+ end
27
+
28
+ context "when no argument" do
29
+ it "should default to before destroy" do
30
+ modifier = NoArgDestroyModifier.create!(some_content: FFaker::Lorem.sentence)
31
+ modifier.test_destroy
32
+ expect(modifier.versions.last.reify).not_to be_flagged_deleted
33
+ end
34
+ end
35
+ end
36
+
37
+ describe "paper_trail_on_update" do
38
+ it "should add :update to paper_trail_options[:on]" do
39
+ modifier = UpdateModifier.create!(some_content: FFaker::Lorem.sentence)
40
+ expect(modifier.paper_trail_options[:on]).to eq [:update]
41
+ end
42
+
43
+ it "should create a version" do
44
+ modifier = UpdateModifier.create!(some_content: FFaker::Lorem.sentence)
45
+ modifier.update_attributes! some_content: "modified"
46
+ expect(modifier.versions.last.event).to eq "update"
47
+ end
48
+ end
49
+
50
+ describe "paper_trail_on_create" do
51
+ it "should add :create to paper_trail_options[:on]" do
52
+ modifier = CreateModifier.create!(some_content: FFaker::Lorem.sentence)
53
+ expect(modifier.paper_trail_options[:on]).to eq [:create]
54
+ end
55
+
56
+ it "should create a version" do
57
+ modifier = CreateModifier.create!(some_content: FFaker::Lorem.sentence)
58
+ expect(modifier.versions.last.event).to eq "create"
59
+ end
60
+ end
61
+
62
+ context "when no callback-method used" do
63
+ it "should set paper_trail_options[:on] to [:create, :update, :destroy]" do
64
+ modifier = DefaultModifier.create!(some_content: FFaker::Lorem.sentence)
65
+ expect(modifier.paper_trail_options[:on]).to eq [:create, :update, :destroy]
66
+ end
67
+
68
+ it "should default to track destroy" do
69
+ modifier = DefaultModifier.create!(some_content: FFaker::Lorem.sentence)
70
+ modifier.destroy
71
+ expect(modifier.versions.last.event).to eq "destroy"
72
+ end
73
+
74
+ it "should default to track update" do
75
+ modifier = DefaultModifier.create!(some_content: FFaker::Lorem.sentence)
76
+ modifier.update_attributes! some_content: "modified"
77
+ expect(modifier.versions.last.event).to eq "update"
78
+ end
79
+
80
+ it "should default to track create" do
81
+ modifier = DefaultModifier.create!(some_content: FFaker::Lorem.sentence)
82
+ expect(modifier.versions.last.event).to eq "create"
83
+ end
84
+ end
85
+
86
+ context "when only one callback-method" do
87
+ it "does only track the corresponding event" do
88
+ modifier = CreateModifier.create!(some_content: FFaker::Lorem.sentence)
89
+ modifier.update_attributes!(some_content: "modified")
90
+ modifier.test_destroy
91
+ expect(modifier.versions.collect(&:event)).to eq ["create"]
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,13 @@
1
+ require "rails_helper"
2
+
3
+ describe Car, type: :model do
4
+ it { is_expected.to be_versioned }
5
+
6
+ describe "changeset", versioning: true do
7
+ it "has the expected keys (see issue 738)" do
8
+ car = Car.create!(name: "Alice")
9
+ car.update_attributes(name: "Bob")
10
+ assert_includes car.versions.last.changeset.keys, "name"
11
+ end
12
+ end
13
+ end
@@ -1,7 +1,7 @@
1
- require 'rails_helper'
1
+ require "rails_helper"
2
2
 
3
- describe Fluxor, :type => :model do
4
- describe '`be_versioned` matcher' do
3
+ describe Fluxor, type: :model do
4
+ describe "`be_versioned` matcher" do
5
5
  it { is_expected.to_not be_versioned }
6
6
  end
7
7
 
@@ -1,36 +1,34 @@
1
- require 'rails_helper'
1
+ require "rails_helper"
2
2
 
3
- describe Gadget, :type => :model do
3
+ describe Gadget, type: :model do
4
4
  it { is_expected.to be_versioned }
5
5
 
6
- let(:gadget) { Gadget.create!(:name => 'Wrench', :brand => 'Acme') }
6
+ let(:gadget) { Gadget.create!(name: "Wrench", brand: "Acme") }
7
7
 
8
- describe "updates", :versioning => true do
8
+ describe "updates", versioning: true do
9
9
  it "should generate a version for updates to `name` attribute" do
10
- expect { gadget.update_attribute(:name, 'Hammer').to change{gadget.versions.size}.by(1) }
10
+ expect { gadget.update_attribute(:name, "Hammer").to change { gadget.versions.size }.by(1) }
11
11
  end
12
12
 
13
13
  it "should ignore for updates to `brand` attribute" do
14
- expect { gadget.update_attribute(:brand, 'Stanley') }.to_not change{gadget.versions.size}
14
+ expect { gadget.update_attribute(:brand, "Stanley") }.to_not change { gadget.versions.size }
15
15
  end
16
16
 
17
17
  it "should still generate a version when only the `updated_at` attribute is updated" do
18
- expect { gadget.update_attribute(:updated_at, Time.now) }.to change{gadget.versions.size}.by(1)
18
+ # Plus 1 second because MySQL lacks sub-second resolution
19
+ expect {
20
+ gadget.update_attribute(:updated_at, Time.now + 1)
21
+ }.to change { gadget.versions.size }.by(1)
19
22
  end
20
23
  end
21
24
 
22
25
  describe "Methods" do
23
- describe "Instance", :versioning => true do
26
+ describe "Instance", versioning: true do
24
27
  describe "private" do
25
28
  describe '#changed_notably?' do
26
- subject { Gadget.new(:created_at => Time.now) }
29
+ subject { Gadget.new(created_at: Time.now) }
27
30
 
28
- # apparently the private methods list in Ruby18 is different than in Ruby19+
29
- if RUBY_VERSION >= '1.9'
30
- it { expect(subject.private_methods).to include(:changed_notably?) }
31
- else
32
- it { expect(subject.private_methods).to include('changed_notably?') }
33
- end
31
+ it { expect(subject.private_methods).to include(:changed_notably?) }
34
32
 
35
33
  context "create events" do
36
34
  it { expect(subject.send(:changed_notably?)).to be true }
@@ -41,24 +39,26 @@ describe Gadget, :type => :model do
41
39
 
42
40
  context "without update timestamps" do
43
41
  it "should only acknowledge non-ignored attrs" do
44
- subject.name = 'Wrench'
42
+ subject.name = "Wrench"
45
43
  expect(subject.send(:changed_notably?)).to be true
46
44
  end
47
45
 
48
46
  it "should not acknowledge ignored attr (brand)" do
49
- subject.brand = 'Acme'
47
+ subject.brand = "Acme"
50
48
  expect(subject.send(:changed_notably?)).to be false
51
49
  end
52
50
  end
53
51
 
54
52
  context "with update timestamps" do
55
53
  it "should only acknowledge non-ignored attrs" do
56
- subject.name, subject.updated_at = 'Wrench', Time.now
54
+ subject.name = "Wrench"
55
+ subject.updated_at = Time.now
57
56
  expect(subject.send(:changed_notably?)).to be true
58
57
  end
59
58
 
60
59
  it "should not acknowledge ignored attrs and timestamps only" do
61
- subject.brand, subject.updated_at = 'Acme', Time.now
60
+ subject.brand = "Acme"
61
+ subject.updated_at = Time.now
62
62
  expect(subject.send(:changed_notably?)).to be false
63
63
  end
64
64
  end
@@ -1,9 +1,9 @@
1
- require 'rails_helper'
1
+ require "rails_helper"
2
2
 
3
- describe JoinedVersion, :type => :model, :versioning => true do
3
+ describe JoinedVersion, type: :model, versioning: true do
4
4
  it { expect(JoinedVersion.superclass).to be PaperTrail::Version }
5
5
 
6
- let(:widget) { Widget.create!(:name => Faker::Name.name) }
6
+ let(:widget) { Widget.create!(name: FFaker::Name.name) }
7
7
  let(:version) { JoinedVersion.first }
8
8
 
9
9
  describe "Scopes" do
@@ -1,18 +1,29 @@
1
- if JsonVersion.table_exists?
1
+ require "rails_helper"
2
2
 
3
- require 'rails_helper'
3
+ # The `json_versions` table tests postgres' `json` data type. So, that
4
+ # table is only created when testing against postgres and ActiveRecord >= 4.
5
+ if JsonVersion.table_exists?
4
6
 
5
- describe JsonVersion, :type => :model do
7
+ describe JsonVersion, type: :model do
6
8
  it "should include the `VersionConcern` module to get base functionality" do
7
9
  expect(JsonVersion).to include(PaperTrail::VersionConcern)
8
10
  end
9
11
 
10
12
  describe "Methods" do
11
13
  describe "Class" do
12
-
13
14
  describe '#where_object' do
14
15
  it { expect(JsonVersion).to respond_to(:where_object) }
15
16
 
17
+ it "escapes values" do
18
+ f = Fruit.create(name: "Bobby")
19
+ expect(
20
+ f.
21
+ versions.
22
+ where_object(name: "Robert'; DROP TABLE Students;--").
23
+ count
24
+ ).to eq(0)
25
+ end
26
+
16
27
  context "invalid arguments" do
17
28
  it "should raise an error" do
18
29
  expect { JsonVersion.where_object(:foo) }.to raise_error(ArgumentError)
@@ -20,21 +31,21 @@ if JsonVersion.table_exists?
20
31
  end
21
32
  end
22
33
 
23
- context "valid arguments", :versioning => true do
34
+ context "valid arguments", versioning: true do
24
35
  let(:fruit_names) { %w(apple orange lemon banana lime coconut strawberry blueberry) }
25
36
  let(:fruit) { Fruit.new }
26
- let(:name) { 'pomegranate' }
27
- let(:color) { Faker::Color.name }
37
+ let(:name) { "pomegranate" }
38
+ let(:color) { FFaker::Color.name }
28
39
 
29
40
  before do
30
- fruit.update_attributes!(:name => name)
31
- fruit.update_attributes!(:name => fruit_names.sample, :color => color)
32
- fruit.update_attributes!(:name => fruit_names.sample, :color => Faker::Color.name)
41
+ fruit.update_attributes!(name: name)
42
+ fruit.update_attributes!(name: fruit_names.sample, color: color)
43
+ fruit.update_attributes!(name: fruit_names.sample, color: FFaker::Color.name)
33
44
  end
34
45
 
35
46
  it "should be able to locate versions according to their `object` contents" do
36
- expect(JsonVersion.where_object(:name => name)).to eq([fruit.versions[1]])
37
- expect(JsonVersion.where_object(:color => color)).to eq([fruit.versions[2]])
47
+ expect(JsonVersion.where_object(name: name)).to eq([fruit.versions[1]])
48
+ expect(JsonVersion.where_object(color: color)).to eq([fruit.versions[2]])
38
49
  end
39
50
  end
40
51
  end
@@ -42,6 +53,16 @@ if JsonVersion.table_exists?
42
53
  describe '#where_object_changes' do
43
54
  it { expect(JsonVersion).to respond_to(:where_object_changes) }
44
55
 
56
+ it "escapes values" do
57
+ f = Fruit.create(name: "Bobby")
58
+ expect(
59
+ f.
60
+ versions.
61
+ where_object_changes(name: "Robert'; DROP TABLE Students;--").
62
+ count
63
+ ).to eq(0)
64
+ end
65
+
45
66
  context "invalid arguments" do
46
67
  it "should raise an error" do
47
68
  expect { JsonVersion.where_object_changes(:foo) }.to raise_error(ArgumentError)
@@ -49,32 +70,33 @@ if JsonVersion.table_exists?
49
70
  end
50
71
  end
51
72
 
52
- context "valid arguments", :versioning => true do
53
- let(:fruit_names) { %w(apple orange lemon banana lime strawberry blueberry) }
54
- let(:tropical_fruit_names) { %w(coconut pineapple kiwi mango melon) }
55
- let(:fruit) { Fruit.new }
56
- let(:name) { 'pomegranate' }
57
- let(:color) { Faker::Color.name }
73
+ context "valid arguments", versioning: true do
74
+ let(:color) { %w(red green) }
75
+ let(:fruit) { Fruit.create!(name: name[0]) }
76
+ let(:name) { %w(banana kiwi mango) }
58
77
 
59
78
  before do
60
- fruit.update_attributes!(:name => name)
61
- fruit.update_attributes!(:name => tropical_fruit_names.sample, :color => color)
62
- fruit.update_attributes!(:name => fruit_names.sample, :color => Faker::Color.name)
79
+ fruit.update_attributes!(name: name[1], color: color[0])
80
+ fruit.update_attributes!(name: name[2], color: color[1])
63
81
  end
64
82
 
65
- it "should be able to locate versions according to their `object_changes` contents" do
66
- expect(fruit.versions.where_object_changes(:name => name)).to eq(fruit.versions[0..1])
67
- expect(fruit.versions.where_object_changes(:color => color)).to eq(fruit.versions[1..2])
83
+ it "finds versions according to their `object_changes` contents" do
84
+ expect(
85
+ fruit.versions.where_object_changes(name: name[0])
86
+ ).to match_array(fruit.versions[0..1])
87
+ expect(
88
+ fruit.versions.where_object_changes(color: color[0])
89
+ ).to match_array(fruit.versions[1..2])
68
90
  end
69
91
 
70
- it "should be able to handle queries for multiple attributes" do
71
- expect(fruit.versions.where_object_changes(:color => color, :name => name)).to eq([fruit.versions[1]])
92
+ it "finds versions with multiple attributes changed" do
93
+ expect(
94
+ fruit.versions.where_object_changes(color: color[0], name: name[0])
95
+ ).to match_array([fruit.versions[1]])
72
96
  end
73
97
  end
74
98
  end
75
99
  end
76
-
77
100
  end
78
101
  end
79
-
80
102
  end
@@ -1,11 +1,11 @@
1
- require 'rails_helper'
1
+ require "rails_helper"
2
2
 
3
3
  module Kitchen
4
- describe Banana, :type => :model do
4
+ describe Banana, type: :model do
5
5
  it { is_expected.to be_versioned }
6
6
 
7
7
  describe '#versions' do
8
- it "returns instances of Kitchen::BananaVersion", :versioning => true do
8
+ it "returns instances of Kitchen::BananaVersion", versioning: true do
9
9
  banana = described_class.create!
10
10
  expect(banana.versions.first).to be_a(Kitchen::BananaVersion)
11
11
  end
@@ -1,7 +1,7 @@
1
- require 'rails_helper'
1
+ require "rails_helper"
2
2
 
3
- describe NotOnUpdate, :type => :model do
4
- describe "#touch_with_version", :versioning => true do
3
+ describe NotOnUpdate, type: :model do
4
+ describe "#touch_with_version", versioning: true do
5
5
  let!(:record) { described_class.create! }
6
6
 
7
7
  it "should create a version, regardless" do
@@ -12,7 +12,10 @@ describe NotOnUpdate, :type => :model do
12
12
 
13
13
  it "increments the `:updated_at` timestamp" do
14
14
  before = record.updated_at
15
- record.touch_with_version
15
+ # Travel 1 second because MySQL lacks sub-second resolution
16
+ Timecop.travel(1) do
17
+ record.touch_with_version
18
+ end
16
19
  expect(record.updated_at).to be > before
17
20
  end
18
21
  end
@@ -1,17 +1,27 @@
1
- require 'rails_helper'
1
+ require "rails_helper"
2
2
 
3
3
  # This model is in the test suite soley for the purpose of testing ActiveRecord::Enum,
4
4
  # which is available in ActiveRecord4+ only
5
- describe PostWithStatus, :type => :model do
5
+ describe PostWithStatus, type: :model do
6
6
  if defined?(ActiveRecord::Enum)
7
7
  with_versioning do
8
- let(:post) { PostWithStatus.create!(:status => 'draft') }
8
+ let(:post) { PostWithStatus.create!(status: "draft") }
9
9
 
10
10
  it "should stash the enum value properly in versions" do
11
11
  post.published!
12
12
  post.archived!
13
13
  expect(post.previous_version.published?).to be true
14
14
  end
15
+
16
+ context "storing enum object_changes" do
17
+ subject { post.versions.last }
18
+
19
+ it "should stash the enum value properly in versions object_changes" do
20
+ post.published!
21
+ post.archived!
22
+ expect(subject.changeset["status"]).to eql %w(published archived)
23
+ end
24
+ end
15
25
  end
16
26
  end
17
27
  end
@@ -1,16 +1,45 @@
1
- require 'rails_helper'
1
+ require "rails_helper"
2
2
 
3
- describe Skipper, :type => :model do
4
- describe "#update_attributes!", :versioning => true do
5
- context "updating a skipped attribute" do
6
- let(:t1) { Time.zone.local(2015, 7, 15, 20, 34, 0) }
7
- let(:t2) { Time.zone.local(2015, 7, 15, 20, 34, 30) }
3
+ describe Skipper, type: :model do
4
+ with_versioning do
5
+ it { is_expected.to be_versioned }
8
6
 
9
- it "should not create a version" do
10
- skipper = Skipper.create!(:another_timestamp => t1)
11
- expect {
12
- skipper.update_attributes!(:another_timestamp => t2)
13
- }.to_not change { skipper.versions.length }
7
+ describe "#update_attributes!", versioning: true do
8
+ context "updating a skipped attribute" do
9
+ let(:t1) { Time.zone.local(2015, 7, 15, 20, 34, 0) }
10
+ let(:t2) { Time.zone.local(2015, 7, 15, 20, 34, 30) }
11
+
12
+ it "should not create a version" do
13
+ skipper = Skipper.create!(another_timestamp: t1)
14
+ expect {
15
+ skipper.update_attributes!(another_timestamp: t2)
16
+ }.to_not change { skipper.versions.length }
17
+ end
18
+ end
19
+ end
20
+
21
+ describe "reify" do
22
+ context "reifying a with a skipped attribute" do
23
+ let(:t1) { Time.zone.local(2015, 7, 15, 20, 34, 0) }
24
+ let(:t2) { Time.zone.local(2015, 7, 15, 20, 34, 30) }
25
+
26
+ context "without preserve (default)" do
27
+ it "should have no timestamp" do
28
+ skipper = Skipper.create!(another_timestamp: t1)
29
+ skipper.update_attributes!(another_timestamp: t2, name: "Foobar")
30
+ skipper = skipper.versions.last.reify
31
+ expect(skipper.another_timestamp).to be(nil)
32
+ end
33
+ end
34
+
35
+ context "with preserve" do
36
+ it "should preserve its timestamp" do
37
+ skipper = Skipper.create!(another_timestamp: t1)
38
+ skipper.update_attributes!(another_timestamp: t2, name: "Foobar")
39
+ skipper = skipper.versions.last.reify(unversioned_attributes: :preserve)
40
+ expect(skipper.another_timestamp).to eq(t2)
41
+ end
42
+ end
14
43
  end
15
44
  end
16
45
  end
@@ -1,10 +1,10 @@
1
- require 'rails_helper'
1
+ require "rails_helper"
2
2
 
3
- describe Thing, :type => :model do
3
+ describe Thing, type: :model do
4
4
  it { is_expected.to be_versioned }
5
5
 
6
- describe "should not store object_changes", :versioning => true do
7
- let(:thing) { Thing.create(:name =>"pencil") }
6
+ describe "should not store object_changes", versioning: true do
7
+ let(:thing) { Thing.create(name: "pencil") }
8
8
 
9
9
  it { expect(thing.versions.last.object_changes).to be_nil }
10
10
  end
@@ -0,0 +1,5 @@
1
+ require "rails_helper"
2
+
3
+ describe Truck, type: :model do
4
+ it { is_expected.to_not be_versioned }
5
+ end
@@ -0,0 +1,5 @@
1
+ require "rails_helper"
2
+
3
+ describe Vehicle, type: :model do
4
+ it { is_expected.to_not be_versioned }
5
+ end