diff --git a/CHANGELOG.md b/CHANGELOG.md index 22f552b8..cf756a27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,12 +25,14 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/). ### Deprecated +- [#1063](https://github.com/airblade/paper_trail/pull/1063) - `touch_with_version` is deprecated in favor of using the original AR `touch` method - [#1033](https://github.com/airblade/paper_trail/pull/1033) - Request variables are now set using eg. `PaperTrail.request.whodunnit=` and the old way, `PaperTrail.whodunnit=` is deprecated. ### Added +- [#1063](https://github.com/airblade/paper_trail/pull/1063) - AR `touch` method now creates a version if the `:on` option includes `touch`. By default it also includes the `touch` event now. - [#1033](https://github.com/airblade/paper_trail/pull/1033) - Set request variables temporarily using a block, eg. `PaperTrail.request(whodunnit: 'Jared') do .. end` diff --git a/README.md b/README.md index cfe5d8c5..fa15e8d1 100644 --- a/README.md +++ b/README.md @@ -207,10 +207,6 @@ widget.paper_trail.previous_version # Returns the widget (not a version) as it became next. widget.paper_trail.next_version -# Generates a version for a `touch` event (`widget.touch` does NOT generate a -# version) -widget.paper_trail.touch_with_version - # Enable/disable PaperTrail, for Widget, for the current request (not all threads) PaperTrail.request.disable_model(Widget) PaperTrail.request.enable_model(Widget) @@ -292,7 +288,7 @@ ignore `create` events: ```ruby class Article < ActiveRecord::Base - has_paper_trail on: [:update, :destroy] + has_paper_trail on: [:update, :destroy, :touch] end ``` @@ -337,6 +333,7 @@ class Article < ActiveRecord::Base paper_trail.on_destroy # add destroy callback paper_trail.on_update # etc. paper_trail.on_create + paper_trail.on_touch end ``` @@ -706,6 +703,22 @@ sql> delete from versions where created_at < 2010-06-01; PaperTrail::Version.delete_all ['created_at < ?', 1.week.ago] ``` +### 3.e. Trigger Version creation + +At some point you may want to trigger a new version to be created. To do this we utilize the AR `touch` method. + +```ruby +widget.touch +``` + +The new versions event will be saved as `update`. + +If you are using the `:on` option in your model you must specify the `touch` event also. For example: + +```ruby +has_paper_trail on: [:update, :destroy, :touch] +``` + ## 4. Saving More Information About Versions ### 4.a. Finding Out Who Was Responsible For A Change diff --git a/lib/paper_trail/model_config.rb b/lib/paper_trail/model_config.rb index f84b9559..c6cd16c8 100644 --- a/lib/paper_trail/model_config.rb +++ b/lib/paper_trail/model_config.rb @@ -116,11 +116,19 @@ module PaperTrail @model_class.paper_trail_options[:on] << :update end + # Adds a callback that records a version after a "touch" event. + # @api public + def on_touch + @model_class.after_touch { |r| + r.paper_trail.record_update(force: true, in_after_callback: true) + } + end + # Set up `@model_class` for PaperTrail. Installs callbacks, associations, # "class attributes", instance methods, and more. # @api private def setup(options = {}) - options[:on] ||= %i[create update destroy] + options[:on] ||= %i[create update destroy touch] options[:on] = Array(options[:on]) # Support single symbol @model_class.send :include, ::PaperTrail::Model::InstanceMethods setup_options(options) diff --git a/lib/paper_trail/record_trail.rb b/lib/paper_trail/record_trail.rb index e0b9016d..d38969d7 100644 --- a/lib/paper_trail/record_trail.rb +++ b/lib/paper_trail/record_trail.rb @@ -7,6 +7,12 @@ module PaperTrail my_model_instance.paper_trail.whodunnit('John') is deprecated, please use PaperTrail.request(whodunnit: 'John') STR + + DPR_TOUCH_WITH_VERSION = <<-STR.squish.freeze + my_model_instance.paper_trail.touch_with_version is deprecated, + please use my_model_instance.touch + STR + RAILS_GTE_5_1 = ::ActiveRecord.gem_version >= ::Gem::Version.new("5.1.0.beta1") def initialize(record) @@ -461,8 +467,9 @@ module PaperTrail # version records are inserted. It's unclear under which specific # circumstances this technique should be adopted. # - # @api public + # @deprecated def touch_with_version(name = nil) + ::ActiveSupport::Deprecation.warn(DPR_TOUCH_WITH_VERSION, caller(1)) unless @record.persisted? raise ::ActiveRecord::ActiveRecordError, "can not touch on a new record object" end @@ -571,9 +578,8 @@ module PaperTrail if @in_after_callback @record.attribute_before_last_save(attr_name.to_s) else - # Either we are doing a `touch_with_version` or `record_destroy`. - # Other events, like `record_create`, can only be done in an - # after-callback. + # We are performing a `record_destroy`. Other events, + # like `record_create`, can only be done in an after-callback. @record.attribute_in_database(attr_name.to_s) end else diff --git a/spec/models/article_spec.rb b/spec/models/article_spec.rb index 3bc67fb5..73e412fb 100644 --- a/spec/models/article_spec.rb +++ b/spec/models/article_spec.rb @@ -189,9 +189,12 @@ RSpec.describe Article, type: :model, versioning: true do describe "#touch_with_version" do it "creates a version, ignoring the :only option" do article = described_class.create + + allow(::ActiveSupport::Deprecation).to receive(:warn) expect { article.paper_trail.touch_with_version }.to change { ::PaperTrail::Version.count }.by(+1) + expect(::ActiveSupport::Deprecation).to have_received(:warn).once end end end diff --git a/spec/models/callback_modifier_spec.rb b/spec/models/callback_modifier_spec.rb index f7683e33..e4344e18 100644 --- a/spec/models/callback_modifier_spec.rb +++ b/spec/models/callback_modifier_spec.rb @@ -67,7 +67,7 @@ RSpec.describe CallbackModifier, type: :model, versioning: true do context "when no callback-method used" do it "sets paper_trail_options[:on] to [:create, :update, :destroy]" do modifier = DefaultModifier.create!(some_content: FFaker::Lorem.sentence) - expect(modifier.paper_trail_options[:on]).to eq %i[create update destroy] + expect(modifier.paper_trail_options[:on]).to eq %i[create update destroy touch] end it "tracks destroy" do diff --git a/spec/models/not_on_update_spec.rb b/spec/models/not_on_update_spec.rb index 23c62422..ebf64b5c 100644 --- a/spec/models/not_on_update_spec.rb +++ b/spec/models/not_on_update_spec.rb @@ -7,17 +7,21 @@ RSpec.describe NotOnUpdate, type: :model do let!(:record) { described_class.create! } it "creates a version, regardless" do + allow(::ActiveSupport::Deprecation).to receive(:warn) expect { record.paper_trail.touch_with_version }.to change { PaperTrail::Version.count }.by(+1) + expect(::ActiveSupport::Deprecation).to have_received(:warn).once end it "increments the `:updated_at` timestamp" do before = record.updated_at + allow(::ActiveSupport::Deprecation).to receive(:warn) # Travel 1 second because MySQL lacks sub-second resolution Timecop.travel(1) do record.paper_trail.touch_with_version end + expect(::ActiveSupport::Deprecation).to have_received(:warn).once expect(record.updated_at).to be > before end end diff --git a/spec/models/on/empty_array_spec.rb b/spec/models/on/empty_array_spec.rb index acf61f6f..ac772fe0 100644 --- a/spec/models/on/empty_array_spec.rb +++ b/spec/models/on/empty_array_spec.rb @@ -15,7 +15,9 @@ module On describe "#touch_with_version" do it "creates a version record" do record = described_class.create(name: "Alice") + allow(::ActiveSupport::Deprecation).to receive(:warn) record.paper_trail.touch_with_version + expect(::ActiveSupport::Deprecation).to have_received(:warn).once expect(record.versions.length).to(eq(1)) v = record.versions.first expect(v.event).to(eq("update")) diff --git a/spec/models/on/update_spec.rb b/spec/models/on/update_spec.rb index fb3fda99..d97f8165 100644 --- a/spec/models/on/update_spec.rb +++ b/spec/models/on/update_spec.rb @@ -25,5 +25,13 @@ module On expect(record.versions.last.event).to(eq("banana")) end end + + describe "#touch" do + it "does not create a version for the touch event" do + record = described_class.create(name: "Alice") + count = record.versions.count + expect(count).to eq(count) + end + end end end diff --git a/spec/models/post_with_status_spec.rb b/spec/models/post_with_status_spec.rb index fc443359..df047736 100644 --- a/spec/models/post_with_status_spec.rb +++ b/spec/models/post_with_status_spec.rb @@ -37,7 +37,9 @@ RSpec.describe PostWithStatus, type: :model do expect(post.versions.count).to eq(1) expect(post.status).to eq("draft") Timecop.travel 1.second.since # because MySQL lacks fractional seconds precision + allow(::ActiveSupport::Deprecation).to receive(:warn) post.paper_trail.touch_with_version + expect(::ActiveSupport::Deprecation).to have_received(:warn).once expect(post.versions.count).to eq(2) expect(post.versions.last[:object]).to include("status: 0") expect(post.paper_trail.previous_version.status).to eq("draft") diff --git a/spec/models/widget_spec.rb b/spec/models/widget_spec.rb index 753e0403..d2514f85 100644 --- a/spec/models/widget_spec.rb +++ b/spec/models/widget_spec.rb @@ -232,21 +232,40 @@ RSpec.describe Widget, type: :model do describe "#touch_with_version", versioning: true do it "creates a version" do + allow(::ActiveSupport::Deprecation).to receive(:warn) count = widget.versions.size - # Travel 1 second because MySQL lacks sub-second resolution Timecop.travel(1) do widget.paper_trail.touch_with_version end expect(widget.versions.size).to eq(count + 1) + expect(::ActiveSupport::Deprecation).to have_received(:warn).once end it "increments the `:updated_at` timestamp" do + allow(::ActiveSupport::Deprecation).to receive(:warn) time_was = widget.updated_at # Travel 1 second because MySQL lacks sub-second resolution Timecop.travel(1) do widget.paper_trail.touch_with_version end expect(widget.updated_at).to be > time_was + expect(::ActiveSupport::Deprecation).to have_received(:warn).once + end + end + + describe "touch", versioning: true do + it "creates a version" do + expect { widget.touch }.to change { + widget.versions.count + }.by(+1) + end + + it "does not create a version using without_versioning" do + count = widget.versions.count + widget.paper_trail.without_versioning do + widget.touch + end + expect(count).to eq(count) end end diff --git a/spec/paper_trail/model_spec.rb b/spec/paper_trail/model_spec.rb index ee2bd9ba..68aca8fd 100644 --- a/spec/paper_trail/model_spec.rb +++ b/spec/paper_trail/model_spec.rb @@ -56,15 +56,15 @@ RSpec.describe(::PaperTrail, versioning: true) do context "and then updated without any changes" do before { @widget.touch } - it "not have a new version" do - expect(@widget.versions.length).to(eq(1)) + it "to have two previous versions" do + expect(@widget.versions.length).to(eq(2)) end end context "and then updated with changes" do before { @widget.update_attributes(name: "Harry") } - it "have two previous versions" do + it "have three previous versions" do expect(@widget.versions.length).to(eq(2)) end @@ -381,7 +381,9 @@ RSpec.describe(::PaperTrail, versioning: true) do context "given a symbol, specifying a method name" do it "does not create a new version" do + allow(::ActiveSupport::Deprecation).to receive(:warn) @widget.paper_trail.without_versioning(:touch_with_version) + expect(::ActiveSupport::Deprecation).to have_received(:warn).once expect(@widget.versions.length).to(eq(@count)) expect(::PaperTrail.request.enabled_for_model?(Widget)).to eq(true) end