paper_trail 7.0.2 → 7.0.3

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CONTRIBUTING.md +1 -1
  3. data/.rubocop_todo.yml +10 -0
  4. data/.travis.yml +4 -4
  5. data/Appraisals +3 -5
  6. data/CHANGELOG.md +16 -1
  7. data/README.md +70 -119
  8. data/Rakefile +6 -1
  9. data/doc/bug_report_template.rb +4 -2
  10. data/doc/warning_about_not_setting_whodunnit.md +6 -5
  11. data/gemfiles/ar_4.0.gemfile +1 -1
  12. data/gemfiles/ar_4.2.gemfile +1 -1
  13. data/gemfiles/ar_5.0.gemfile +2 -3
  14. data/gemfiles/ar_5.1.gemfile +8 -0
  15. data/gemfiles/ar_master.gemfile +2 -2
  16. data/lib/generators/paper_trail/templates/add_object_changes_to_versions.rb.erb +1 -1
  17. data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb.erb +1 -1
  18. data/lib/generators/paper_trail/templates/create_version_associations.rb.erb +1 -1
  19. data/lib/paper_trail/model_config.rb +4 -4
  20. data/lib/paper_trail/version_concern.rb +4 -4
  21. data/lib/paper_trail/version_number.rb +1 -1
  22. data/paper_trail.gemspec +5 -5
  23. data/spec/controllers/articles_controller_spec.rb +1 -1
  24. data/spec/generators/install_generator_spec.rb +2 -2
  25. data/spec/models/animal_spec.rb +5 -5
  26. data/spec/models/boolit_spec.rb +2 -2
  27. data/spec/models/callback_modifier_spec.rb +2 -2
  28. data/spec/models/car_spec.rb +2 -2
  29. data/spec/models/custom_primary_key_record_spec.rb +2 -2
  30. data/spec/models/document_spec.rb +2 -2
  31. data/spec/models/gadget_spec.rb +2 -2
  32. data/spec/models/joined_version_spec.rb +1 -1
  33. data/spec/models/json_version_spec.rb +4 -4
  34. data/spec/models/kitchen/banana_spec.rb +2 -2
  35. data/spec/models/not_on_update_spec.rb +2 -2
  36. data/spec/models/post_with_status_spec.rb +4 -4
  37. data/spec/models/skipper_spec.rb +1 -1
  38. data/spec/models/thing_spec.rb +2 -2
  39. data/spec/models/vehicle_spec.rb +2 -2
  40. data/spec/models/version_spec.rb +26 -5
  41. data/spec/models/widget_spec.rb +10 -2
  42. data/spec/modules/paper_trail_spec.rb +2 -2
  43. data/spec/modules/version_concern_spec.rb +2 -2
  44. data/spec/modules/version_number_spec.rb +1 -1
  45. data/spec/paper_trail/associations_spec.rb +965 -0
  46. data/spec/paper_trail/cleaner_spec.rb +2 -2
  47. data/spec/paper_trail/config_spec.rb +2 -2
  48. data/spec/paper_trail/model_spec.rb +1421 -0
  49. data/spec/paper_trail/serializer_spec.rb +85 -0
  50. data/spec/paper_trail/serializers/custom_yaml_serializer_spec.rb +1 -1
  51. data/spec/paper_trail/serializers/json_spec.rb +2 -2
  52. data/spec/paper_trail/serializers/yaml_spec.rb +42 -0
  53. data/spec/paper_trail/version_limit_spec.rb +2 -2
  54. data/spec/paper_trail/version_spec.rb +96 -0
  55. data/spec/paper_trail_spec.rb +1 -1
  56. data/spec/requests/articles_spec.rb +2 -2
  57. data/spec/spec_helper.rb +47 -79
  58. data/{test → spec/support}/custom_json_serializer.rb +0 -0
  59. data/test/dummy/app/models/document.rb +1 -1
  60. data/test/dummy/app/models/not_on_update.rb +1 -1
  61. data/test/dummy/app/models/widget.rb +1 -1
  62. data/test/dummy/config/routes.rb +1 -1
  63. data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +18 -9
  64. data/test/dummy/db/schema.rb +64 -64
  65. data/test/test_helper.rb +1 -33
  66. data/test/unit/serializers/mixin_json_test.rb +1 -1
  67. metadata +27 -32
  68. data/spec/models/truck_spec.rb +0 -5
  69. data/spec/rails_helper.rb +0 -34
  70. data/test/time_travel_helper.rb +0 -1
  71. data/test/unit/associations_test.rb +0 -1032
  72. data/test/unit/model_test.rb +0 -1416
  73. data/test/unit/serializer_test.rb +0 -107
  74. data/test/unit/serializers/yaml_test.rb +0 -50
  75. data/test/unit/version_test.rb +0 -112
@@ -0,0 +1,85 @@
1
+ require "spec_helper"
2
+ require "support/custom_json_serializer"
3
+
4
+ RSpec.describe(PaperTrail, versioning: true) do
5
+ context "YAML serializer" do
6
+ it "saves the expected YAML in the object column" do
7
+ customer = Customer.create(name: "Some text.")
8
+ original_attributes = customer.paper_trail.attributes_before_change
9
+ customer.update(name: "Some more text.")
10
+ expect(customer.versions.length).to(eq(2))
11
+ expect(customer.versions[0].reify).to(be_nil)
12
+ expect(customer.versions[1].reify.name).to(eq("Some text."))
13
+ expect(YAML.load(customer.versions[1].object)).to(eq(original_attributes))
14
+ expect(customer.versions[1].object).to(eq(YAML.dump(original_attributes)))
15
+ end
16
+ end
17
+
18
+ context "JSON Serializer" do
19
+ before do
20
+ PaperTrail.configure do |config|
21
+ config.serializer = PaperTrail::Serializers::JSON
22
+ end
23
+ end
24
+
25
+ after do
26
+ PaperTrail.config.serializer = PaperTrail::Serializers::YAML
27
+ end
28
+
29
+ it "reify with JSON serializer" do
30
+ customer = Customer.create(name: "Some text.")
31
+ original_attributes = customer.paper_trail.attributes_before_change
32
+ customer.update(name: "Some more text.")
33
+ expect(customer.versions.length).to(eq(2))
34
+ expect(customer.versions[0].reify).to(be_nil)
35
+ expect(customer.versions[1].reify.name).to(eq("Some text."))
36
+ expect(ActiveSupport::JSON.decode(customer.versions[1].object)).to(eq(original_attributes))
37
+ expect(customer.versions[1].object).to(eq(ActiveSupport::JSON.encode(original_attributes)))
38
+ end
39
+
40
+ describe "#changeset" do
41
+ it "returns the expected hash" do
42
+ customer = Customer.create(name: "Some text.")
43
+ customer.update(name: "Some more text.")
44
+ initial_changeset = { "name" => [nil, "Some text."], "id" => [nil, customer.id] }
45
+ second_changeset = { "name" => ["Some text.", "Some more text."] }
46
+ expect(customer.versions[0].changeset).to(eq(initial_changeset))
47
+ expect(customer.versions[1].changeset).to(eq(second_changeset))
48
+ end
49
+ end
50
+ end
51
+
52
+ context "Custom Serializer" do
53
+ before do
54
+ PaperTrail.configure { |config| config.serializer = CustomJsonSerializer }
55
+ end
56
+
57
+ after do
58
+ PaperTrail.config.serializer = PaperTrail::Serializers::YAML
59
+ end
60
+
61
+ it "reify with custom serializer" do
62
+ customer = Customer.create
63
+ original_attributes = customer.paper_trail.attributes_before_change.reject { |_k, v| v.nil? }
64
+ customer.update(name: "Some more text.")
65
+ expect(customer.versions.length).to(eq(2))
66
+ expect(customer.versions[0].reify).to(be_nil)
67
+ expect(customer.versions[1].reify.name).to(be_nil)
68
+ expect(
69
+ ActiveSupport::JSON.decode(customer.versions[1].object)
70
+ ).to eq(original_attributes)
71
+ expect(
72
+ customer.versions[1].object
73
+ ).to eq(ActiveSupport::JSON.encode(original_attributes))
74
+ end
75
+
76
+ describe "#changeset" do
77
+ it "store object_changes" do
78
+ customer = Customer.create
79
+ customer.update(name: "banana")
80
+ expect(customer.versions[0].changeset).to eq("id" => [nil, customer.id])
81
+ expect(customer.versions[1].changeset).to eq("name" => [nil, "banana"])
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1,4 +1,4 @@
1
- require "rails_helper"
1
+ require "spec_helper"
2
2
 
3
3
  module CustomYamlSerializer
4
4
  extend PaperTrail::Serializers::YAML
@@ -1,8 +1,8 @@
1
- require "rails_helper"
1
+ require "spec_helper"
2
2
 
3
3
  module PaperTrail
4
4
  module Serializers
5
- RSpec.describe JSON do
5
+ ::RSpec.describe JSON do
6
6
  let(:word_hash) {
7
7
  (1..4).each_with_object({}) { |i, a| a["key#{i}"] = ::FFaker::Lorem.word }
8
8
  }
@@ -0,0 +1,42 @@
1
+ require "spec_helper"
2
+
3
+ module PaperTrail
4
+ module Serializers
5
+ ::RSpec.describe(YAML, versioning: true) do
6
+ let(:array) { ::Array.new(10) { ::FFaker::Lorem.word } }
7
+ let(:hash) {
8
+ {
9
+ alice: "bob",
10
+ binary: 0xdeadbeef,
11
+ octal_james_bond: 0o7,
12
+ int: 42,
13
+ float: 4.2
14
+ }
15
+ }
16
+
17
+ describe ".load" do
18
+ it "deserializes YAML to Ruby" do
19
+ expect(described_class.load(hash.to_yaml)).to eq(hash)
20
+ expect(described_class.load(array.to_yaml)).to eq(array)
21
+ end
22
+ end
23
+
24
+ describe ".dump" do
25
+ it "serializes Ruby to YAML" do
26
+ expect(described_class.dump(hash)).to eq(hash.to_yaml)
27
+ expect(described_class.dump(array)).to eq(array.to_yaml)
28
+ end
29
+ end
30
+
31
+ describe ".where_object" do
32
+ it "constructs the correct WHERE query" do
33
+ matches = described_class.where_object_condition(
34
+ ::PaperTrail::Version.arel_table[:object], :arg1, "Val 1"
35
+ )
36
+ expect(matches.instance_of?(Arel::Nodes::Matches)).to(eq(true))
37
+ expect(matches.right.val).to eq("%\narg1: Val 1\n%")
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,7 +1,7 @@
1
- require "rails_helper"
1
+ require "spec_helper"
2
2
 
3
3
  module PaperTrail
4
- RSpec.describe Cleaner, versioning: true do
4
+ ::RSpec.describe Cleaner, versioning: true do
5
5
  after do
6
6
  PaperTrail.config.version_limit = nil
7
7
  end
@@ -0,0 +1,96 @@
1
+ require "spec_helper"
2
+
3
+ module PaperTrail
4
+ ::RSpec.describe(Version, versioning: true) do
5
+ describe ".creates" do
6
+ it "returns only create events" do
7
+ animal = Animal.create(name: "Foo")
8
+ animal.update_attributes(name: "Bar")
9
+ expect(described_class.creates.pluck(:event)).to eq(["create"])
10
+ end
11
+ end
12
+
13
+ describe ".updates" do
14
+ it "returns only update events" do
15
+ animal = Animal.create
16
+ animal.update_attributes(name: "Animal")
17
+ expect(described_class.updates.pluck(:event)).to eq(["update"])
18
+ end
19
+ end
20
+
21
+ describe ".destroys" do
22
+ it "returns only destroy events" do
23
+ animal = Animal.create
24
+ animal.destroy
25
+ expect(described_class.destroys.pluck(:event)).to eq(["destroy"])
26
+ end
27
+ end
28
+
29
+ describe ".not_creates" do
30
+ it "returns all versions except create events" do
31
+ animal = Animal.create
32
+ animal.update_attributes(name: "Animal")
33
+ animal.destroy
34
+ expect(
35
+ described_class.not_creates.pluck(:event)
36
+ ).to match_array(%w[update destroy])
37
+ end
38
+ end
39
+
40
+ describe ".subsequent" do
41
+ context "given a timestamp" do
42
+ it "returns all versions that were created after the timestamp" do
43
+ animal = Animal.create
44
+ 2.times do
45
+ animal.update_attributes(name: FFaker::Lorem.word)
46
+ end
47
+ value = described_class.subsequent(1.hour.ago, true)
48
+ expect(value).to eq(animal.versions.to_a)
49
+ expect(value.to_sql).to match(
50
+ /ORDER BY #{described_class.arel_table[:created_at].asc.to_sql}/
51
+ )
52
+ end
53
+ end
54
+
55
+ context "given a Version" do
56
+ it "grab the timestamp from the version and use that as the value" do
57
+ animal = Animal.create
58
+ 2.times do
59
+ animal.update_attributes(name: FFaker::Lorem.word)
60
+ end
61
+ expect(described_class.subsequent(animal.versions.first)).to eq(
62
+ animal.versions.to_a.drop(1)
63
+ )
64
+ end
65
+ end
66
+ end
67
+
68
+ describe ".preceding" do
69
+ context "given a timestamp" do
70
+ it "returns all versions that were created before the timestamp" do
71
+ animal = Animal.create
72
+ 2.times do
73
+ animal.update_attributes(name: FFaker::Lorem.word)
74
+ end
75
+ value = described_class.preceding(5.seconds.from_now, true)
76
+ expect(value).to eq(animal.versions.reverse)
77
+ expect(value.to_sql).to match(
78
+ /ORDER BY #{described_class.arel_table[:created_at].desc.to_sql}/
79
+ )
80
+ end
81
+ end
82
+
83
+ context "given a Version" do
84
+ it "grab the timestamp from the version and use that as the value" do
85
+ animal = Animal.create
86
+ 2.times do
87
+ animal.update_attributes(name: FFaker::Lorem.word)
88
+ end
89
+ expect(described_class.preceding(animal.versions.last)).to eq(
90
+ animal.versions.to_a.tap(&:pop).reverse
91
+ )
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -1,4 +1,4 @@
1
- require "rails_helper"
1
+ require "spec_helper"
2
2
 
3
3
  RSpec.describe PaperTrail do
4
4
  describe ".gem_version" do
@@ -1,6 +1,6 @@
1
- require "rails_helper"
1
+ require "spec_helper"
2
2
 
3
- describe "Articles management", type: :request, order: :defined do
3
+ RSpec.describe "Articles management", type: :request, order: :defined do
4
4
  let(:valid_params) { { article: { title: "Doh", content: FFaker::Lorem.sentence } } }
5
5
 
6
6
  context "versioning disabled" do
@@ -1,42 +1,19 @@
1
- require "pry-nav"
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV["RAILS_ENV"] ||= "test"
3
+ ENV["DB"] ||= "sqlite"
4
+
5
+ unless File.exist?(File.expand_path("../../test/dummy/config/database.yml", __FILE__))
6
+ warn "WARNING: No database.yml detected for the dummy app, please run `rake prepare` first"
7
+ end
8
+
9
+ require "pry"
2
10
 
3
- # This file was generated by the `rspec --init` command. Conventionally, all
4
- # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
5
- # The generated `.rspec` file contains `--require spec_helper` which will cause this
6
- # file to always be loaded, without a need to explicitly require it in any files.
7
- #
8
- # Given that it is always loaded, you are encouraged to keep this file as
9
- # light-weight as possible. Requiring heavyweight dependencies from this file
10
- # will add to the boot time of your test suite on EVERY test run, even for an
11
- # individual file that may not need all of that loaded. Instead, consider making
12
- # a separate helper file that requires the additional dependencies and performs
13
- # the additional setup, and require it from the spec files that actually need it.
14
- #
15
- # The `.rspec` file also contains a few flags that are not defaults but that
16
- # users commonly want.
17
- #
18
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
11
  RSpec.configure do |config|
20
- # rspec-expectations config goes here. You can use an alternate
21
- # assertion/expectation library such as wrong or the stdlib/minitest
22
- # assertions if you prefer.
23
12
  config.expect_with :rspec do |expectations|
24
- # This option will default to `true` in RSpec 4. It makes the `description`
25
- # and `failure_message` of custom matchers include text for helper methods
26
- # defined using `chain`, e.g.:
27
- # be_bigger_than(2).and_smaller_than(4).description
28
- # # => "be bigger than 2 and smaller than 4"
29
- # ...rather than:
30
- # # => "be bigger than 2"
31
13
  expectations.include_chain_clauses_in_custom_matcher_descriptions = true
32
14
  end
33
15
 
34
- # rspec-mocks config goes here. You can use an alternate test double
35
- # library (such as bogus or mocha) by changing the `mock_with` option here.
36
16
  config.mock_with :rspec do |mocks|
37
- # Prevents you from mocking or stubbing a method that does not exist on
38
- # a real object. This is generally recommended, and will default to
39
- # `true` in RSpec 4.
40
17
  mocks.verify_partial_doubles = true
41
18
  end
42
19
 
@@ -49,53 +26,15 @@ RSpec.configure do |config|
49
26
  end
50
27
  end
51
28
 
52
- # The settings below are suggested to provide a good initial experience
53
- # with RSpec, but feel free to customize to your heart's content.
54
-
55
- # These two settings work together to allow you to limit a spec run
56
- # to individual examples or groups you care about by tagging them with
57
- # `:focus` metadata. When nothing is tagged with `:focus`, all examples
58
- # get run.
59
- # config.filter_run :focus
60
- # config.run_all_when_everything_filtered = true
61
-
62
- # Limits the available syntax to the non-monkey patched syntax that is recommended.
63
- # For more details, see:
64
- # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
65
- # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
66
- # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
67
- # config.disable_monkey_patching!
68
-
69
- # This setting enables warnings. It's recommended, but in some cases may
70
- # be too noisy due to issues in dependencies.
71
- # config.warnings = true
72
-
73
- # Many RSpec users commonly either run the entire suite or an individual
74
- # file, and it's useful to allow more verbose output when running an
75
- # individual spec file.
76
- # if config.files_to_run.one?
77
- # # Use the documentation formatter for detailed output,
78
- # # unless a formatter has already been configured
79
- # # (e.g. via a command-line flag).
80
- # config.default_formatter = 'doc'
81
- # end
82
-
83
- # Print the 10 slowest examples and example groups at the
84
- # end of the spec run, to help surface which specs are running
85
- # particularly slow.
86
- # config.profile_examples = 10
87
-
88
- # Run specs in random order to surface order dependencies. If you find an
89
- # order dependency and want to debug it, you can fix the order by providing
90
- # the seed, which is printed after each run.
91
- # --seed 1234
92
- # config.order = :random
93
-
94
- # Seed global randomization in this process using the `--seed` CLI option.
95
- # Setting this allows you to use `--seed` to deterministically reproduce
96
- # test failures related to randomization by passing the same `--seed` value
97
- # as the one that triggered the failure.
98
- # Kernel.srand config.seed
29
+ config.filter_run :focus
30
+ config.run_all_when_everything_filtered = true
31
+ config.disable_monkey_patching!
32
+ config.warnings = false
33
+ if config.files_to_run.one?
34
+ config.default_formatter = "doc"
35
+ end
36
+ config.order = :random
37
+ Kernel.srand config.seed
99
38
  end
100
39
 
101
40
  def active_record_gem_version
@@ -112,3 +51,32 @@ def params_wrapper(args)
112
51
  args
113
52
  end
114
53
  end
54
+
55
+ require File.expand_path("../../test/dummy/config/environment", __FILE__)
56
+ require "rspec/rails"
57
+ require "paper_trail/frameworks/rspec"
58
+ require "ffaker"
59
+ require "timecop"
60
+
61
+ # prevent Test::Unit's AutoRunner from executing during RSpec's rake task
62
+ Test::Unit.run = true if defined?(Test::Unit) && Test::Unit.respond_to?(:run=)
63
+
64
+ # Checks for pending migrations before tests are run.
65
+ # If you are not using ActiveRecord, you can remove this line.
66
+ ActiveRecord::Migration.check_pending! if ActiveRecord::Migration.respond_to?(:check_pending!)
67
+
68
+ require "database_cleaner"
69
+ DatabaseCleaner.strategy = :truncation
70
+
71
+ RSpec.configure do |config|
72
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
73
+ config.use_transactional_fixtures = active_record_gem_version >= ::Gem::Version.new("5")
74
+
75
+ # In rails < 5, some tests seem to require DatabaseCleaner-truncation.
76
+ # Truncation is about three times slower than transaction rollback, so it'll
77
+ # be nice when we can drop support for rails < 5.
78
+ if active_record_gem_version < ::Gem::Version.new("5")
79
+ config.before(:each) { DatabaseCleaner.start }
80
+ config.after(:each) { DatabaseCleaner.clean }
81
+ end
82
+ end
@@ -1,6 +1,6 @@
1
1
  class Document < ActiveRecord::Base
2
2
  has_paper_trail(
3
3
  versions: :paper_trail_versions,
4
- on: %i(create update)
4
+ on: %i[create update]
5
5
  )
6
6
  end
@@ -1,4 +1,4 @@
1
1
  # This model does not record versions when updated.
2
2
  class NotOnUpdate < ActiveRecord::Base
3
- has_paper_trail on: %i(create destroy)
3
+ has_paper_trail on: %i[create destroy]
4
4
  end
@@ -2,7 +2,7 @@ class Widget < ActiveRecord::Base
2
2
  EXCLUDED_NAME = "Biglet".freeze
3
3
  has_paper_trail
4
4
  has_one :wotsit
5
- has_many :fluxors, -> { order(:name) }
5
+ has_many(:fluxors, -> { order(:name) })
6
6
  has_many :whatchamajiggers, as: :owner
7
7
  validates :name, exclusion: { in: [EXCLUDED_NAME] }
8
8
  end
@@ -1,4 +1,4 @@
1
1
  Dummy::Application.routes.draw do
2
2
  resources :articles, only: [:create]
3
- resources :widgets, only: %i(create update destroy)
3
+ resources :widgets, only: %i[create update destroy]
4
4
  end