paper_trail 3.0.6 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -0
  3. data/.rspec +1 -2
  4. data/.travis.yml +14 -5
  5. data/CHANGELOG.md +215 -8
  6. data/CONTRIBUTING.md +84 -0
  7. data/README.md +922 -502
  8. data/Rakefile +2 -2
  9. data/doc/bug_report_template.rb +65 -0
  10. data/gemfiles/ar3.gemfile +61 -0
  11. data/lib/generators/paper_trail/install_generator.rb +22 -3
  12. data/lib/generators/paper_trail/templates/add_object_changes_to_versions.rb +6 -1
  13. data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb +11 -0
  14. data/lib/generators/paper_trail/templates/create_version_associations.rb +17 -0
  15. data/lib/generators/paper_trail/templates/create_versions.rb +22 -1
  16. data/lib/paper_trail.rb +52 -22
  17. data/lib/paper_trail/attributes_serialization.rb +89 -0
  18. data/lib/paper_trail/cleaner.rb +32 -15
  19. data/lib/paper_trail/config.rb +35 -2
  20. data/lib/paper_trail/frameworks/active_record.rb +4 -5
  21. data/lib/paper_trail/frameworks/active_record/models/paper_trail/version_association.rb +7 -0
  22. data/lib/paper_trail/frameworks/rails.rb +1 -0
  23. data/lib/paper_trail/frameworks/rails/controller.rb +27 -11
  24. data/lib/paper_trail/frameworks/rspec.rb +5 -0
  25. data/lib/paper_trail/frameworks/sinatra.rb +3 -1
  26. data/lib/paper_trail/has_paper_trail.rb +304 -148
  27. data/lib/paper_trail/record_history.rb +59 -0
  28. data/lib/paper_trail/reifier.rb +270 -0
  29. data/lib/paper_trail/serializers/json.rb +13 -2
  30. data/lib/paper_trail/serializers/yaml.rb +16 -2
  31. data/lib/paper_trail/version_association_concern.rb +15 -0
  32. data/lib/paper_trail/version_concern.rb +160 -122
  33. data/lib/paper_trail/version_number.rb +3 -3
  34. data/paper_trail.gemspec +22 -9
  35. data/spec/generators/install_generator_spec.rb +4 -4
  36. data/spec/models/animal_spec.rb +36 -0
  37. data/spec/models/boolit_spec.rb +48 -0
  38. data/spec/models/callback_modifier_spec.rb +96 -0
  39. data/spec/models/fluxor_spec.rb +19 -0
  40. data/spec/models/gadget_spec.rb +14 -12
  41. data/spec/models/joined_version_spec.rb +9 -9
  42. data/spec/models/json_version_spec.rb +103 -0
  43. data/spec/models/kitchen/banana_spec.rb +14 -0
  44. data/spec/models/not_on_update_spec.rb +19 -0
  45. data/spec/models/post_with_status_spec.rb +3 -3
  46. data/spec/models/skipper_spec.rb +46 -0
  47. data/spec/models/thing_spec.rb +11 -0
  48. data/spec/models/version_spec.rb +195 -44
  49. data/spec/models/widget_spec.rb +136 -76
  50. data/spec/modules/paper_trail_spec.rb +27 -0
  51. data/spec/modules/version_concern_spec.rb +8 -8
  52. data/spec/modules/version_number_spec.rb +16 -16
  53. data/spec/paper_trail/config_spec.rb +52 -0
  54. data/spec/paper_trail_spec.rb +17 -17
  55. data/spec/rails_helper.rb +34 -0
  56. data/spec/requests/articles_spec.rb +10 -14
  57. data/spec/spec_helper.rb +81 -34
  58. data/spec/support/alt_db_init.rb +1 -1
  59. data/test/dummy/app/controllers/application_controller.rb +1 -1
  60. data/test/dummy/app/controllers/articles_controller.rb +4 -1
  61. data/test/dummy/app/models/animal.rb +2 -0
  62. data/test/dummy/app/models/book.rb +4 -0
  63. data/test/dummy/app/models/boolit.rb +4 -0
  64. data/test/dummy/app/models/callback_modifier.rb +45 -0
  65. data/test/dummy/app/models/chapter.rb +9 -0
  66. data/test/dummy/app/models/citation.rb +5 -0
  67. data/test/dummy/app/models/customer.rb +4 -0
  68. data/test/dummy/app/models/editor.rb +4 -0
  69. data/test/dummy/app/models/editorship.rb +5 -0
  70. data/test/dummy/app/models/fruit.rb +5 -0
  71. data/test/dummy/app/models/kitchen/banana.rb +5 -0
  72. data/test/dummy/app/models/line_item.rb +4 -0
  73. data/test/dummy/app/models/not_on_update.rb +4 -0
  74. data/test/dummy/app/models/order.rb +5 -0
  75. data/test/dummy/app/models/paragraph.rb +5 -0
  76. data/test/dummy/app/models/person.rb +13 -3
  77. data/test/dummy/app/models/post.rb +0 -1
  78. data/test/dummy/app/models/quotation.rb +5 -0
  79. data/test/dummy/app/models/section.rb +6 -0
  80. data/test/dummy/app/models/skipper.rb +6 -0
  81. data/test/dummy/app/models/song.rb +20 -0
  82. data/test/dummy/app/models/thing.rb +3 -0
  83. data/test/dummy/app/models/whatchamajigger.rb +4 -0
  84. data/test/dummy/app/models/widget.rb +5 -0
  85. data/test/dummy/app/versions/json_version.rb +3 -0
  86. data/test/dummy/app/versions/kitchen/banana_version.rb +5 -0
  87. data/test/dummy/config/application.rb +6 -0
  88. data/test/dummy/config/database.postgres.yml +1 -1
  89. data/test/dummy/config/environments/test.rb +5 -1
  90. data/test/dummy/config/initializers/paper_trail.rb +6 -1
  91. data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +143 -3
  92. data/test/dummy/db/schema.rb +169 -25
  93. data/test/functional/controller_test.rb +4 -2
  94. data/test/functional/modular_sinatra_test.rb +1 -1
  95. data/test/functional/sinatra_test.rb +1 -1
  96. data/test/paper_trail_test.rb +7 -0
  97. data/test/test_helper.rb +38 -2
  98. data/test/time_travel_helper.rb +15 -0
  99. data/test/unit/associations_test.rb +726 -0
  100. data/test/unit/inheritance_column_test.rb +6 -6
  101. data/test/unit/model_test.rb +109 -125
  102. data/test/unit/protected_attrs_test.rb +4 -3
  103. data/test/unit/serializer_test.rb +6 -6
  104. data/test/unit/serializers/json_test.rb +17 -4
  105. data/test/unit/serializers/yaml_test.rb +5 -1
  106. data/test/unit/version_test.rb +87 -69
  107. metadata +172 -75
  108. data/gemfiles/3.0.gemfile +0 -42
  109. data/test/dummy/public/404.html +0 -26
  110. data/test/dummy/public/422.html +0 -26
  111. data/test/dummy/public/500.html +0 -26
  112. data/test/dummy/public/favicon.ico +0 -0
  113. data/test/dummy/public/javascripts/application.js +0 -2
  114. data/test/dummy/public/javascripts/controls.js +0 -965
  115. data/test/dummy/public/javascripts/dragdrop.js +0 -974
  116. data/test/dummy/public/javascripts/effects.js +0 -1123
  117. data/test/dummy/public/javascripts/prototype.js +0 -6001
  118. data/test/dummy/public/javascripts/rails.js +0 -175
  119. data/test/dummy/public/stylesheets/.gitkeep +0 -0
@@ -1,8 +1,8 @@
1
1
  module PaperTrail
2
2
  module VERSION
3
- MAJOR = 3
4
- MINOR = 0
5
- TINY = 6
3
+ MAJOR = 4
4
+ MINOR = 2
5
+ TINY = 0
6
6
  PRE = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
data/paper_trail.gemspec CHANGED
@@ -19,28 +19,41 @@ Gem::Specification.new do |s|
19
19
 
20
20
  s.required_rubygems_version = '>= 1.3.6'
21
21
 
22
- s.add_dependency 'activerecord', ['>= 3.0', '< 5.0']
23
- s.add_dependency 'activesupport', ['>= 3.0', '< 5.0']
22
+ s.add_dependency 'activerecord', ['>= 3.0', '< 6.0']
23
+ s.add_dependency 'activesupport', ['>= 3.0', '< 6.0']
24
+ s.add_dependency 'request_store', '~> 1.1'
24
25
 
25
26
  s.add_development_dependency 'rake', '~> 10.1.1'
26
27
  s.add_development_dependency 'shoulda', '~> 3.5'
27
28
  # s.add_development_dependency 'shoulda-matchers', '~> 1.5' # needed for ActiveRecord < 4
28
- s.add_development_dependency 'ffaker', '>= 1.15'
29
+ s.add_development_dependency 'ffaker', '<= 1.31.0'
29
30
  s.add_development_dependency 'railties', ['>= 3.0', '< 5.0']
30
31
  s.add_development_dependency 'sinatra', '~> 1.0'
31
32
  s.add_development_dependency 'rack-test', '>= 0.6'
32
- s.add_development_dependency 'rspec-rails', '~> 2.14'
33
+ s.add_development_dependency 'rspec-rails', '~> 3.1.0'
33
34
  s.add_development_dependency 'generator_spec'
34
35
  s.add_development_dependency 'database_cleaner', '~> 1.2'
36
+ s.add_development_dependency 'pry-nav'
35
37
 
36
- # JRuby support for the test ENV
37
- unless defined?(JRUBY_VERSION)
38
- s.add_development_dependency 'sqlite3', '~> 1.2'
39
- s.add_development_dependency 'mysql2', '~> 0.3'
40
- s.add_development_dependency 'pg', '~> 0.17'
38
+ # Allow time travel in testing. timecop is only supported after 1.9.2 but does a better cleanup at 'return'
39
+ if RUBY_VERSION < "1.9.2"
40
+ s.add_development_dependency 'delorean'
41
41
  else
42
+ s.add_development_dependency 'timecop'
43
+ end
44
+
45
+ if defined?(JRUBY_VERSION)
42
46
  s.add_development_dependency 'activerecord-jdbcsqlite3-adapter', '~> 1.3'
43
47
  s.add_development_dependency 'activerecord-jdbcpostgresql-adapter', '~> 1.3'
44
48
  s.add_development_dependency 'activerecord-jdbcmysql-adapter', '~> 1.3'
49
+ else
50
+ s.add_development_dependency 'sqlite3', '~> 1.2'
51
+
52
+ # We would prefer to only constrain mysql2 to '~> 0.3',
53
+ # but a rails bug (https://github.com/rails/rails/issues/21544)
54
+ # requires us to constrain to '~> 0.3.20' for now.
55
+ s.add_development_dependency 'mysql2', '~> 0.3.20'
56
+
57
+ s.add_development_dependency 'pg', '~> 0.17'
45
58
  end
46
59
  end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require 'rails_helper'
2
2
  require 'generator_spec/test_case'
3
3
  require File.expand_path('../../../lib/generators/paper_trail/install_generator', __FILE__)
4
4
 
@@ -15,7 +15,7 @@ describe PaperTrail::InstallGenerator, :type => :generator do
15
15
  end
16
16
 
17
17
  it "generates a migration for creating the 'versions' table" do
18
- destination_root.should have_structure {
18
+ expect(destination_root).to have_structure {
19
19
  directory 'db' do
20
20
  directory 'migrate' do
21
21
  migration 'create_versions' do
@@ -36,7 +36,7 @@ describe PaperTrail::InstallGenerator, :type => :generator do
36
36
  end
37
37
 
38
38
  it "generates a migration for creating the 'versions' table" do
39
- destination_root.should have_structure {
39
+ expect(destination_root).to have_structure {
40
40
  directory 'db' do
41
41
  directory 'migrate' do
42
42
  migration 'create_versions' do
@@ -50,7 +50,7 @@ describe PaperTrail::InstallGenerator, :type => :generator do
50
50
  end
51
51
 
52
52
  it "generates a migration for adding the 'object_changes' column to the 'versions' table" do
53
- destination_root.should have_structure {
53
+ expect(destination_root).to have_structure {
54
54
  directory 'db' do
55
55
  directory 'migrate' do
56
56
  migration 'add_object_changes_to_versions' do
@@ -0,0 +1,36 @@
1
+ require 'rails_helper'
2
+
3
+ describe Animal, :type => :model do
4
+ it { is_expected.to be_versioned }
5
+
6
+ describe "STI", :versioning => true do
7
+ it { expect(Animal.inheritance_column).to eq('species') }
8
+
9
+ describe "updates to the `inheritance_column`" do
10
+ subject { Cat.create!(:name => 'Leo') }
11
+
12
+ it "should be allowed" do
13
+ subject.update_attributes(:name => 'Spike', :species => 'Dog')
14
+ dog = Animal.find(subject.id)
15
+ expect(dog).to be_instance_of(Dog)
16
+ end
17
+ end
18
+
19
+ context 'with callback-methods' do
20
+ context 'when only has_paper_trail set in super class' do
21
+ let(:callback_cat) { Cat.create(:name => 'Markus') }
22
+
23
+ it 'trails all events' do
24
+ callback_cat.update_attributes(:name => 'Billie')
25
+ callback_cat.destroy
26
+ expect(callback_cat.versions.collect(&:event)).to eq %w(create update destroy)
27
+ end
28
+
29
+ it 'does not break reify' do
30
+ callback_cat.destroy
31
+ expect { callback_cat.versions.last.reify }.not_to raise_error
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,48 @@
1
+ require 'rails_helper'
2
+ require Rails.root.join('..', 'custom_json_serializer')
3
+
4
+ describe Boolit, :type => :model do
5
+ it { is_expected.to be_versioned }
6
+
7
+ it "has a default scope" do
8
+ expect(subject.default_scopes).to_not be_empty
9
+ end
10
+
11
+ describe "Versioning", :versioning => true do
12
+ subject { Boolit.create! }
13
+ before { subject.update_attributes!(:name => Faker::Name.name) }
14
+
15
+ it "should have versions" do
16
+ expect(subject.versions.size).to eq(2)
17
+ end
18
+
19
+ it "should be able to be reified and persisted" do
20
+ expect { subject.versions.last.reify.save! }.to_not raise_error
21
+ end
22
+
23
+ context "Instance falls out of default scope" do
24
+ before { subject.update_attributes!(:scoped => false) }
25
+
26
+ it "is NOT scoped" do
27
+ expect(Boolit.first).to be_nil
28
+ end
29
+
30
+ it "should still be able to be reified and persisted" do
31
+ expect { subject.previous_version.save! }.to_not raise_error
32
+ end
33
+
34
+ context "with `nil` attributes on the live instance" do
35
+ before do
36
+ PaperTrail.serializer = CustomJsonSerializer
37
+ subject.update_attributes!(:name => nil)
38
+ subject.update_attributes!(:name => Faker::Name.name)
39
+ end
40
+ after { PaperTrail.serializer = PaperTrail::Serializers::YAML }
41
+
42
+ it "should not overwrite that attribute during the reification process" do
43
+ expect(subject.previous_version.name).to be_nil
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -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 => Faker::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 => Faker::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 => Faker::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 after destroy' do
30
+ modifier = NoArgDestroyModifier.create!(:some_content => Faker::Lorem.sentence)
31
+ modifier.test_destroy
32
+ expect(modifier.versions.last.reify).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 => Faker::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 => Faker::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 => Faker::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 => Faker::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 => Faker::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 => Faker::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 => Faker::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 => Faker::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 => Faker::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,19 @@
1
+ require 'rails_helper'
2
+
3
+ describe Fluxor, :type => :model do
4
+ describe '`be_versioned` matcher' do
5
+ it { is_expected.to_not be_versioned }
6
+ end
7
+
8
+ describe "Methods" do
9
+ describe "Class" do
10
+ subject { Fluxor }
11
+
12
+ describe "#paper_trail_enabled_for_model?" do
13
+ it { is_expected.to respond_to(:paper_trail_enabled_for_model?) }
14
+
15
+ it { expect(subject.paper_trail_enabled_for_model?).to be false }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,7 +1,7 @@
1
- require 'spec_helper'
1
+ require 'rails_helper'
2
2
 
3
- describe Gadget do
4
- it { should be_versioned }
3
+ describe Gadget, :type => :model do
4
+ it { is_expected.to be_versioned }
5
5
 
6
6
  let(:gadget) { Gadget.create!(:name => 'Wrench', :brand => 'Acme') }
7
7
 
@@ -22,16 +22,18 @@ describe Gadget do
22
22
  describe "Methods" do
23
23
  describe "Instance", :versioning => true do
24
24
  describe "private" do
25
- describe :changed_notably? do
25
+ describe '#changed_notably?' do
26
26
  subject { Gadget.new(:created_at => Time.now) }
27
27
 
28
28
  # apparently the private methods list in Ruby18 is different than in Ruby19+
29
- if RUBY_VERSION.to_f >= 1.9
30
- its(:private_methods) { should include(:changed_notably?) }
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?') }
31
33
  end
32
34
 
33
35
  context "create events" do
34
- it { subject.send(:changed_notably?).should == true }
36
+ it { expect(subject.send(:changed_notably?)).to be true }
35
37
  end
36
38
 
37
39
  context "update events" do
@@ -40,24 +42,24 @@ describe Gadget do
40
42
  context "without update timestamps" do
41
43
  it "should only acknowledge non-ignored attrs" do
42
44
  subject.name = 'Wrench'
43
- subject.send(:changed_notably?).should == true
45
+ expect(subject.send(:changed_notably?)).to be true
44
46
  end
45
47
 
46
- it "should not acknowledge ignored attrs and timestamps only" do
48
+ it "should not acknowledge ignored attr (brand)" do
47
49
  subject.brand = 'Acme'
48
- subject.send(:changed_notably?).should == false
50
+ expect(subject.send(:changed_notably?)).to be false
49
51
  end
50
52
  end
51
53
 
52
54
  context "with update timestamps" do
53
55
  it "should only acknowledge non-ignored attrs" do
54
56
  subject.name, subject.updated_at = 'Wrench', Time.now
55
- subject.send(:changed_notably?).should == true
57
+ expect(subject.send(:changed_notably?)).to be true
56
58
  end
57
59
 
58
60
  it "should not acknowledge ignored attrs and timestamps only" do
59
61
  subject.brand, subject.updated_at = 'Acme', Time.now
60
- subject.send(:changed_notably?).should == false
62
+ expect(subject.send(:changed_notably?)).to be false
61
63
  end
62
64
  end
63
65
  end
@@ -1,32 +1,32 @@
1
- require 'spec_helper'
1
+ require 'rails_helper'
2
2
 
3
- describe JoinedVersion, :versioning => true do
4
- it { JoinedVersion.superclass.should == PaperTrail::Version }
3
+ describe JoinedVersion, :type => :model, :versioning => true do
4
+ it { expect(JoinedVersion.superclass).to be PaperTrail::Version }
5
5
 
6
6
  let(:widget) { Widget.create!(:name => Faker::Name.name) }
7
7
  let(:version) { JoinedVersion.first }
8
8
 
9
9
  describe "Scopes" do
10
10
  describe "default_scope" do
11
- it { JoinedVersion.default_scopes.should_not be_empty }
11
+ it { expect(JoinedVersion.default_scopes).not_to be_empty }
12
12
  end
13
13
 
14
14
  describe "VersionConcern::ClassMethods" do
15
15
  before { widget } # persist a widget
16
16
 
17
- describe :subsequent do
17
+ describe '#subsequent' do
18
18
  it "shouldn't error out when there is a default_scope that joins" do
19
19
  JoinedVersion.subsequent(version).first
20
20
  end
21
21
  end
22
22
 
23
- describe :preceding do
23
+ describe '#preceding' do
24
24
  it "shouldn't error out when there is a default scope that joins" do
25
25
  JoinedVersion.preceding(version).first
26
26
  end
27
27
  end
28
28
 
29
- describe :between do
29
+ describe '#between' do
30
30
  it "shouldn't error out when there is a default scope that joins" do
31
31
  JoinedVersion.between(Time.now, 1.minute.from_now).first
32
32
  end
@@ -35,8 +35,8 @@ describe JoinedVersion, :versioning => true do
35
35
  end
36
36
 
37
37
  describe "Methods" do
38
- describe :index do
39
- it { should respond_to(:index) }
38
+ describe '#index' do
39
+ it { is_expected.to respond_to(:index) }
40
40
 
41
41
  it "shouldn't error out when there is a default scope that joins" do
42
42
  widget # persist a widget
@@ -0,0 +1,103 @@
1
+ require 'rails_helper'
2
+
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?
6
+
7
+ describe JsonVersion, :type => :model do
8
+ it "should include the `VersionConcern` module to get base functionality" do
9
+ expect(JsonVersion).to include(PaperTrail::VersionConcern)
10
+ end
11
+
12
+ describe "Methods" do
13
+ describe "Class" do
14
+
15
+ describe '#where_object' do
16
+ it { expect(JsonVersion).to respond_to(:where_object) }
17
+
18
+ it "escapes values" do
19
+ f = Fruit.create(:name => 'Bobby')
20
+ expect(
21
+ f.
22
+ versions.
23
+ where_object(:name => "Robert'; DROP TABLE Students;--").
24
+ count
25
+ ).to eq(0)
26
+ end
27
+
28
+ context "invalid arguments" do
29
+ it "should raise an error" do
30
+ expect { JsonVersion.where_object(:foo) }.to raise_error(ArgumentError)
31
+ expect { JsonVersion.where_object([]) }.to raise_error(ArgumentError)
32
+ end
33
+ end
34
+
35
+ context "valid arguments", :versioning => true do
36
+ let(:fruit_names) { %w(apple orange lemon banana lime coconut strawberry blueberry) }
37
+ let(:fruit) { Fruit.new }
38
+ let(:name) { 'pomegranate' }
39
+ let(:color) { Faker::Color.name }
40
+
41
+ before do
42
+ fruit.update_attributes!(:name => name)
43
+ fruit.update_attributes!(:name => fruit_names.sample, :color => color)
44
+ fruit.update_attributes!(:name => fruit_names.sample, :color => Faker::Color.name)
45
+ end
46
+
47
+ it "should be able to locate versions according to their `object` contents" do
48
+ expect(JsonVersion.where_object(:name => name)).to eq([fruit.versions[1]])
49
+ expect(JsonVersion.where_object(:color => color)).to eq([fruit.versions[2]])
50
+ end
51
+ end
52
+ end
53
+
54
+ describe '#where_object_changes' do
55
+ it { expect(JsonVersion).to respond_to(:where_object_changes) }
56
+
57
+ it "escapes values" do
58
+ f = Fruit.create(:name => 'Bobby')
59
+ expect(
60
+ f.
61
+ versions.
62
+ where_object_changes(:name => "Robert'; DROP TABLE Students;--").
63
+ count
64
+ ).to eq(0)
65
+ end
66
+
67
+ context "invalid arguments" do
68
+ it "should raise an error" do
69
+ expect { JsonVersion.where_object_changes(:foo) }.to raise_error(ArgumentError)
70
+ expect { JsonVersion.where_object_changes([]) }.to raise_error(ArgumentError)
71
+ end
72
+ end
73
+
74
+ context "valid arguments", :versioning => true do
75
+ let(:color) { %w[red green] }
76
+ let(:fruit) { Fruit.create!(:name => name[0]) }
77
+ let(:name) { %w[banana kiwi mango] }
78
+
79
+ before do
80
+ fruit.update_attributes!(:name => name[1], :color => color[0])
81
+ fruit.update_attributes!(:name => name[2], :color => color[1])
82
+ end
83
+
84
+ it "finds versions according to their `object_changes` contents" do
85
+ expect(
86
+ fruit.versions.where_object_changes(:name => name[0])
87
+ ).to match_array(fruit.versions[0..1])
88
+ expect(
89
+ fruit.versions.where_object_changes(:color => color[0])
90
+ ).to match_array(fruit.versions[1..2])
91
+ end
92
+
93
+ it "finds versions with multiple attributes changed" do
94
+ expect(
95
+ fruit.versions.where_object_changes(:color => color[0], :name => name[0])
96
+ ).to match_array([fruit.versions[1]])
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end