paper_trail 6.0.2 → 7.0.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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CONTRIBUTING.md +20 -0
  3. data/.rubocop.yml +30 -2
  4. data/.rubocop_todo.yml +20 -0
  5. data/.travis.yml +3 -5
  6. data/Appraisals +5 -6
  7. data/CHANGELOG.md +33 -0
  8. data/README.md +43 -81
  9. data/Rakefile +1 -1
  10. data/doc/bug_report_template.rb +4 -2
  11. data/gemfiles/ar_4.0.gemfile +7 -0
  12. data/gemfiles/ar_4.2.gemfile +0 -1
  13. data/lib/generators/paper_trail/templates/create_version_associations.rb +1 -1
  14. data/lib/generators/paper_trail/templates/create_versions.rb +1 -1
  15. data/lib/paper_trail.rb +7 -9
  16. data/lib/paper_trail/config.rb +0 -15
  17. data/lib/paper_trail/frameworks/rspec.rb +8 -2
  18. data/lib/paper_trail/model_config.rb +6 -2
  19. data/lib/paper_trail/record_trail.rb +3 -1
  20. data/lib/paper_trail/reifier.rb +43 -354
  21. data/lib/paper_trail/reifiers/belongs_to.rb +48 -0
  22. data/lib/paper_trail/reifiers/has_and_belongs_to_many.rb +50 -0
  23. data/lib/paper_trail/reifiers/has_many.rb +110 -0
  24. data/lib/paper_trail/reifiers/has_many_through.rb +90 -0
  25. data/lib/paper_trail/reifiers/has_one.rb +76 -0
  26. data/lib/paper_trail/serializers/yaml.rb +2 -25
  27. data/lib/paper_trail/version_concern.rb +5 -5
  28. data/lib/paper_trail/version_number.rb +7 -3
  29. data/paper_trail.gemspec +7 -34
  30. data/spec/controllers/articles_controller_spec.rb +1 -1
  31. data/spec/generators/install_generator_spec.rb +40 -34
  32. data/spec/models/animal_spec.rb +50 -25
  33. data/spec/models/boolit_spec.rb +8 -7
  34. data/spec/models/callback_modifier_spec.rb +13 -13
  35. data/spec/models/document_spec.rb +21 -0
  36. data/spec/models/gadget_spec.rb +35 -39
  37. data/spec/models/joined_version_spec.rb +4 -4
  38. data/spec/models/json_version_spec.rb +14 -15
  39. data/spec/models/not_on_update_spec.rb +1 -1
  40. data/spec/models/post_with_status_spec.rb +2 -2
  41. data/spec/models/skipper_spec.rb +4 -4
  42. data/spec/models/thing_spec.rb +1 -1
  43. data/spec/models/truck_spec.rb +1 -1
  44. data/spec/models/vehicle_spec.rb +1 -1
  45. data/spec/models/version_spec.rb +152 -168
  46. data/spec/models/widget_spec.rb +170 -196
  47. data/spec/modules/paper_trail_spec.rb +3 -3
  48. data/spec/modules/version_concern_spec.rb +5 -8
  49. data/spec/modules/version_number_spec.rb +11 -36
  50. data/spec/paper_trail/cleaner_spec.rb +152 -0
  51. data/spec/paper_trail/config_spec.rb +1 -1
  52. data/spec/paper_trail/serializers/custom_yaml_serializer_spec.rb +45 -0
  53. data/spec/paper_trail/serializers/json_spec.rb +57 -0
  54. data/spec/paper_trail/version_limit_spec.rb +55 -0
  55. data/spec/paper_trail_spec.rb +45 -32
  56. data/spec/requests/articles_spec.rb +4 -4
  57. data/test/dummy/app/models/custom_primary_key_record.rb +4 -2
  58. data/test/dummy/app/models/document.rb +1 -1
  59. data/test/dummy/app/models/not_on_update.rb +1 -1
  60. data/test/dummy/app/models/on/create.rb +6 -0
  61. data/test/dummy/app/models/on/destroy.rb +6 -0
  62. data/test/dummy/app/models/on/empty_array.rb +6 -0
  63. data/test/dummy/app/models/on/update.rb +6 -0
  64. data/test/dummy/app/models/person.rb +1 -0
  65. data/test/dummy/app/models/song.rb +19 -28
  66. data/test/dummy/config/application.rb +10 -43
  67. data/test/dummy/config/routes.rb +1 -1
  68. data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +25 -51
  69. data/test/dummy/db/schema.rb +29 -19
  70. data/test/test_helper.rb +0 -16
  71. data/test/unit/associations_test.rb +81 -81
  72. data/test/unit/model_test.rb +48 -131
  73. data/test/unit/serializer_test.rb +34 -45
  74. data/test/unit/serializers/mixin_json_test.rb +3 -1
  75. data/test/unit/serializers/yaml_test.rb +1 -5
  76. metadata +44 -19
  77. data/lib/paper_trail/frameworks/sinatra.rb +0 -40
  78. data/test/functional/modular_sinatra_test.rb +0 -46
  79. data/test/functional/sinatra_test.rb +0 -51
  80. data/test/unit/cleaner_test.rb +0 -151
  81. data/test/unit/inheritance_column_test.rb +0 -41
  82. data/test/unit/serializers/json_test.rb +0 -95
  83. data/test/unit/serializers/mixin_yaml_test.rb +0 -53
@@ -1,40 +0,0 @@
1
- require "active_support/core_ext/object" # provides the `try` method
2
-
3
- module PaperTrail
4
- # Extensions to `Sinatra`.
5
- module Sinatra
6
- # Register this module inside your Sinatra application to gain access to
7
- # controller-level methods used by PaperTrail.
8
- def self.registered(app)
9
- app.use RequestStore::Middleware
10
- app.helpers self
11
- app.before { set_paper_trail_whodunnit }
12
- end
13
-
14
- protected
15
-
16
- # Returns the user who is responsible for any changes that occur.
17
- # By default this calls `current_user` and returns the result.
18
- #
19
- # Override this method in your controller to call a different
20
- # method, e.g. `current_person`, or anything you like.
21
- def user_for_paper_trail
22
- return unless defined?(current_user)
23
- ActiveSupport::VERSION::MAJOR >= 4 ? current_user.try!(:id) : current_user.try(:id)
24
- rescue NoMethodError
25
- current_user
26
- end
27
-
28
- private
29
-
30
- # Tells PaperTrail who is responsible for any changes that occur.
31
- def set_paper_trail_whodunnit
32
- @set_paper_trail_whodunnit_called = true
33
- ::PaperTrail.whodunnit = user_for_paper_trail if ::PaperTrail.enabled?
34
- end
35
- end
36
- end
37
-
38
- if defined?(::Sinatra)
39
- ::Sinatra.register(::PaperTrail::Sinatra)
40
- end
@@ -1,46 +0,0 @@
1
- # PaperTrail is not compatible with sinatra 2 yet. Contributions welcome.
2
- if Gem::Version.new(Rack.release) < Gem::Version.new("2.0.0.alpha")
3
- require "test_helper"
4
- require "sinatra/base"
5
-
6
- # --- Tests for modular `Sinatra::Base` style ----
7
- class BaseApp < Sinatra::Base
8
- configs = YAML.load_file(File.expand_path("../../dummy/config/database.yml", __FILE__))
9
- ActiveRecord::Base.configurations = configs
10
- ActiveRecord::Base.establish_connection(:test)
11
- register PaperTrail::Sinatra
12
-
13
- get "/test" do
14
- Widget.create!(name: "foo")
15
- "Hello"
16
- end
17
-
18
- def current_user
19
- @current_user ||= OpenStruct.new(id: "foobar")
20
- end
21
- end
22
-
23
- class ModularSinatraTest < ActionDispatch::IntegrationTest
24
- include Rack::Test::Methods
25
-
26
- def app
27
- @app ||= BaseApp
28
- end
29
-
30
- test "baseline" do
31
- assert_nil Widget.create.versions.first.whodunnit
32
- end
33
-
34
- context "`PaperTrail::Sinatra` in a `Sinatra::Base` application" do
35
- should "sets the `user_for_paper_trail` from the `current_user` method" do
36
- get "/test"
37
- assert_equal "Hello", last_response.body
38
- widget = Widget.last
39
- assert_not_nil widget
40
- assert_equal "foo", widget.name
41
- assert_equal 1, widget.versions.size
42
- assert_equal "foobar", widget.versions.first.whodunnit
43
- end
44
- end
45
- end
46
- end
@@ -1,51 +0,0 @@
1
- # PaperTrail is not compatible with sinatra 2 yet. Contributions welcome.
2
- if Gem::Version.new(Rack.release) < Gem::Version.new("2.0.0.alpha")
3
- require "test_helper"
4
- # require 'sinatra/main'
5
-
6
- # --- Tests for non-modular `Sinatra::Application` style ----
7
- module Sinatra
8
- class Application
9
- configs = YAML.load_file(File.expand_path("../../dummy/config/database.yml", __FILE__))
10
- ActiveRecord::Base.configurations = configs
11
- ActiveRecord::Base.establish_connection(:test)
12
-
13
- # We shouldn't actually need this line if I'm not mistaken but the tests
14
- # seem to fail without it ATM
15
- register PaperTrail::Sinatra
16
-
17
- get "/test" do
18
- Widget.create!(name: "bar")
19
- "Hai"
20
- end
21
-
22
- def current_user
23
- @current_user ||= OpenStruct.new(id: "raboof")
24
- end
25
- end
26
- end
27
-
28
- class SinatraTest < ActionDispatch::IntegrationTest
29
- include Rack::Test::Methods
30
-
31
- def app
32
- @app ||= Sinatra::Application
33
- end
34
-
35
- test "baseline" do
36
- assert_nil Widget.create.versions.first.whodunnit
37
- end
38
-
39
- context "`PaperTrail::Sinatra` in a `Sinatra::Application` application" do
40
- should "sets the `user_for_paper_trail` from the `current_user` method" do
41
- get "/test"
42
- assert_equal "Hai", last_response.body
43
- widget = Widget.last
44
- assert_not_nil widget
45
- assert_equal "bar", widget.name
46
- assert_equal 1, widget.versions.size
47
- assert_equal "raboof", widget.versions.first.whodunnit
48
- end
49
- end
50
- end
51
- end
@@ -1,151 +0,0 @@
1
- require "test_helper"
2
-
3
- class PaperTrailCleanerTest < ActiveSupport::TestCase
4
- def populate_db!
5
- @animals = [@animal = Animal.new, @dog = Dog.new, @cat = Cat.new]
6
- @animals.each do |animal|
7
- 3.times { animal.update_attribute(:name, FFaker::Name.name) }
8
- end
9
- end
10
-
11
- context "`clean_versions!` method" do
12
- setup { populate_db! }
13
-
14
- should "Baseline" do
15
- assert_equal 9, PaperTrail::Version.count
16
- @animals.each { |animal| assert_equal 3, animal.versions.size }
17
- end
18
-
19
- should "be extended by `PaperTrail` module" do
20
- assert_respond_to PaperTrail, :clean_versions!
21
- end
22
-
23
- context "No options provided" do
24
- should "removes extra versions for each item" do
25
- PaperTrail.clean_versions!
26
- assert_equal 3, PaperTrail::Version.count
27
- @animals.each { |animal| assert_equal 1, animal.versions.size }
28
- end
29
-
30
- should "removes the earliest version(s)" do
31
- before = @animals.map { |animal| animal.versions.last.reify.name }
32
- PaperTrail.clean_versions!
33
- after = @animals.map { |animal| animal.versions.last.reify.name }
34
- assert_equal before, after
35
- end
36
- end
37
-
38
- context "`:keeping` option" do
39
- should "modifies the number of versions ommitted from destruction" do
40
- PaperTrail.clean_versions!(keeping: 2)
41
- assert_equal 6, PaperTrail::Version.all.count
42
- @animals.each { |animal| assert_equal 2, animal.versions.size }
43
- end
44
- end
45
-
46
- context "`:date` option" do
47
- setup do
48
- @animal.versions.each { |ver| ver.update_attribute(:created_at, ver.created_at - 1.day) }
49
- @date = @animal.versions.first.created_at.to_date
50
- @animal.update_attribute(:name, FFaker::Name.name)
51
- end
52
-
53
- should "restrict the versions destroyed to those that were created on the date provided" do
54
- assert_equal 10, PaperTrail::Version.count
55
- assert_equal 4, @animal.versions.size
56
- assert_equal 3, @animal.paper_trail.versions_between(@date, @date + 1.day).size
57
- PaperTrail.clean_versions!(date: @date)
58
- assert_equal 8, PaperTrail::Version.count
59
- assert_equal 2, @animal.versions.reload.size
60
- assert_equal @date, @animal.versions.first.created_at.to_date
61
- assert_not_same @date, @animal.versions.last.created_at.to_date
62
- end
63
- end
64
-
65
- context "`:item_id` option" do
66
- context "single ID received" do
67
- should "restrict the versions destroyed to the versions for the Item with that ID" do
68
- PaperTrail.clean_versions!(item_id: @animal.id)
69
- assert_equal 1, @animal.versions.size
70
- assert_equal 7, PaperTrail::Version.count
71
- end
72
- end
73
-
74
- context "collection of ID's received" do
75
- should "restrict the versions destroyed to the versions for the Item with those ID's" do
76
- PaperTrail.clean_versions!(item_id: [@animal.id, @dog.id])
77
- assert_equal 1, @animal.versions.size
78
- assert_equal 1, @dog.versions.size
79
- assert_equal 5, PaperTrail::Version.count
80
- end
81
- end
82
- end
83
-
84
- context "options combinations" do # additional tests to cover combinations of options
85
- context "`:date`" do
86
- setup do
87
- [@animal, @dog].each do |animal|
88
- animal.versions.each { |ver| ver.update_attribute(:created_at, ver.created_at - 1.day) }
89
- animal.update_attribute(:name, FFaker::Name.name)
90
- end
91
- @date = @animal.versions.first.created_at.to_date
92
- end
93
-
94
- should "Baseline" do
95
- assert_equal 11, PaperTrail::Version.count
96
- [@animal, @dog].each do |animal|
97
- assert_equal 4, animal.versions.size
98
- assert_equal 3, animal.versions.between(@date, @date + 1.day).size
99
- end
100
- end
101
-
102
- context "and `:keeping`" do
103
- should "restrict cleaning properly" do
104
- PaperTrail.clean_versions!(date: @date, keeping: 2)
105
- [@animal, @dog].each do |animal|
106
- # reload the association to pick up the destructions made by the `Cleaner`
107
- animal.versions.reload
108
- assert_equal 3, animal.versions.size
109
- assert_equal 2, animal.versions.between(@date, @date + 1.day).size
110
- end
111
- # ensure that the versions for the `@cat` instance wasn't touched
112
- assert_equal 9, PaperTrail::Version.count
113
- end
114
- end
115
-
116
- context "and `:item_id`" do
117
- should "restrict cleaning properly" do
118
- PaperTrail.clean_versions!(date: @date, item_id: @dog.id)
119
- # reload the association to pick up the destructions made by the `Cleaner`
120
- @dog.versions.reload
121
- assert_equal 2, @dog.versions.size
122
- assert_equal 1, @dog.versions.between(@date, @date + 1.day).size
123
- # ensure the versions for other animals besides `@animal` weren't touched
124
- assert_equal 9, PaperTrail::Version.count
125
- end
126
- end
127
-
128
- context ", `:item_id`, and `:keeping`" do
129
- should "restrict cleaning properly" do
130
- PaperTrail.clean_versions!(date: @date, item_id: @dog.id, keeping: 2)
131
- # reload the association to pick up the destructions made by the `Cleaner`
132
- @dog.versions.reload
133
- assert_equal 3, @dog.versions.size
134
- assert_equal 2, @dog.versions.between(@date, @date + 1.day).size
135
- # ensure the versions for other animals besides `@animal` weren't touched
136
- assert_equal 10, PaperTrail::Version.count
137
- end
138
- end
139
- end
140
-
141
- context "`:keeping` and `:item_id`" do
142
- should "restrict cleaning properly" do
143
- PaperTrail.clean_versions!(keeping: 2, item_id: @animal.id)
144
- assert_equal 2, @animal.versions.size
145
- # ensure the versions for other animals besides `@animal` weren't touched
146
- assert_equal 8, PaperTrail::Version.count
147
- end
148
- end
149
- end
150
- end # clean_versions! method
151
- end
@@ -1,41 +0,0 @@
1
- require "test_helper"
2
-
3
- class InheritanceColumnTest < ActiveSupport::TestCase
4
- context "STI models" do
5
- setup do
6
- @animal = Animal.create name: "Animal"
7
- @animal.update_attributes name: "Animal from the Muppets"
8
- @animal.update_attributes name: "Animal Muppet"
9
- @animal.destroy
10
-
11
- @dog = Dog.create name: "Snoopy"
12
- @dog.update_attributes name: "Scooby"
13
- @dog.update_attributes name: "Scooby Doo"
14
- @dog.destroy
15
-
16
- @cat = Cat.create name: "Garfield"
17
- @cat.update_attributes name: "Garfield (I hate Mondays)"
18
- @cat.update_attributes name: "Garfield The Cat"
19
- @cat.destroy
20
- end
21
-
22
- should "work with custom STI inheritance column" do
23
- assert_equal 12, PaperTrail::Version.count
24
- assert_equal 4, @animal.versions.count
25
- assert_nil @animal.versions.first.reify
26
- @animal.versions[1..-1].each { |v| assert_equal "Animal", v.reify.class.name }
27
-
28
- # For some reason `@dog.versions` doesn't include the final `destroy` version.
29
- # Neither do `@dog.versions.scoped` nor `@dog.versions(true)` nor `@dog.versions.reload`.
30
- dog_versions = PaperTrail::Version.where(item_id: @dog.id).order(:created_at)
31
- assert_equal 4, dog_versions.count
32
- assert_nil dog_versions.first.reify
33
- assert_equal %w(NilClass Dog Dog Dog), dog_versions.map { |v| v.reify.class.name }
34
-
35
- cat_versions = PaperTrail::Version.where(item_id: @cat.id).order(:created_at)
36
- assert_equal 4, cat_versions.count
37
- assert_nil cat_versions.first.reify
38
- assert_equal %w(NilClass Cat Cat Cat), cat_versions.map { |v| v.reify.class.name }
39
- end
40
- end
41
- end
@@ -1,95 +0,0 @@
1
- require "test_helper"
2
-
3
- class JSONTest < ActiveSupport::TestCase
4
- setup do
5
- # Setup a hash with random values
6
- @hash = {}
7
- (1..4).each do |i|
8
- @hash["key#{i}"] = FFaker::Lorem.word
9
- end
10
- @hash_as_json = @hash.to_json
11
- # Setup an array of random words
12
- @array = []
13
- (rand(5) + 4).times { @array << FFaker::Lorem.word }
14
- @array_as_json = @array.to_json
15
- end
16
-
17
- context "`load` class method" do
18
- should "exist" do
19
- assert PaperTrail::Serializers::JSON.respond_to?(:load)
20
- end
21
-
22
- should "`deserialize` JSON to Ruby" do
23
- assert_equal @hash, PaperTrail::Serializers::JSON.load(@hash_as_json)
24
- assert_equal @array, PaperTrail::Serializers::JSON.load(@array_as_json)
25
- end
26
- end
27
-
28
- context "`dump` class method" do
29
- should "exist" do
30
- assert PaperTrail::Serializers::JSON.respond_to?(:dump)
31
- end
32
-
33
- should "`serialize` Ruby to JSON" do
34
- assert_equal @hash_as_json, PaperTrail::Serializers::JSON.dump(@hash)
35
- assert_equal @array_as_json, PaperTrail::Serializers::JSON.dump(@array)
36
- end
37
- end
38
-
39
- context "`where_object` class method" do
40
- context "when value is a string" do
41
- should "construct correct WHERE query" do
42
- matches = PaperTrail::Serializers::JSON.where_object_condition(
43
- PaperTrail::Version.arel_table[:object], :arg1,
44
- "Val 1"
45
- )
46
-
47
- assert matches.instance_of?(Arel::Nodes::Matches)
48
- if Arel::VERSION >= "6"
49
- assert_equal matches.right.val, "%\"arg1\":\"Val 1\"%"
50
- else
51
- assert_equal matches.right, "%\"arg1\":\"Val 1\"%"
52
- end
53
- end
54
- end
55
-
56
- context "when value is `null`" do
57
- should "construct correct WHERE query" do
58
- matches = PaperTrail::Serializers::JSON.where_object_condition(
59
- PaperTrail::Version.arel_table[:object],
60
- :arg1,
61
- nil
62
- )
63
-
64
- assert matches.instance_of?(Arel::Nodes::Matches)
65
- if Arel::VERSION >= "6"
66
- assert_equal matches.right.val, "%\"arg1\":null%"
67
- else
68
- assert_equal matches.right, "%\"arg1\":null%"
69
- end
70
- end
71
- end
72
-
73
- context "when value is a number" do
74
- should "construct correct WHERE query" do
75
- grouping = PaperTrail::Serializers::JSON.where_object_condition(
76
- PaperTrail::Version.arel_table[:object],
77
- :arg1,
78
- -3.5
79
- )
80
-
81
- assert grouping.instance_of?(Arel::Nodes::Grouping)
82
- matches = grouping.select { |v| v.instance_of?(Arel::Nodes::Matches) }
83
- # Numeric arguments need to ensure that they match for only the number, not the beginning
84
- # of a #, so it uses an Grouping matcher (See notes on `PaperTrail::Serializers::JSON`)
85
- if Arel::VERSION >= "6"
86
- assert_equal matches.first.right.val, "%\"arg1\":-3.5,%"
87
- assert_equal matches.last.right.val, "%\"arg1\":-3.5}%"
88
- else
89
- assert_equal matches.first.right, "%\"arg1\":-3.5,%"
90
- assert_equal matches.last.right, "%\"arg1\":-3.5}%"
91
- end
92
- end
93
- end
94
- end
95
- end
@@ -1,53 +0,0 @@
1
- require "test_helper"
2
-
3
- module CustomYamlSerializer
4
- extend PaperTrail::Serializers::YAML
5
-
6
- def self.load(string)
7
- parsed_value = super(string)
8
- if parsed_value.is_a?(Hash)
9
- parsed_value.reject { |k, v| k.blank? || v.blank? }
10
- else
11
- parsed_value
12
- end
13
- end
14
-
15
- def self.dump(object)
16
- object.is_a?(Hash) ? super(object.reject { |_k, v| v.nil? }) : super
17
- end
18
- end
19
-
20
- class MixinYamlTest < ActiveSupport::TestCase
21
- setup do
22
- # Setup a hash with random values, ensuring some values are nil
23
- @hash = {}
24
- (1..4).each do |i|
25
- @hash["key#{i}"] = [FFaker::Lorem.word, nil].sample
26
- end
27
- @hash["tkey"] = nil
28
- @hash[""] = "foo"
29
- @hash_as_yaml = @hash.to_yaml
30
- end
31
-
32
- context "`load` class method" do
33
- should "exist" do
34
- assert CustomYamlSerializer.respond_to?(:load)
35
- end
36
-
37
- should "`deserialize` YAML to Ruby, removing pairs with `blank` keys or values" do
38
- assert_equal @hash.reject { |k, v| k.blank? || v.blank? },
39
- CustomYamlSerializer.load(@hash_as_yaml)
40
- end
41
- end
42
-
43
- context "`dump` class method" do
44
- should "exist" do
45
- assert CustomYamlSerializer.respond_to?(:dump)
46
- end
47
-
48
- should "`serialize` Ruby to YAML, removing pairs with `nil` values" do
49
- assert_equal @hash.reject { |_k, v| v.nil? }.to_yaml,
50
- CustomYamlSerializer.dump(@hash)
51
- end
52
- end
53
- end