merge airblade/paper_trail branch Master into master and fix conflicts
This commit is contained in:
commit
2e688a8794
|
@ -13,6 +13,7 @@ gemfile:
|
|||
- gemfiles/3.0.gemfile
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- rvm: jruby-18mode
|
||||
gemfile: Gemfile
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
## 3.0.1 (Unreleased)
|
||||
|
||||
- [#340](https://github.com/airblade/paper_trail/issues/340) - Prevent potential error encountered when using the `InstallGenerator`
|
||||
with Rails `4.1.0.rc1`.
|
||||
- [#334](https://github.com/airblade/paper_trail/pull/334) - Add small-scope `whodunnit` method to `PaperTrail::Model::InstanceMethods`.
|
||||
- [#329](https://github.com/airblade/paper_trail/issues/329) - Add `touch_with_version` method to `PaperTrail::Model::InstanceMethods`,
|
||||
to allow for generating a version `touch`ing a model.
|
||||
- [#328](https://github.com/airblade/paper_trail/pull/328) / [#326](https://github.com/airblade/paper_trail/issues/326) /
|
||||
|
@ -9,6 +12,9 @@
|
|||
instead of `current_user` (if `current_user` is defined).
|
||||
- [#313](https://github.com/airblade/paper_trail/pull/313) - Make the `Rails::Controller` helper compatible with
|
||||
`ActionController::API` for compatibility with the [`rails-api`](https://github.com/rails-api/rails-api) gem.
|
||||
- [#312](https://github.com/airblade/paper_trail/issues/312) - Fix RSpec `with_versioning` class level helper method.
|
||||
- `model_instance.without_versioning` now yields the `model_instance`, enabling syntax like this:
|
||||
`model_instance.without_versioning { |obj| obj.update_attributes(:name => 'value') }`.
|
||||
- Deprecated `Model.paper_trail_on` and `Model.paper_trail_off` in favor of bang versions of the methods. Deprecation warning
|
||||
informs users that the non-bang versions of the methods will be removed in version `3.1.0`.
|
||||
|
||||
|
|
30
README.md
30
README.md
|
@ -480,13 +480,26 @@ In a console session you can manually set who is responsible like this:
|
|||
You can avoid having to do this manually by setting your initializer to pick up the username of the current user from the OS, like this:
|
||||
|
||||
```ruby
|
||||
class PaperTrail::Version < ActiveRecord::Base
|
||||
if defined?(Rails::Console)
|
||||
# config/initializers/paper_trail.rb
|
||||
if defined?(::Rails::Console)
|
||||
PaperTrail.whodunnit = "#{`whoami`.strip}: console"
|
||||
elsif File.basename($0) == "rake"
|
||||
PaperTrail.whodunnit = "#{`whoami`.strip}: rake #{ARGV.join ' '}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Sometimes you want to define who is responsible for a change in a small scope without overwriting value of `PaperTrail.whodunnit`. It is possible to define the `whodunnit` value for an operation inside a block like this:
|
||||
|
||||
```ruby
|
||||
>> PaperTrail.whodunnit = 'Andy Stewart'
|
||||
>> widget.whodunnit('Lucas Souza') do
|
||||
>> widget.update_attributes :name => 'Wibble'
|
||||
>> end
|
||||
>> widget.versions.last.whodunnit # Lucas Souza
|
||||
>> widget.update_attributes :name => 'Clair'
|
||||
>> widget.versions.last.whodunnit # Andy Stewart
|
||||
>> widget.whodunnit('Ben Atkins') { |w| w.update_attributes :name => 'Beth' } # this syntax also works
|
||||
>> widget.versions.last.whodunnit # Ben Atkins
|
||||
```
|
||||
|
||||
A version's `whodunnit` records who changed the object causing the `version` to be stored. Because a version stores the object as it looked before the change (see the table above), `whodunnit` returns who stopped the object looking like this -- not who made it look like this. Hence `whodunnit` is aliased as `terminator`.
|
||||
|
@ -540,7 +553,7 @@ Alternatively you could store certain metadata for one type of version, and othe
|
|||
|
||||
If you only use custom version classes and don't use PaperTrail's built-in one, on Rails `>= 3.2` you must:
|
||||
|
||||
- either declare PaperTrail's version class abstract like this (in `config/initializers/paper_trail_patch.rb`):
|
||||
- either declare the `PaperTrail::Version` class to be abstract like this (in an initializer):
|
||||
|
||||
```ruby
|
||||
PaperTrail::Version.module_eval do
|
||||
|
@ -580,8 +593,14 @@ If you can think of a good way to achieve this, please let me know.
|
|||
PaperTrail can restore `:has_one` associations as they were at (actually, 3 seconds before) the time.
|
||||
|
||||
```ruby
|
||||
class Location < ActiveRecord::Base
|
||||
belongs_to :treasure
|
||||
has_paper_trail
|
||||
end
|
||||
|
||||
class Treasure < ActiveRecord::Base
|
||||
has_one :location
|
||||
has_paper_trail
|
||||
end
|
||||
|
||||
>> treasure.amount # 100
|
||||
|
@ -696,7 +715,7 @@ PaperTrail will call your proc with the current article and store the result in
|
|||
N.B. You must also:
|
||||
|
||||
* Add your metadata columns to the `versions` table.
|
||||
* Declare your metadata columns using `attr_accessible`. (If you are using `Rails 3`, or `Rails 4` with the [ProtectedAttributes](https://github.com/rails/protected_attributes) gem)
|
||||
* Declare your metadata columns using `attr_accessible`. (If you are using `ActiveRecord 3`, or `ActiveRecord 4` with the [ProtectedAttributes](https://github.com/rails/protected_attributes) gem)
|
||||
|
||||
For example:
|
||||
|
||||
|
@ -1062,6 +1081,7 @@ Many thanks to:
|
|||
* [Vlad Bokov](https://github.com/razum2um)
|
||||
* [Sean Marcia](https://github.com/SeanMarcia)
|
||||
* [Chulki Lee](https://github.com/chulkilee)
|
||||
* [Lucas Souza](https://github.com/lucasas)
|
||||
|
||||
|
||||
## Inspirations
|
||||
|
|
|
@ -19,6 +19,7 @@ group :development, :test do
|
|||
|
||||
# RSpec testing
|
||||
gem 'rspec-rails', '~> 2.14'
|
||||
gem 'generator_spec'
|
||||
|
||||
platforms :jruby, :ruby_18 do
|
||||
# shoulda-matchers > 2.0 is not compatible with Ruby18.
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
require 'rails/generators'
|
||||
require 'rails/generators/migration'
|
||||
require 'rails/generators/active_record'
|
||||
|
||||
module PaperTrail
|
||||
|
@ -13,7 +12,7 @@ module PaperTrail
|
|||
|
||||
def create_migration_file
|
||||
add_paper_trail_migration('create_versions')
|
||||
add_paper_trail_migration('add_object_changes_column_to_versions') if options.with_changes?
|
||||
add_paper_trail_migration('add_object_changes_to_versions') if options.with_changes?
|
||||
add_paper_trail_migration('create_version_associations')
|
||||
add_paper_trail_migration('add_transaction_id_column_to_versions')
|
||||
end
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
class AddObjectChangesColumnToVersions < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :versions, :object_changes, :text
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :versions, :object_changes
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class AddObjectChangesToVersions < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :versions, :object_changes, :text
|
||||
end
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
class CreateVersions < ActiveRecord::Migration
|
||||
def self.up
|
||||
def change
|
||||
create_table :versions do |t|
|
||||
t.string :item_type, :null => false
|
||||
t.integer :item_id, :null => false
|
||||
|
@ -10,9 +10,4 @@ class CreateVersions < ActiveRecord::Migration
|
|||
end
|
||||
add_index :versions, [:item_type, :item_id]
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_index :versions, [:item_type, :item_id]
|
||||
drop_table :versions
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,12 +34,12 @@ module PaperTrail
|
|||
|
||||
# Sets whether PaperTrail is enabled or disabled for this model in the current request.
|
||||
def self.enabled_for_model(model, value)
|
||||
paper_trail_store[:"request_enabled_for_#{model}"] = value
|
||||
paper_trail_store[:"enabled_for_#{model}"] = value
|
||||
end
|
||||
|
||||
# Returns `true` if PaperTrail is enabled for this model in the current request, `false` otherwise.
|
||||
def self.enabled_for_model?(model)
|
||||
!!paper_trail_store.fetch(:"request_enabled_for_#{model}", true)
|
||||
!!paper_trail_store.fetch(:"enabled_for_#{model}", true)
|
||||
end
|
||||
|
||||
# Set the field which records when a version was created.
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
require 'rspec/core'
|
||||
require 'rspec/matchers'
|
||||
require File.expand_path('../rspec/extensions', __FILE__)
|
||||
require 'paper_trail/frameworks/rspec/helpers'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.include ::PaperTrail::RSpec::Extensions
|
||||
config.include ::PaperTrail::RSpec::Helpers::InstanceMethods
|
||||
config.extend ::PaperTrail::RSpec::Helpers::ClassMethods
|
||||
|
||||
config.before(:each) do
|
||||
::PaperTrail.enabled = false
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
module PaperTrail
|
||||
module RSpec
|
||||
module Extensions
|
||||
# :call-seq:
|
||||
# with_versioning
|
||||
#
|
||||
# enable versioning for specific blocks
|
||||
|
||||
def with_versioning
|
||||
was_enabled = ::PaperTrail.enabled?
|
||||
::PaperTrail.enabled = true
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
::PaperTrail.enabled = was_enabled
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
module PaperTrail
|
||||
module RSpec
|
||||
module Helpers
|
||||
module InstanceMethods
|
||||
# enable versioning for specific blocks (at instance-level)
|
||||
def with_versioning
|
||||
was_enabled = ::PaperTrail.enabled?
|
||||
::PaperTrail.enabled = true
|
||||
yield
|
||||
ensure
|
||||
::PaperTrail.enabled = was_enabled
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# enable versioning for specific blocks (at class-level)
|
||||
def with_versioning(&block)
|
||||
context 'with versioning', :versioning => true do
|
||||
class_exec(&block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -215,7 +215,7 @@ module PaperTrail
|
|||
def without_versioning(method = nil)
|
||||
paper_trail_was_enabled = self.paper_trail_enabled_for_model?
|
||||
self.class.paper_trail_off!
|
||||
method ? method.to_proc.call(self) : yield
|
||||
method ? method.to_proc.call(self) : yield(self)
|
||||
ensure
|
||||
self.class.paper_trail_on! if paper_trail_was_enabled
|
||||
end
|
||||
|
@ -230,6 +230,16 @@ module PaperTrail
|
|||
instance_eval { alias :new_record? :old_new_record? }
|
||||
end
|
||||
|
||||
# Temporarily overwrites the value of whodunnit and then executes the provided block.
|
||||
def whodunnit(value)
|
||||
raise ArgumentError, 'expected to receive a block' unless block_given?
|
||||
current_whodunnit = PaperTrail.whodunnit
|
||||
PaperTrail.whodunnit = value
|
||||
yield self
|
||||
ensure
|
||||
PaperTrail.whodunnit = current_whodunnit
|
||||
end
|
||||
|
||||
# Mimicks behavior of `touch` method from `ActiveRecord::Persistence`, but generates a version
|
||||
#
|
||||
# TODO: lookinto leveraging the `after_touch` callback from `ActiveRecord` to allow the
|
||||
|
|
|
@ -30,6 +30,7 @@ Gem::Specification.new do |s|
|
|||
s.add_development_dependency 'sinatra', '~> 1.0'
|
||||
s.add_development_dependency 'rack-test', '>= 0.6'
|
||||
s.add_development_dependency 'rspec-rails', '~> 2.14'
|
||||
s.add_development_dependency 'generator_spec'
|
||||
|
||||
# JRuby support for the test ENV
|
||||
unless defined?(JRUBY_VERSION)
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
require 'spec_helper'
|
||||
require 'generator_spec/test_case'
|
||||
require File.expand_path('../../../lib/generators/paper_trail/install_generator', __FILE__)
|
||||
|
||||
describe PaperTrail::InstallGenerator, :type => :generator do
|
||||
include GeneratorSpec::TestCase
|
||||
destination File.expand_path('../tmp', __FILE__)
|
||||
|
||||
after(:all) { prepare_destination } # cleanup the tmp directory
|
||||
|
||||
describe "no options" do
|
||||
before(:all) do
|
||||
prepare_destination
|
||||
run_generator
|
||||
end
|
||||
|
||||
it "generates a migration for creating the 'versions' table" do
|
||||
destination_root.should have_structure {
|
||||
directory 'db' do
|
||||
directory 'migrate' do
|
||||
migration 'create_versions' do
|
||||
contains 'class CreateVersions'
|
||||
contains 'def change'
|
||||
contains 'create_table :versions do |t|'
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "`--with-changes` option set to `true`" do
|
||||
before(:all) do
|
||||
prepare_destination
|
||||
run_generator %w(--with-changes)
|
||||
end
|
||||
|
||||
it "generates a migration for creating the 'versions' table" do
|
||||
destination_root.should have_structure {
|
||||
directory 'db' do
|
||||
directory 'migrate' do
|
||||
migration 'create_versions' do
|
||||
contains 'class CreateVersions'
|
||||
contains 'def change'
|
||||
contains 'create_table :versions do |t|'
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
it "generates a migration for adding the 'object_changes' column to the 'versions' table" do
|
||||
destination_root.should have_structure {
|
||||
directory 'db' do
|
||||
directory 'migrate' do
|
||||
migration 'add_object_changes_to_versions' do
|
||||
contains 'class AddObjectChangesToVersions'
|
||||
contains 'def change'
|
||||
contains 'add_column :versions, :object_changes, :text'
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -23,6 +23,46 @@ describe Widget do
|
|||
|
||||
describe "Methods" do
|
||||
describe "Instance", :versioning => true do
|
||||
describe :whodunnit do
|
||||
it { should respond_to(:whodunnit) }
|
||||
|
||||
context "no block given" do
|
||||
it "should raise an error" do
|
||||
expect { widget.whodunnit('Ben') }.to raise_error(ArgumentError, 'expected to receive a block')
|
||||
end
|
||||
end
|
||||
|
||||
context "block given" do
|
||||
let(:orig_name) { Faker::Name.name }
|
||||
let(:new_name) { Faker::Name.name }
|
||||
|
||||
before do
|
||||
PaperTrail.whodunnit = orig_name
|
||||
widget.versions.last.whodunnit.should == orig_name # persist `widget`
|
||||
end
|
||||
|
||||
it "should modify value of `PaperTrail.whodunnit` while executing the block" do
|
||||
widget.whodunnit(new_name) do
|
||||
PaperTrail.whodunnit.should == new_name
|
||||
widget.update_attributes(:name => 'Elizabeth')
|
||||
end
|
||||
widget.versions.last.whodunnit.should == new_name
|
||||
end
|
||||
|
||||
it "should revert the value of `PaperTrail.whodunnit` to it's previous value after executing the block" do
|
||||
widget.whodunnit(new_name) { |w| w.update_attributes(:name => 'Elizabeth') }
|
||||
PaperTrail.whodunnit.should == orig_name
|
||||
end
|
||||
|
||||
context "error within block" do
|
||||
it "should ensure that the whodunnit value still reverts to it's previous value" do
|
||||
expect { widget.whodunnit(new_name) { raise } }.to raise_error
|
||||
PaperTrail.whodunnit.should == orig_name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe :touch_with_version do
|
||||
it { should respond_to(:touch_with_version) }
|
||||
|
||||
|
|
|
@ -5,13 +5,21 @@ describe "PaperTrail RSpec Helper" do
|
|||
it 'should have versioning off by default' do
|
||||
::PaperTrail.should_not be_enabled
|
||||
end
|
||||
it 'should turn versioning on in a with_versioning block' do
|
||||
it 'should turn versioning on in a `with_versioning` block' do
|
||||
::PaperTrail.should_not be_enabled
|
||||
with_versioning do
|
||||
::PaperTrail.should be_enabled
|
||||
end
|
||||
::PaperTrail.should_not be_enabled
|
||||
end
|
||||
|
||||
context "error within `with_versioning` block" do
|
||||
it "should revert the value of `PaperTrail.enabled?` to it's previous state" do
|
||||
::PaperTrail.should_not be_enabled
|
||||
expect { with_versioning { raise } }.to raise_error
|
||||
::PaperTrail.should_not be_enabled
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '`versioning: true`', :versioning => true do
|
||||
|
@ -27,6 +35,19 @@ describe "PaperTrail RSpec Helper" do
|
|||
end
|
||||
end
|
||||
|
||||
context '`with_versioning` block at class level' do
|
||||
it { ::PaperTrail.should_not be_enabled }
|
||||
|
||||
with_versioning do
|
||||
it 'should have versioning on by default' do
|
||||
::PaperTrail.should be_enabled
|
||||
end
|
||||
end
|
||||
it 'should not leak the `enabled?` state into successive tests' do
|
||||
::PaperTrail.should_not be_enabled
|
||||
end
|
||||
end
|
||||
|
||||
describe :whodunnit do
|
||||
before(:all) { ::PaperTrail.whodunnit = 'foobar' }
|
||||
|
||||
|
|
|
@ -515,10 +515,24 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|||
@widget.without_versioning do
|
||||
@widget.update_attributes :name => 'Ford'
|
||||
end
|
||||
# The model instance should yield itself for convenience purposes
|
||||
@widget.without_versioning { |w| w.update_attributes :name => 'Nixon' }
|
||||
end
|
||||
|
||||
should 'not create new version' do
|
||||
assert_equal 1, @widget.versions.length
|
||||
assert_equal @count, @widget.versions.length
|
||||
end
|
||||
|
||||
should 'enable paper trail after call' do
|
||||
assert Widget.paper_trail_enabled_for_model?
|
||||
end
|
||||
end
|
||||
|
||||
context 'when receiving a method name as an argument' do
|
||||
setup { @widget.without_versioning(:touch_with_version) }
|
||||
|
||||
should 'not create new version' do
|
||||
assert_equal @count, @widget.versions.length
|
||||
end
|
||||
|
||||
should 'enable paper trail after call' do
|
||||
|
@ -837,7 +851,6 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|||
should 'return its previous self' do
|
||||
assert_equal @widget.versions[-2].reify, @widget.previous_version
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue