paper_trail 6.0.2 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
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