paper_trail 3.0.6 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/.rspec +1 -2
- data/.travis.yml +14 -5
- data/CHANGELOG.md +215 -8
- data/CONTRIBUTING.md +84 -0
- data/README.md +922 -502
- data/Rakefile +2 -2
- data/doc/bug_report_template.rb +65 -0
- data/gemfiles/ar3.gemfile +61 -0
- data/lib/generators/paper_trail/install_generator.rb +22 -3
- data/lib/generators/paper_trail/templates/add_object_changes_to_versions.rb +6 -1
- data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb +11 -0
- data/lib/generators/paper_trail/templates/create_version_associations.rb +17 -0
- data/lib/generators/paper_trail/templates/create_versions.rb +22 -1
- data/lib/paper_trail.rb +52 -22
- data/lib/paper_trail/attributes_serialization.rb +89 -0
- data/lib/paper_trail/cleaner.rb +32 -15
- data/lib/paper_trail/config.rb +35 -2
- data/lib/paper_trail/frameworks/active_record.rb +4 -5
- data/lib/paper_trail/frameworks/active_record/models/paper_trail/version_association.rb +7 -0
- data/lib/paper_trail/frameworks/rails.rb +1 -0
- data/lib/paper_trail/frameworks/rails/controller.rb +27 -11
- data/lib/paper_trail/frameworks/rspec.rb +5 -0
- data/lib/paper_trail/frameworks/sinatra.rb +3 -1
- data/lib/paper_trail/has_paper_trail.rb +304 -148
- data/lib/paper_trail/record_history.rb +59 -0
- data/lib/paper_trail/reifier.rb +270 -0
- data/lib/paper_trail/serializers/json.rb +13 -2
- data/lib/paper_trail/serializers/yaml.rb +16 -2
- data/lib/paper_trail/version_association_concern.rb +15 -0
- data/lib/paper_trail/version_concern.rb +160 -122
- data/lib/paper_trail/version_number.rb +3 -3
- data/paper_trail.gemspec +22 -9
- data/spec/generators/install_generator_spec.rb +4 -4
- data/spec/models/animal_spec.rb +36 -0
- data/spec/models/boolit_spec.rb +48 -0
- data/spec/models/callback_modifier_spec.rb +96 -0
- data/spec/models/fluxor_spec.rb +19 -0
- data/spec/models/gadget_spec.rb +14 -12
- data/spec/models/joined_version_spec.rb +9 -9
- data/spec/models/json_version_spec.rb +103 -0
- data/spec/models/kitchen/banana_spec.rb +14 -0
- data/spec/models/not_on_update_spec.rb +19 -0
- data/spec/models/post_with_status_spec.rb +3 -3
- data/spec/models/skipper_spec.rb +46 -0
- data/spec/models/thing_spec.rb +11 -0
- data/spec/models/version_spec.rb +195 -44
- data/spec/models/widget_spec.rb +136 -76
- data/spec/modules/paper_trail_spec.rb +27 -0
- data/spec/modules/version_concern_spec.rb +8 -8
- data/spec/modules/version_number_spec.rb +16 -16
- data/spec/paper_trail/config_spec.rb +52 -0
- data/spec/paper_trail_spec.rb +17 -17
- data/spec/rails_helper.rb +34 -0
- data/spec/requests/articles_spec.rb +10 -14
- data/spec/spec_helper.rb +81 -34
- data/spec/support/alt_db_init.rb +1 -1
- data/test/dummy/app/controllers/application_controller.rb +1 -1
- data/test/dummy/app/controllers/articles_controller.rb +4 -1
- data/test/dummy/app/models/animal.rb +2 -0
- data/test/dummy/app/models/book.rb +4 -0
- data/test/dummy/app/models/boolit.rb +4 -0
- data/test/dummy/app/models/callback_modifier.rb +45 -0
- data/test/dummy/app/models/chapter.rb +9 -0
- data/test/dummy/app/models/citation.rb +5 -0
- data/test/dummy/app/models/customer.rb +4 -0
- data/test/dummy/app/models/editor.rb +4 -0
- data/test/dummy/app/models/editorship.rb +5 -0
- data/test/dummy/app/models/fruit.rb +5 -0
- data/test/dummy/app/models/kitchen/banana.rb +5 -0
- data/test/dummy/app/models/line_item.rb +4 -0
- data/test/dummy/app/models/not_on_update.rb +4 -0
- data/test/dummy/app/models/order.rb +5 -0
- data/test/dummy/app/models/paragraph.rb +5 -0
- data/test/dummy/app/models/person.rb +13 -3
- data/test/dummy/app/models/post.rb +0 -1
- data/test/dummy/app/models/quotation.rb +5 -0
- data/test/dummy/app/models/section.rb +6 -0
- data/test/dummy/app/models/skipper.rb +6 -0
- data/test/dummy/app/models/song.rb +20 -0
- data/test/dummy/app/models/thing.rb +3 -0
- data/test/dummy/app/models/whatchamajigger.rb +4 -0
- data/test/dummy/app/models/widget.rb +5 -0
- data/test/dummy/app/versions/json_version.rb +3 -0
- data/test/dummy/app/versions/kitchen/banana_version.rb +5 -0
- data/test/dummy/config/application.rb +6 -0
- data/test/dummy/config/database.postgres.yml +1 -1
- data/test/dummy/config/environments/test.rb +5 -1
- data/test/dummy/config/initializers/paper_trail.rb +6 -1
- data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +143 -3
- data/test/dummy/db/schema.rb +169 -25
- data/test/functional/controller_test.rb +4 -2
- data/test/functional/modular_sinatra_test.rb +1 -1
- data/test/functional/sinatra_test.rb +1 -1
- data/test/paper_trail_test.rb +7 -0
- data/test/test_helper.rb +38 -2
- data/test/time_travel_helper.rb +15 -0
- data/test/unit/associations_test.rb +726 -0
- data/test/unit/inheritance_column_test.rb +6 -6
- data/test/unit/model_test.rb +109 -125
- data/test/unit/protected_attrs_test.rb +4 -3
- data/test/unit/serializer_test.rb +6 -6
- data/test/unit/serializers/json_test.rb +17 -4
- data/test/unit/serializers/yaml_test.rb +5 -1
- data/test/unit/version_test.rb +87 -69
- metadata +172 -75
- data/gemfiles/3.0.gemfile +0 -42
- data/test/dummy/public/404.html +0 -26
- data/test/dummy/public/422.html +0 -26
- data/test/dummy/public/500.html +0 -26
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/javascripts/application.js +0 -2
- data/test/dummy/public/javascripts/controls.js +0 -965
- data/test/dummy/public/javascripts/dragdrop.js +0 -974
- data/test/dummy/public/javascripts/effects.js +0 -1123
- data/test/dummy/public/javascripts/prototype.js +0 -6001
- data/test/dummy/public/javascripts/rails.js +0 -175
- data/test/dummy/public/stylesheets/.gitkeep +0 -0
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', '<
|
|
23
|
-
s.add_dependency 'activesupport', ['>= 3.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',
|
|
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', '~>
|
|
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
|
-
#
|
|
37
|
-
|
|
38
|
-
s.add_development_dependency '
|
|
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 '
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
data/spec/models/gadget_spec.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'rails_helper'
|
|
2
2
|
|
|
3
|
-
describe Gadget do
|
|
4
|
-
it {
|
|
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
|
|
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
|
|
30
|
-
|
|
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?).
|
|
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?).
|
|
45
|
+
expect(subject.send(:changed_notably?)).to be true
|
|
44
46
|
end
|
|
45
47
|
|
|
46
|
-
it "should not acknowledge ignored
|
|
48
|
+
it "should not acknowledge ignored attr (brand)" do
|
|
47
49
|
subject.brand = 'Acme'
|
|
48
|
-
subject.send(:changed_notably?).
|
|
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?).
|
|
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?).
|
|
62
|
+
expect(subject.send(:changed_notably?)).to be false
|
|
61
63
|
end
|
|
62
64
|
end
|
|
63
65
|
end
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'rails_helper'
|
|
2
2
|
|
|
3
|
-
describe JoinedVersion, :versioning => true do
|
|
4
|
-
it { JoinedVersion.superclass.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
39
|
-
it {
|
|
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
|