paper_trail 4.0.2 → 4.1.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -0
  3. data/CHANGELOG.md +27 -0
  4. data/CONTRIBUTING.md +78 -5
  5. data/README.md +328 -268
  6. data/doc/bug_report_template.rb +65 -0
  7. data/gemfiles/3.0.gemfile +7 -4
  8. data/lib/generators/paper_trail/templates/add_object_changes_to_versions.rb +1 -1
  9. data/lib/generators/paper_trail/templates/create_versions.rb +14 -0
  10. data/lib/paper_trail.rb +11 -9
  11. data/lib/paper_trail/attributes_serialization.rb +89 -0
  12. data/lib/paper_trail/cleaner.rb +8 -1
  13. data/lib/paper_trail/config.rb +15 -18
  14. data/lib/paper_trail/frameworks/rails/controller.rb +16 -2
  15. data/lib/paper_trail/has_paper_trail.rb +102 -99
  16. data/lib/paper_trail/record_history.rb +59 -0
  17. data/lib/paper_trail/reifier.rb +270 -0
  18. data/lib/paper_trail/version_association_concern.rb +3 -1
  19. data/lib/paper_trail/version_concern.rb +60 -226
  20. data/lib/paper_trail/version_number.rb +2 -2
  21. data/paper_trail.gemspec +7 -10
  22. data/spec/models/animal_spec.rb +17 -0
  23. data/spec/models/callback_modifier_spec.rb +96 -0
  24. data/spec/models/json_version_spec.rb +20 -17
  25. data/spec/paper_trail/config_spec.rb +52 -0
  26. data/spec/spec_helper.rb +6 -0
  27. data/test/dummy/app/models/callback_modifier.rb +45 -0
  28. data/test/dummy/app/models/chapter.rb +9 -0
  29. data/test/dummy/app/models/citation.rb +5 -0
  30. data/test/dummy/app/models/paragraph.rb +5 -0
  31. data/test/dummy/app/models/quotation.rb +5 -0
  32. data/test/dummy/app/models/section.rb +6 -0
  33. data/test/dummy/config/database.postgres.yml +1 -1
  34. data/test/dummy/config/initializers/paper_trail.rb +3 -1
  35. data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +33 -0
  36. data/test/dummy/db/schema.rb +27 -0
  37. data/test/test_helper.rb +36 -0
  38. data/test/unit/associations_test.rb +726 -0
  39. data/test/unit/inheritance_column_test.rb +6 -6
  40. data/test/unit/model_test.rb +62 -594
  41. data/test/unit/protected_attrs_test.rb +3 -2
  42. data/test/unit/version_test.rb +87 -69
  43. metadata +38 -2
@@ -1,8 +1,8 @@
1
1
  module PaperTrail
2
2
  module VERSION
3
3
  MAJOR = 4
4
- MINOR = 0
5
- TINY = 2
4
+ MINOR = 1
5
+ TINY = 0
6
6
  PRE = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
@@ -33,19 +33,20 @@ Gem::Specification.new do |s|
33
33
  s.add_development_dependency 'rspec-rails', '~> 3.1.0'
34
34
  s.add_development_dependency 'generator_spec'
35
35
  s.add_development_dependency 'database_cleaner', '~> 1.2'
36
+ s.add_development_dependency 'pry-nav'
36
37
 
38
+ # Allow time travel in testing. timecop is only supported after 1.9.2 but does a better cleanup at 'return'
37
39
  if RUBY_VERSION < "1.9.2"
38
40
  s.add_development_dependency 'delorean'
39
-
40
- # rack-cache 1.3 drops ruby 1.8.7 support
41
- s.add_development_dependency 'rack-cache', '1.2'
42
41
  else
43
- # timecop is only supported after 1.9.2 but does a better cleanup at 'return'
44
42
  s.add_development_dependency 'timecop'
45
43
  end
46
44
 
47
- # JRuby support for the test ENV
48
- unless defined?(JRUBY_VERSION)
45
+ if defined?(JRUBY_VERSION)
46
+ s.add_development_dependency 'activerecord-jdbcsqlite3-adapter', '~> 1.3'
47
+ s.add_development_dependency 'activerecord-jdbcpostgresql-adapter', '~> 1.3'
48
+ s.add_development_dependency 'activerecord-jdbcmysql-adapter', '~> 1.3'
49
+ else
49
50
  s.add_development_dependency 'sqlite3', '~> 1.2'
50
51
 
51
52
  # We would prefer to only constrain mysql2 to '~> 0.3',
@@ -54,9 +55,5 @@ Gem::Specification.new do |s|
54
55
  s.add_development_dependency 'mysql2', '~> 0.3.20'
55
56
 
56
57
  s.add_development_dependency 'pg', '~> 0.17'
57
- else
58
- s.add_development_dependency 'activerecord-jdbcsqlite3-adapter', '~> 1.3'
59
- s.add_development_dependency 'activerecord-jdbcpostgresql-adapter', '~> 1.3'
60
- s.add_development_dependency 'activerecord-jdbcmysql-adapter', '~> 1.3'
61
58
  end
62
59
  end
@@ -15,5 +15,22 @@ describe Animal, :type => :model do
15
15
  expect(dog).to be_instance_of(Dog)
16
16
  end
17
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
18
35
  end
19
36
  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
@@ -1,6 +1,8 @@
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
7
  describe JsonVersion, :type => :model do
6
8
  it "should include the `VersionConcern` module to get base functionality" do
@@ -70,31 +72,32 @@ if JsonVersion.table_exists?
70
72
  end
71
73
 
72
74
  context "valid arguments", :versioning => true do
73
- let(:fruit_names) { %w(apple orange lemon banana lime strawberry blueberry) }
74
- let(:tropical_fruit_names) { %w(coconut pineapple kiwi mango melon) }
75
- let(:fruit) { Fruit.new }
76
- let(:name) { 'pomegranate' }
77
- let(:color) { Faker::Color.name }
75
+ let(:color) { %w[red green] }
76
+ let(:fruit) { Fruit.create!(:name => name[0]) }
77
+ let(:name) { %w[banana kiwi mango] }
78
78
 
79
79
  before do
80
- fruit.update_attributes!(:name => name)
81
- fruit.update_attributes!(:name => tropical_fruit_names.sample, :color => color)
82
- fruit.update_attributes!(:name => fruit_names.sample, :color => Faker::Color.name)
80
+ fruit.update_attributes!(:name => name[1], :color => color[0])
81
+ fruit.update_attributes!(:name => name[2], :color => color[1])
83
82
  end
84
83
 
85
- it "should be able to locate versions according to their `object_changes` contents" do
86
- expect(fruit.versions.where_object_changes(:name => name)).to eq(fruit.versions[0..1])
87
- expect(fruit.versions.where_object_changes(:color => color)).to eq(fruit.versions[1..2])
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])
88
91
  end
89
92
 
90
- it "should be able to handle queries for multiple attributes" do
91
- expect(fruit.versions.where_object_changes(:color => color, :name => name)).to eq([fruit.versions[1]])
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]])
92
97
  end
93
98
  end
94
99
  end
95
100
  end
96
-
97
101
  end
98
102
  end
99
-
100
103
  end
@@ -0,0 +1,52 @@
1
+ require "rails_helper"
2
+
3
+ module PaperTrail
4
+ RSpec.describe Config do
5
+ describe ".instance" do
6
+ it "returns the singleton instance" do
7
+ expect { described_class.instance }.to_not raise_error
8
+ end
9
+ end
10
+
11
+ describe ".new" do
12
+ it "raises NoMethodError" do
13
+ expect { described_class.new }.to raise_error(NoMethodError)
14
+ end
15
+ end
16
+
17
+ describe "#enabled" do
18
+ context "when paper_trail_enabled is true" do
19
+ it "returns true" do
20
+ store = double
21
+ allow(store).to receive(:fetch).
22
+ with(:paper_trail_enabled, true).
23
+ and_return(true)
24
+ allow(PaperTrail).to receive(:paper_trail_store).and_return(store)
25
+ expect(described_class.instance.enabled).to eq(true)
26
+ end
27
+ end
28
+
29
+ context "when paper_trail_enabled is false" do
30
+ it "returns false" do
31
+ store = double
32
+ allow(store).to receive(:fetch).
33
+ with(:paper_trail_enabled, true).
34
+ and_return(false)
35
+ allow(PaperTrail).to receive(:paper_trail_store).and_return(store)
36
+ expect(described_class.instance.enabled).to eq(false)
37
+ end
38
+ end
39
+
40
+ context "when paper_trail_enabled is nil" do
41
+ it "returns true" do
42
+ store = double
43
+ allow(store).to receive(:fetch).
44
+ with(:paper_trail_enabled, true).
45
+ and_return(nil)
46
+ allow(PaperTrail).to receive(:paper_trail_store).and_return(store)
47
+ expect(described_class.instance.enabled).to eq(true)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,3 +1,9 @@
1
+ begin
2
+ require 'pry-nav'
3
+ rescue LoadError
4
+ # It's OK, we don't include pry in e.g. gemfiles/3.0.gemfile
5
+ end
6
+
1
7
  # This file was generated by the `rspec --init` command. Conventionally, all
2
8
  # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
9
  # The generated `.rspec` file contains `--require spec_helper` which will cause this
@@ -0,0 +1,45 @@
1
+ class CallbackModifier < ActiveRecord::Base
2
+ has_paper_trail :on => []
3
+
4
+ def test_destroy
5
+ transaction do
6
+ run_callbacks(:destroy) do
7
+ self.deleted = true
8
+ save!
9
+ end
10
+ end
11
+ end
12
+
13
+ def flagged_deleted?
14
+ deleted?
15
+ end
16
+ end
17
+
18
+ class BeforeDestroyModifier < CallbackModifier
19
+ has_paper_trail :on => []
20
+ paper_trail_on_destroy :before
21
+ end
22
+
23
+ class AfterDestroyModifier < CallbackModifier
24
+ has_paper_trail :on => []
25
+ paper_trail_on_destroy :after
26
+ end
27
+
28
+ class NoArgDestroyModifier < CallbackModifier
29
+ has_paper_trail :on => []
30
+ paper_trail_on_destroy
31
+ end
32
+
33
+ class UpdateModifier < CallbackModifier
34
+ has_paper_trail :on => []
35
+ paper_trail_on_update
36
+ end
37
+
38
+ class CreateModifier < CallbackModifier
39
+ has_paper_trail :on => []
40
+ paper_trail_on_create
41
+ end
42
+
43
+ class DefaultModifier < CallbackModifier
44
+ has_paper_trail
45
+ end
@@ -0,0 +1,9 @@
1
+ class Chapter < ActiveRecord::Base
2
+ has_many :sections, :dependent => :destroy
3
+ has_many :paragraphs, :through => :sections
4
+
5
+ has_many :quotations, :dependent => :destroy
6
+ has_many :citations, :through => :quotations
7
+
8
+ has_paper_trail
9
+ end
@@ -0,0 +1,5 @@
1
+ class Citation < ActiveRecord::Base
2
+ belongs_to :quotation
3
+
4
+ has_paper_trail
5
+ end
@@ -0,0 +1,5 @@
1
+ class Paragraph < ActiveRecord::Base
2
+ belongs_to :section
3
+
4
+ has_paper_trail
5
+ end
@@ -0,0 +1,5 @@
1
+ class Quotation < ActiveRecord::Base
2
+ belongs_to :chapter
3
+ has_many :citations, :dependent => :destroy
4
+ has_paper_trail
5
+ end
@@ -0,0 +1,6 @@
1
+ class Section < ActiveRecord::Base
2
+ belongs_to :chapter
3
+ has_many :paragraphs, :dependent => :destroy
4
+
5
+ has_paper_trail
6
+ end
@@ -12,4 +12,4 @@ foo:
12
12
 
13
13
  bar:
14
14
  <<: *test
15
- database: paper_trail_bar
15
+ database: paper_trail_bar
@@ -3,6 +3,8 @@ PaperTrail.config.track_associations = true if ENV['TRAVIS']
3
3
 
4
4
  module PaperTrail
5
5
  class Version < ActiveRecord::Base
6
- attr_accessible :answer, :action, :question, :article_id, :ip, :user_agent, :title if ::PaperTrail.active_record_protected_attributes?
6
+ if ::PaperTrail.active_record_protected_attributes?
7
+ attr_accessible :answer, :action, :question, :article_id, :ip, :user_agent, :title
8
+ end
7
9
  end
8
10
  end
@@ -211,9 +211,38 @@ class SetUpTestTables < ActiveRecord::Migration
211
211
  t.string :name
212
212
  t.boolean :scoped, :default => true
213
213
  end
214
+
215
+ create_table :callback_modifiers, :force => true do |t|
216
+ t.string :some_content
217
+ t.boolean :deleted, :default => false
218
+ end
219
+
220
+ create_table :chapters, :force => true do |t|
221
+ t.string :name
222
+ end
223
+
224
+ create_table :sections, :force => true do |t|
225
+ t.integer :chapter_id
226
+ t.string :name
227
+ end
228
+
229
+ create_table :paragraphs, :force => true do |t|
230
+ t.integer :section_id
231
+ t.string :name
232
+ end
233
+
234
+ create_table :quotations, :force => true do |t|
235
+ t.integer :chapter_id
236
+ end
237
+
238
+ create_table :citations, :force => true do |t|
239
+ t.integer :quotation_id
240
+ end
214
241
  end
215
242
 
216
243
  def self.down
244
+ drop_table :citations
245
+ drop_table :quotations
217
246
  drop_table :animals
218
247
  drop_table :skippers
219
248
  drop_table :not_on_updates
@@ -247,8 +276,12 @@ class SetUpTestTables < ActiveRecord::Migration
247
276
  drop_table :line_items
248
277
  drop_table :fruits
249
278
  drop_table :boolits
279
+ drop_table :chapters
280
+ drop_table :sections
281
+ drop_table :paragraphs
250
282
  remove_index :version_associations, :column => [:version_id]
251
283
  remove_index :version_associations, :name => 'index_version_associations_on_foreign_key'
252
284
  drop_table :version_associations
285
+ drop_table :callback_modifiers
253
286
  end
254
287
  end