paper_trail 7.0.2 → 7.0.3

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