paper_trail 3.0.0.beta1 → 3.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -3
  3. data/CHANGELOG.md +15 -0
  4. data/README.md +113 -24
  5. data/Rakefile +2 -5
  6. data/lib/generators/paper_trail/install_generator.rb +1 -1
  7. data/lib/paper_trail.rb +16 -3
  8. data/lib/paper_trail/config.rb +1 -1
  9. data/lib/paper_trail/frameworks/cucumber.rb +22 -24
  10. data/lib/paper_trail/frameworks/rails.rb +1 -1
  11. data/lib/paper_trail/frameworks/rspec.rb +16 -18
  12. data/lib/paper_trail/frameworks/sinatra.rb +4 -4
  13. data/lib/paper_trail/has_paper_trail.rb +68 -39
  14. data/lib/paper_trail/serializers/json.rb +1 -1
  15. data/lib/paper_trail/serializers/yaml.rb +3 -3
  16. data/lib/paper_trail/version.rb +73 -78
  17. data/lib/paper_trail/version_number.rb +1 -1
  18. data/paper_trail.gemspec +4 -2
  19. data/spec/models/version_spec.rb +44 -0
  20. data/spec/models/widget_spec.rb +16 -6
  21. data/spec/modules/version_concern_spec.rb +50 -0
  22. data/spec/paper_trail_spec.rb +18 -20
  23. data/spec/spec_helper.rb +3 -1
  24. data/test/custom_json_serializer.rb +1 -1
  25. data/test/dummy/app/controllers/articles_controller.rb +10 -0
  26. data/test/dummy/app/models/article.rb +2 -2
  27. data/test/dummy/app/models/wotsit.rb +4 -0
  28. data/test/dummy/config/environments/development.rb +1 -1
  29. data/test/dummy/config/routes.rb +1 -0
  30. data/test/functional/enabled_for_controller_test.rb +29 -0
  31. data/test/functional/modular_sinatra_test.rb +1 -1
  32. data/test/functional/sinatra_test.rb +1 -1
  33. data/test/test_helper.rb +2 -7
  34. data/test/unit/model_test.rb +56 -2
  35. data/test/unit/serializer_test.rb +6 -6
  36. data/test/unit/serializers/json_test.rb +7 -7
  37. data/test/unit/serializers/mixin_yaml_test.rb +1 -1
  38. data/test/unit/serializers/yaml_test.rb +8 -8
  39. data/test/unit/version_test.rb +41 -12
  40. metadata +45 -16
@@ -1,3 +1,3 @@
1
1
  module PaperTrail
2
- VERSION = '3.0.0.beta1'
2
+ VERSION = '3.0.0.rc1'
3
3
  end
@@ -4,11 +4,13 @@ require 'paper_trail/version_number'
4
4
  Gem::Specification.new do |s|
5
5
  s.name = 'paper_trail'
6
6
  s.version = PaperTrail::VERSION
7
+ s.platform = Gem::Platform::RUBY
7
8
  s.summary = "Track changes to your models' data. Good for auditing or versioning."
8
9
  s.description = s.summary
9
10
  s.homepage = 'http://github.com/airblade/paper_trail'
10
11
  s.authors = ['Andy Stewart', 'Ben Atkins']
11
12
  s.email = 'boss@airbladesoftware.com'
13
+ s.license = 'MIT'
12
14
 
13
15
  s.files = `git ls-files`.split("\n")
14
16
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -16,10 +18,10 @@ Gem::Specification.new do |s|
16
18
  s.require_paths = ['lib']
17
19
 
18
20
  s.add_dependency 'activerecord', ['>= 3.0', '< 5.0']
21
+ s.add_dependency 'activesupport', ['>= 3.0', '< 5.0']
19
22
 
20
23
  s.add_development_dependency 'rake'
21
24
  s.add_development_dependency 'shoulda', '~> 3.5'
22
- # s.add_development_dependency 'shoulda-matchers', '~> 1.5'
23
25
  s.add_development_dependency 'ffaker', '>= 1.15'
24
26
  s.add_development_dependency 'railties', ['>= 3.0', '< 5.0']
25
27
  s.add_development_dependency 'sinatra', '~> 1.0'
@@ -30,6 +32,6 @@ Gem::Specification.new do |s|
30
32
  unless defined?(JRUBY_VERSION)
31
33
  s.add_development_dependency 'sqlite3', '~> 1.2'
32
34
  else
33
- s.add_development_dependency 'activerecord-jdbcsqlite3-adapter', ['>= 1.3.0.rc1', '< 1.4']
35
+ s.add_development_dependency 'activerecord-jdbcsqlite3-adapter', '~> 1.3'
34
36
  end
35
37
  end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe PaperTrail::Version do
4
+ it "should include the `VersionConcern` module to get base functionality" do
5
+ PaperTrail::Version.should include(PaperTrail::VersionConcern)
6
+ end
7
+
8
+ describe "Attributes" do
9
+ it { should have_db_column(:item_type).of_type(:string) }
10
+ it { should have_db_column(:item_id).of_type(:integer) }
11
+ it { should have_db_column(:event).of_type(:string) }
12
+ it { should have_db_column(:whodunnit).of_type(:string) }
13
+ it { should have_db_column(:object).of_type(:text) }
14
+ it { should have_db_column(:created_at).of_type(:datetime) }
15
+ end
16
+
17
+ describe "Indexes" do
18
+ it { should have_db_index([:item_type, :item_id]) }
19
+ end
20
+
21
+ describe "Methods" do
22
+ describe "Instance" do
23
+ subject { PaperTrail::Version.new(attributes) rescue PaperTrail::Version.new }
24
+
25
+ describe :terminator do
26
+ it { should respond_to(:terminator) }
27
+
28
+ let(:attributes) { {:whodunnit => Faker::Name.first_name} }
29
+
30
+ it "is an alias for the `whodunnit` attribute" do
31
+ subject.whodunnit.should == attributes[:whodunnit]
32
+ end
33
+ end
34
+
35
+ describe :version_author do
36
+ it { should respond_to(:terminator) }
37
+
38
+ it "should be an alias for the `terminator` method" do
39
+ subject.method(:version_author).should == subject.method(:terminator)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,13 +1,23 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Widget do
4
- it { should be_versioned }
4
+ describe '`be_versioned` matcher' do
5
+ it { should be_versioned }
6
+ end
7
+
8
+ describe "`versioning` option" do
9
+ let(:widget) { Widget.create :name => 'Bob', :an_integer => 1 }
10
+
11
+ context :enabled, :versioning => true do
12
+ it 'should enable versioning for models wrapped within a block' do
13
+ widget.versions.size.should == 1
14
+ end
15
+ end
5
16
 
6
- context 'be_versioned matcher', :versioning => true do
7
- it 'should respond to be_versioned' do
8
- widget = Widget.create :name => 'Bob', :an_integer => 1
9
- widget.should be_versioned
10
- widget.versions.size.should == 1
17
+ context '`disabled` (default)' do
18
+ it 'should not enable versioning for models wrapped within a block not marked to used versioning' do
19
+ widget.versions.size.should == 0
20
+ end
11
21
  end
12
22
  end
13
23
  end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe PaperTrail::VersionConcern do
4
+
5
+ before(:all) do
6
+ module Foo
7
+ class Base < ActiveRecord::Base
8
+ self.abstract_class = true
9
+ end
10
+
11
+ class Document < Base
12
+ has_paper_trail :class_name => 'Foo::Version'
13
+ end
14
+
15
+ class Version < Base
16
+ include PaperTrail::VersionConcern
17
+ end
18
+ end
19
+ Foo::Base.establish_connection(:adapter => 'sqlite3', :database => File.expand_path('../../../test/dummy/db/test-foo.sqlite3', __FILE__))
20
+
21
+ module Bar
22
+ class Base < ActiveRecord::Base
23
+ self.abstract_class = true
24
+ end
25
+
26
+ class Document < Base
27
+ has_paper_trail :class_name => 'Bar::Version'
28
+ end
29
+
30
+ class Version < Base
31
+ include PaperTrail::VersionConcern
32
+ end
33
+ end
34
+ Bar::Base.establish_connection(:adapter => 'sqlite3', :database => File.expand_path('../../../test/dummy/db/test-bar.sqlite3', __FILE__))
35
+ end
36
+
37
+ it 'allows included class to have different connections' do
38
+ Foo::Version.connection.should_not eq Bar::Version.connection
39
+ end
40
+
41
+ it 'allows custom version class to share connection with superclass' do
42
+ Foo::Version.connection.should eq Foo::Document.connection
43
+ Bar::Version.connection.should eq Bar::Document.connection
44
+ end
45
+
46
+ it 'can be used with class_name option' do
47
+ Foo::Document.version_class_name.should eq 'Foo::Version'
48
+ Bar::Document.version_class_name.should eq 'Bar::Version'
49
+ end
50
+ end
@@ -1,31 +1,29 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "PaperTrail RSpec Helper" do
4
- describe :enabled do
5
- context 'default' do
6
- it 'should have versioning off by default' do
7
- ::PaperTrail.should_not be_enabled
8
- end
9
- it 'should turn versioning on in a with_versioning block' do
10
- ::PaperTrail.should_not be_enabled
11
- with_versioning do
12
- ::PaperTrail.should be_enabled
13
- end
14
- ::PaperTrail.should_not be_enabled
15
- end
4
+ context 'default' do
5
+ it 'should have versioning off by default' do
6
+ ::PaperTrail.should_not be_enabled
16
7
  end
17
-
18
- context 'versioning: true', :versioning => true do
19
- it 'should have versioning on by default' do
8
+ it 'should turn versioning on in a with_versioning block' do
9
+ ::PaperTrail.should_not be_enabled
10
+ with_versioning do
20
11
  ::PaperTrail.should be_enabled
21
12
  end
22
- it 'should keep versioning on after a with_versioning block' do
23
- ::PaperTrail.should be_enabled
24
- with_versioning do
25
- ::PaperTrail.should be_enabled
26
- end
13
+ ::PaperTrail.should_not be_enabled
14
+ end
15
+ end
16
+
17
+ context '`versioning: true`', :versioning => true do
18
+ it 'should have versioning on by default' do
19
+ ::PaperTrail.should be_enabled
20
+ end
21
+ it 'should keep versioning on after a with_versioning block' do
22
+ ::PaperTrail.should be_enabled
23
+ with_versioning do
27
24
  ::PaperTrail.should be_enabled
28
25
  end
26
+ ::PaperTrail.should be_enabled
29
27
  end
30
28
  end
31
29
 
@@ -1,8 +1,10 @@
1
1
  ENV["RAILS_ENV"] ||= 'test'
2
- require File.expand_path('../../test/dummy/config/environment', __FILE__)
3
2
 
3
+ require File.expand_path('../../test/dummy/config/environment', __FILE__)
4
4
  require 'rspec/rails'
5
5
  require 'rspec/autorun'
6
+ require 'shoulda/matchers'
7
+ require 'ffaker'
6
8
 
7
9
  # Requires supporting ruby files with custom matchers and macros, etc,
8
10
  # in spec/support/ and its subdirectories.
@@ -1,6 +1,6 @@
1
1
  # This custom serializer excludes nil values
2
2
  module CustomJsonSerializer
3
- extend PaperTrail::Serializers::Json
3
+ extend PaperTrail::Serializers::JSON
4
4
 
5
5
  def self.load(string)
6
6
  parsed_value = super(string)
@@ -0,0 +1,10 @@
1
+ class ArticlesController < ApplicationController
2
+ def create
3
+ if PaperTrail.active_record_protected_attributes?
4
+ @article = Article.create params[:article]
5
+ else
6
+ @article = Article.create params.require(:article).permit!
7
+ end
8
+ head :ok
9
+ end
10
+ end
@@ -1,6 +1,6 @@
1
1
  class Article < ActiveRecord::Base
2
- has_paper_trail :ignore => :title,
3
- :only => [:content],
2
+ has_paper_trail :ignore => [:title, { :abstract => Proc.new { |obj| ['ignore abstract', 'Other abstract'].include? obj.abstract } } ],
3
+ :only => [:content, { :abstract => Proc.new { |obj| obj.abstract.present? } } ],
4
4
  :skip => [:file_upload],
5
5
  :meta => {
6
6
  :answer => 42,
@@ -1,4 +1,8 @@
1
1
  class Wotsit < ActiveRecord::Base
2
2
  has_paper_trail
3
3
  belongs_to :widget
4
+
5
+ def created_on
6
+ created_at.to_date
7
+ end
4
8
  end
@@ -27,7 +27,7 @@ Dummy::Application.configure do
27
27
 
28
28
  # Log the query plan for queries taking more than this (works
29
29
  # with SQLite, MySQL, and PostgreSQL)
30
- config.active_record.auto_explain_threshold_in_seconds = 0.5
30
+ # config.active_record.auto_explain_threshold_in_seconds = 0.5
31
31
 
32
32
  # Do not compress assets
33
33
  config.assets.compress = false
@@ -1,3 +1,4 @@
1
1
  Dummy::Application.routes.draw do
2
+ resources :articles, :only => [:create]
2
3
  resources :widgets
3
4
  end
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+
3
+ class EnabledForControllerTest < ActionController::TestCase
4
+ tests ArticlesController
5
+
6
+ context "`PaperTrail.enabled? == true`" do
7
+ should 'enabled_for_controller?.should == true' do
8
+ assert PaperTrail.enabled?
9
+ post :create, :article => { :title => 'Doh', :content => Faker::Lorem.sentence }
10
+ assert_not_nil assigns(:article)
11
+ assert PaperTrail.enabled_for_controller?
12
+ assert_equal 1, assigns(:article).versions.length
13
+ end
14
+ end
15
+
16
+ context "`PaperTrail.enabled? == false`" do
17
+ setup { PaperTrail.enabled = false }
18
+
19
+ should 'enabled_for_controller?.should == false' do
20
+ assert !PaperTrail.enabled?
21
+ post :create, :article => { :title => 'Doh', :content => Faker::Lorem.sentence }
22
+ assert !PaperTrail.enabled_for_controller?
23
+ assert_equal 0, assigns(:article).versions.length
24
+ end
25
+
26
+ teardown { PaperTrail.enabled = true }
27
+ end
28
+
29
+ end
@@ -4,7 +4,7 @@ require 'sinatra/base'
4
4
  # --- Tests for modular `Sinatra::Base` style ----
5
5
  class BaseApp < Sinatra::Base
6
6
  ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => File.expand_path('../../dummy/db/test.sqlite3', __FILE__))
7
- register Sinatra::PaperTrail
7
+ register PaperTrail::Sinatra
8
8
 
9
9
  get '/test' do
10
10
  Widget.create!(:name => 'foo')
@@ -4,7 +4,7 @@ require 'test_helper'
4
4
  # --- Tests for non-modular `Sinatra::Application` style ----
5
5
  class Sinatra::Application
6
6
  ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => File.expand_path('../../dummy/db/test.sqlite3', __FILE__))
7
- register Sinatra::PaperTrail # we shouldn't actually need this line if I'm not mistaken but the tests seem to fail without it ATM
7
+ register PaperTrail::Sinatra # we shouldn't actually need this line if I'm not mistaken but the tests seem to fail without it ATM
8
8
 
9
9
  get '/test' do
10
10
  Widget.create!(:name => 'bar')
@@ -3,16 +3,11 @@ ENV["RAILS_ENV"] = "test"
3
3
 
4
4
  require File.expand_path("../dummy/config/environment.rb", __FILE__)
5
5
  require "rails/test_help"
6
-
7
- #ActionMailer::Base.delivery_method = :test
8
- #ActionMailer::Base.perform_deliveries = true
9
- #ActionMailer::Base.default_url_options[:host] = "test.com"
10
-
11
- Rails.backtrace_cleaner.remove_silencers!
12
-
13
6
  require 'shoulda'
14
7
  require 'ffaker'
15
8
 
9
+ Rails.backtrace_cleaner.remove_silencers!
10
+
16
11
  # Run any available migration
17
12
  ActiveRecord::Migrator.migrate File.expand_path("../dummy/db/migrate/", __FILE__)
18
13
 
@@ -11,8 +11,21 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
11
11
  should 'not change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
12
12
  end
13
13
 
14
- context 'which updates an ignored column and a selected column' do
15
- setup { @article.update_attributes :title => 'My first title', :content => 'Some text here.' }
14
+ context 'which updates an ignored column with truly Proc' do
15
+ setup { @article.update_attributes :abstract => 'ignore abstract' }
16
+ should 'not change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
17
+ end
18
+
19
+ context 'which updates an ignored column with falsy Proc' do
20
+ setup { @article.update_attributes :abstract => 'do not ignore abstract!' }
21
+ should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
22
+ end
23
+
24
+ context 'which updates an ignored column, ignored with truly Proc and a selected column' do
25
+ setup { @article.update_attributes :title => 'My first title',
26
+ :content => 'Some text here.',
27
+ :abstract => 'ignore abstract'
28
+ }
16
29
  should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
17
30
 
18
31
  should "show the new version in the model's `versions` association" do
@@ -24,6 +37,22 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
24
37
  end
25
38
  end
26
39
 
40
+ context 'which updates an ignored column, ignored with falsy Proc and a selected column' do
41
+ setup { @article.update_attributes :title => 'My first title',
42
+ :content => 'Some text here.',
43
+ :abstract => 'do not ignore abstract'
44
+ }
45
+ should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
46
+
47
+ should "show the new version in the model's `versions` association" do
48
+ assert_equal(2, @article.versions.size)
49
+ end
50
+
51
+ should 'have stored only non-ignored attributes' do
52
+ assert_equal ({'content' => [nil, 'Some text here.'], 'abstract' => [nil, 'do not ignore abstract']}), @article.versions.last.changeset
53
+ end
54
+ end
55
+
27
56
  context 'which updates a selected column' do
28
57
  setup { @article.update_attributes :content => 'Some text here.' }
29
58
  should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
@@ -553,6 +582,31 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
553
582
  end
554
583
 
555
584
 
585
+ context 'Timestamps' do
586
+ setup do
587
+ @wotsit = Wotsit.create! :name => 'wotsit'
588
+ end
589
+
590
+ should 'record timestamps' do
591
+ @wotsit.update_attributes! :name => 'changed'
592
+ assert_not_nil @wotsit.versions.last.reify.created_at
593
+ assert_not_nil @wotsit.versions.last.reify.updated_at
594
+ end
595
+
596
+ should 'not generate warning' do
597
+ # Tests that it doesn't try to write created_on as an attribute just because a created_on
598
+ # method exists.
599
+ warnings = capture(:stderr) { # Deprecation warning in Rails 3.2
600
+ assert_nothing_raised { # ActiveModel::MissingAttributeError in Rails 4
601
+ @wotsit.update_attributes! :name => 'changed'
602
+ }
603
+ }
604
+ assert_equal '', warnings
605
+ end
606
+
607
+ end
608
+
609
+
556
610
  context 'A subclass' do
557
611
  setup do
558
612
  @foo = FooWidget.create
@@ -14,16 +14,16 @@ class SerializerTest < ActiveSupport::TestCase
14
14
  @fluxor.update_attributes :name => 'Some more text.'
15
15
  end
16
16
 
17
- should 'work with the default yaml serializer' do
17
+ should 'work with the default `YAML` serializer' do
18
18
  # Normal behaviour
19
19
  assert_equal 2, @fluxor.versions.length
20
20
  assert_nil @fluxor.versions[0].reify
21
21
  assert_equal 'Some text.', @fluxor.versions[1].reify.name
22
22
 
23
- # Check values are stored as YAML.
23
+ # Check values are stored as `YAML`.
24
24
  assert_equal @original_fluxor_attributes, YAML.load(@fluxor.versions[1].object)
25
25
  # This test can't consistently pass in Ruby1.8 because hashes do no preserve order, which means the order of the
26
- # attributes in the YAML can't be ensured.
26
+ # attributes in the `YAML` can't be ensured.
27
27
  if RUBY_VERSION.to_f >= 1.9
28
28
  assert_equal YAML.dump(@original_fluxor_attributes), @fluxor.versions[1].object
29
29
  end
@@ -33,7 +33,7 @@ class SerializerTest < ActiveSupport::TestCase
33
33
  context 'JSON Serializer' do
34
34
  setup do
35
35
  PaperTrail.configure do |config|
36
- config.serializer = PaperTrail::Serializers::Json
36
+ config.serializer = PaperTrail::Serializers::JSON
37
37
  end
38
38
 
39
39
  Fluxor.instance_eval <<-END
@@ -46,7 +46,7 @@ class SerializerTest < ActiveSupport::TestCase
46
46
  end
47
47
 
48
48
  teardown do
49
- PaperTrail.config.serializer = PaperTrail::Serializers::Yaml
49
+ PaperTrail.config.serializer = PaperTrail::Serializers::YAML
50
50
  end
51
51
 
52
52
  should 'reify with JSON serializer' do
@@ -88,7 +88,7 @@ class SerializerTest < ActiveSupport::TestCase
88
88
  end
89
89
 
90
90
  teardown do
91
- PaperTrail.config.serializer = PaperTrail::Serializers::Yaml
91
+ PaperTrail.config.serializer = PaperTrail::Serializers::YAML
92
92
  end
93
93
 
94
94
  should 'reify with custom serializer' do