paper_trail 2.6.3 → 2.6.4

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.
data/lib/paper_trail.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'singleton'
2
1
  require 'yaml'
3
2
 
4
3
  require 'paper_trail/config'
@@ -1,3 +1,5 @@
1
+ require 'singleton'
2
+
1
3
  module PaperTrail
2
4
  class Config
3
5
  include Singleton
@@ -2,16 +2,15 @@ module PaperTrail
2
2
  module Controller
3
3
 
4
4
  def self.included(base)
5
- base.before_filter :set_paper_trail_whodunnit
6
- base.before_filter :set_paper_trail_controller_info
7
5
  base.before_filter :set_paper_trail_enabled_for_controller
6
+ base.before_filter :set_paper_trail_whodunnit, :set_paper_trail_controller_info
8
7
  end
9
8
 
10
9
  protected
11
10
 
12
11
  # Returns the user who is responsible for any changes that occur.
13
12
  # By default this calls `current_user` and returns the result.
14
- #
13
+ #
15
14
  # Override this method in your controller to call a different
16
15
  # method, e.g. `current_person`, or anything you like.
17
16
  def user_for_paper_trail
@@ -57,7 +56,7 @@ module PaperTrail
57
56
 
58
57
  # Tells PaperTrail who is responsible for any changes that occur.
59
58
  def set_paper_trail_whodunnit
60
- ::PaperTrail.whodunnit = user_for_paper_trail
59
+ ::PaperTrail.whodunnit = user_for_paper_trail if paper_trail_enabled_for_controller
61
60
  end
62
61
 
63
62
  # DEPRECATED: please use `set_paper_trail_whodunnit` instead.
@@ -69,7 +68,7 @@ module PaperTrail
69
68
  # Tells PaperTrail any information from the controller you want
70
69
  # to store alongside any changes that occur.
71
70
  def set_paper_trail_controller_info
72
- ::PaperTrail.controller_info = info_for_paper_trail
71
+ ::PaperTrail.controller_info = info_for_paper_trail if paper_trail_enabled_for_controller
73
72
  end
74
73
 
75
74
  end
@@ -41,23 +41,15 @@ module PaperTrail
41
41
  class_attribute :version_class_name
42
42
  self.version_class_name = options[:class_name] || 'Version'
43
43
 
44
- class_attribute :ignore
45
- self.ignore = ([options[:ignore]].flatten.compact || []).map &:to_s
44
+ class_attribute :paper_trail_options
45
+ self.paper_trail_options = options.dup
46
46
 
47
- class_attribute :if_condition
48
- self.if_condition = options[:if]
49
-
50
- class_attribute :unless_condition
51
- self.unless_condition = options[:unless]
52
-
53
- class_attribute :skip
54
- self.skip = ([options[:skip]].flatten.compact || []).map &:to_s
55
-
56
- class_attribute :only
57
- self.only = ([options[:only]].flatten.compact || []).map &:to_s
47
+ [:ignore, :skip, :only].each do |k|
48
+ paper_trail_options[k] =
49
+ ([paper_trail_options[k]].flatten.compact || []).map &:to_s
50
+ end
58
51
 
59
- class_attribute :meta
60
- self.meta = options[:meta] || {}
52
+ paper_trail_options[:meta] ||= {}
61
53
 
62
54
  class_attribute :paper_trail_enabled_for_model
63
55
  self.paper_trail_enabled_for_model = true
@@ -68,11 +60,15 @@ module PaperTrail
68
60
  has_many self.versions_association_name,
69
61
  :class_name => version_class_name,
70
62
  :as => :item,
71
- :order => "#{PaperTrail.timestamp_field} ASC, #{self.version_class_name.constantize.primary_key} ASC"
63
+ :order => "#{PaperTrail.timestamp_field} ASC, #{self.version_key} ASC"
72
64
 
73
65
  after_create :record_create, :if => :save_version? if !options[:on] || options[:on].include?(:create)
74
66
  before_update :record_update, :if => :save_version? if !options[:on] || options[:on].include?(:update)
75
- after_destroy :record_destroy if !options[:on] || options[:on].include?(:destroy)
67
+ after_destroy :record_destroy, :if => :save_version? if !options[:on] || options[:on].include?(:destroy)
68
+ end
69
+
70
+ def version_key
71
+ self.version_class_name.constantize.primary_key
76
72
  end
77
73
 
78
74
  # Switches PaperTrail off for this class.
@@ -97,7 +93,7 @@ module PaperTrail
97
93
 
98
94
  # Returns who put the object into its current state.
99
95
  def originator
100
- version_class.with_item_keys(self.class.name, id).last.try :whodunnit
96
+ version_class.with_item_keys(self.class.base_class.name, id).last.try :whodunnit
101
97
  end
102
98
 
103
99
  # Returns the object (not a Version) as it was at the given timestamp.
@@ -149,7 +145,17 @@ module PaperTrail
149
145
 
150
146
  def record_create
151
147
  if switched_on?
152
- send(self.class.versions_association_name).create merge_metadata(:event => 'create', :whodunnit => PaperTrail.whodunnit)
148
+ data = {
149
+ :event => 'create',
150
+ :whodunnit => PaperTrail.whodunnit
151
+ }
152
+
153
+ if changed_notably? and version_class.column_names.include?('object_changes')
154
+ # The double negative (reject, !include?) preserves the hash structure of self.changes.
155
+ data[:object_changes] = self.changes.reject { |k, _| !notably_changed.include?(k) }.to_yaml
156
+ end
157
+
158
+ send(self.class.versions_association_name).create merge_metadata(data)
153
159
  end
154
160
  end
155
161
 
@@ -183,12 +189,17 @@ module PaperTrail
183
189
 
184
190
  def merge_metadata(data)
185
191
  # First we merge the model-level metadata in `meta`.
186
- meta.each do |k,v|
192
+ paper_trail_options[:meta].each do |k,v|
187
193
  data[k] =
188
194
  if v.respond_to?(:call)
189
195
  v.call(self)
190
196
  elsif v.is_a?(Symbol) && respond_to?(v)
191
- send(v)
197
+ # if it is an attribute that is changing, be sure to grab the current version
198
+ if has_attribute?(v) && send("#{v}_changed?".to_sym)
199
+ send("#{v}_was".to_sym)
200
+ else
201
+ send(v)
202
+ end
192
203
  else
193
204
  v
194
205
  end
@@ -210,7 +221,7 @@ module PaperTrail
210
221
  end
211
222
 
212
223
  def object_to_string(object)
213
- object.attributes.except(*self.class.skip).to_yaml
224
+ object.attributes.except(*self.class.paper_trail_options[:skip]).to_yaml
214
225
  end
215
226
 
216
227
  def changed_notably?
@@ -218,11 +229,14 @@ module PaperTrail
218
229
  end
219
230
 
220
231
  def notably_changed
221
- self.class.only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & self.class.only)
232
+ only = self.class.paper_trail_options[:only]
233
+ only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & only)
222
234
  end
223
235
 
224
236
  def changed_and_not_ignored
225
- changed - self.class.ignore - self.class.skip
237
+ ignore = self.class.paper_trail_options[:ignore]
238
+ skip = self.class.paper_trail_options[:skip]
239
+ changed - ignore - skip
226
240
  end
227
241
 
228
242
  def switched_on?
@@ -230,6 +244,8 @@ module PaperTrail
230
244
  end
231
245
 
232
246
  def save_version?
247
+ if_condition = self.class.paper_trail_options[:if]
248
+ unless_condition = self.class.paper_trail_options[:unless]
233
249
  (if_condition.blank? || if_condition.call(self)) && !unless_condition.try(:call, self)
234
250
  end
235
251
  end
@@ -1,3 +1,3 @@
1
1
  module PaperTrail
2
- VERSION = '2.6.3'
2
+ VERSION = '2.6.4'
3
3
  end
data/paper_trail.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.add_dependency 'activerecord', '~> 3.0'
20
20
 
21
21
  s.add_development_dependency 'rake'
22
- s.add_development_dependency 'shoulda', '2.10.3'
22
+ s.add_development_dependency 'shoulda', '~> 3.3'
23
23
  s.add_development_dependency 'sqlite3', '~> 1.2'
24
- s.add_development_dependency 'capybara', '~> 1.0.0'
24
+ s.add_development_dependency 'capybara', '~> 2.0'
25
25
  end
@@ -2,10 +2,13 @@ class Article < ActiveRecord::Base
2
2
  has_paper_trail :ignore => :title,
3
3
  :only => [:content],
4
4
  :skip => [:file_upload],
5
- :meta => {:answer => 42,
5
+ :meta => {
6
+ :answer => 42,
6
7
  :action => :action_data_provider_method,
7
8
  :question => Proc.new { "31 + 11 = #{31 + 11}" },
8
- :article_id => Proc.new { |article| article.id } }
9
+ :article_id => Proc.new { |article| article.id },
10
+ :title => :title
11
+ }
9
12
 
10
13
  def action_data_provider_method
11
14
  self.object_id.to_s
@@ -29,6 +29,7 @@ class SetUpTestTables < ActiveRecord::Migration
29
29
  t.string :action
30
30
  t.string :question
31
31
  t.integer :article_id
32
+ t.string :title
32
33
 
33
34
  # Controller info columns.
34
35
  t.string :ip
@@ -68,4 +68,23 @@ class ControllerTest < ActionController::TestCase
68
68
  assert_equal '127.0.0.1', versions_for_widget.last.ip
69
69
  assert_equal 'Rails Testing', versions_for_widget.last.user_agent
70
70
  end
71
+
72
+ test "controller metadata methods should get evaluated if paper trail is enabled for controller" do
73
+ @request.env['HTTP_USER_AGENT'] = 'User-Agent'
74
+ post :create, :widget => { :name => 'Flugel' }
75
+ assert PaperTrail.enabled_for_controller?
76
+ assert_equal 153, PaperTrail.whodunnit
77
+ assert PaperTrail.controller_info.present?
78
+ assert PaperTrail.controller_info.keys.include?(:ip)
79
+ assert PaperTrail.controller_info.keys.include?(:user_agent)
80
+ end
81
+
82
+ test "controller metadata methods should not get evaluated if paper trail is disabled for controller" do
83
+ @request.env['HTTP_USER_AGENT'] = 'Disable User-Agent'
84
+ post :create, :widget => { :name => 'Flugel' }
85
+ assert_equal 0, assigns(:widget).versions.length
86
+ assert !PaperTrail.enabled_for_controller?
87
+ assert PaperTrail.whodunnit.nil?
88
+ assert PaperTrail.controller_info.nil?
89
+ end
71
90
  end
data/test/test_helper.rb CHANGED
@@ -45,5 +45,5 @@ end
45
45
 
46
46
  class Version < ActiveRecord::Base
47
47
  attr_accessible :created_at, :updated_at,
48
- :answer, :action, :question, :article_id, :ip, :user_agent
48
+ :answer, :action, :question, :article_id, :ip, :user_agent, :title
49
49
  end
@@ -7,12 +7,12 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
7
7
 
8
8
  context 'which updates an ignored column' do
9
9
  setup { @article.update_attributes :title => 'My first title' }
10
- should_not_change('the number of versions') { Version.count }
10
+ should 'not change the number of versions' do assert_equal(1, Version.count) end
11
11
  end
12
12
 
13
13
  context 'which updates an ignored column and a selected column' do
14
14
  setup { @article.update_attributes :title => 'My first title', :content => 'Some text here.' }
15
- should_change('the number of versions', :by => 1) { Version.count }
15
+ should 'change the number of versions' do assert_equal(2, Version.count) end
16
16
 
17
17
  should 'have stored only non-ignored attributes' do
18
18
  assert_equal ({'content' => [nil, 'Some text here.']}), @article.versions.last.changeset
@@ -21,22 +21,22 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
21
21
 
22
22
  context 'which updates a selected column' do
23
23
  setup { @article.update_attributes :content => 'Some text here.' }
24
- should_change('the number of versions', :by => 1) { Version.count }
24
+ should 'change the number of versions' do assert_equal(2, Version.count) end
25
25
  end
26
26
 
27
27
  context 'which updates a non-ignored and non-selected column' do
28
28
  setup { @article.update_attributes :abstract => 'Other abstract'}
29
- should_not_change('the number of versions') { Version.count }
29
+ should 'not change the number of versions' do assert_equal(1, Version.count) end
30
30
  end
31
31
 
32
32
  context 'which updates a skipped column' do
33
33
  setup { @article.update_attributes :file_upload => 'Your data goes here' }
34
- should_not_change('the number of versions') { Version.count }
34
+ should 'not change the number of versions' do assert_equal(1, Version.count) end
35
35
  end
36
36
 
37
37
  context 'which updates a skipped column and a selected column' do
38
38
  setup { @article.update_attributes :file_upload => 'Your data goes here', :content => 'Some text here.' }
39
- should_change('the number of versions', :by => 1) { Version.count }
39
+ should 'change the number of versions' do assert_equal(2, Version.count) end
40
40
 
41
41
  should 'have stored only non-skipped attributes' do
42
42
  assert_equal ({'content' => [nil, 'Some text here.']}), @article.versions.last.changeset
@@ -64,7 +64,7 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
64
64
 
65
65
  context 'which updates an ignored column' do
66
66
  setup { @legacy_widget.update_attributes :version => 1 }
67
- should_not_change('the number of versions') { Version.count }
67
+ should 'not change the number of versions' do assert_equal(1, Version.count) end
68
68
  end
69
69
  end
70
70
 
@@ -73,11 +73,16 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
73
73
 
74
74
  context 'for non-US translations' do
75
75
  setup { @translation.save }
76
- should_not_change('the number of versions') { Version.count }
76
+ should 'not change the number of versions' do assert_equal(0, Version.count) end
77
77
 
78
78
  context 'after update' do
79
79
  setup { @translation.update_attributes :content => 'Content' }
80
- should_not_change('the number of versions') { Version.count }
80
+ should 'not change the number of versions' do assert_equal(0, Version.count) end
81
+ end
82
+
83
+ context 'after destroy' do
84
+ setup { @translation.destroy }
85
+ should 'not change the number of versions' do assert_equal(0, Version.count) end
81
86
  end
82
87
  end
83
88
 
@@ -90,22 +95,27 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
90
95
  @translation.save
91
96
  end
92
97
 
93
- should_not_change('the number of versions') { Version.count }
98
+ should 'not change the number of versions' do assert_equal(0, Version.count) end
94
99
 
95
100
  context 'after update' do
96
101
  setup { @translation.update_attributes :content => 'Content' }
97
- should_not_change('the number of versions') { Version.count }
102
+ should 'not change the number of versions' do assert_equal(0, Version.count) end
98
103
  end
99
104
  end
100
105
 
101
106
  context 'that are not drafts' do
102
107
  setup { @translation.save }
103
108
 
104
- should_change('the number of versions', :by => 1) { Version.count }
109
+ should 'change the number of versions' do assert_equal(1, Version.count) end
105
110
 
106
111
  context 'after update' do
107
112
  setup { @translation.update_attributes :content => 'Content' }
108
- should_change('the number of versions', :by => 1) { Version.count }
113
+ should 'change the number of versions' do assert_equal(2, Version.count) end
114
+ end
115
+
116
+ context 'after destroy' do
117
+ setup { @translation.destroy }
118
+ should 'change the number of versions' do assert_equal(2, Version.count) end
109
119
  end
110
120
  end
111
121
  end
@@ -143,12 +153,19 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
143
153
  assert @widget.live?
144
154
  end
145
155
 
146
- should 'not have changes' do
147
- assert_equal Hash.new, @widget.versions.last.changeset
156
+ should 'have changes' do
157
+ changes = {
158
+ 'name' => [nil, 'Henry'],
159
+ 'created_at' => [nil, @widget.created_at],
160
+ 'updated_at' => [nil, @widget.updated_at],
161
+ 'id' => [nil, 1]
162
+ }
163
+
164
+ assert_equal changes, @widget.versions.last.changeset
148
165
  end
149
166
 
150
167
  context 'and then updated without any changes' do
151
- setup { @widget.save }
168
+ setup { @widget.touch }
152
169
 
153
170
  should 'not have a new version' do
154
171
  assert_equal 1, @widget.versions.length
@@ -353,6 +370,7 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
353
370
  context "after a column is removed from the record's schema" do
354
371
  setup do
355
372
  change_schema
373
+ Widget.connection.schema_cache.clear!
356
374
  Widget.reset_column_information
357
375
  assert_raise(NoMethodError) { Widget.new.sacrificial_column }
358
376
  @last = @widget.versions.last
@@ -516,6 +534,12 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
516
534
  assert_nil Version.last.next
517
535
  end
518
536
 
537
+ should 'should return the correct originator' do
538
+ PaperTrail.whodunnit = 'Ben'
539
+ @foo.update_attribute(:name, 'Geoffrey')
540
+ assert_equal PaperTrail.whodunnit, @foo.originator
541
+ end
542
+
519
543
  context 'when destroyed' do
520
544
  setup { @foo.destroy }
521
545
 
@@ -630,7 +654,10 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
630
654
 
631
655
 
632
656
  context 'An item' do
633
- setup { @article = Article.new }
657
+ setup do
658
+ @initial_title = 'Foobar'
659
+ @article = Article.new :title => @initial_title
660
+ end
634
661
 
635
662
  context 'which is created' do
636
663
  setup { @article.save }
@@ -650,10 +677,16 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
650
677
  should 'store dynamic meta data based on a method of the item' do
651
678
  assert_equal @article.action_data_provider_method, @article.versions.last.action
652
679
  end
680
+
681
+ should 'store dynamic meta data based on an attribute of the item prior to creation' do
682
+ assert_equal nil, @article.versions.last.title
683
+ end
653
684
 
654
685
 
655
686
  context 'and updated' do
656
- setup { @article.update_attributes! :content => 'Better text.' }
687
+ setup do
688
+ @article.update_attributes! :content => 'Better text.', :title => 'Rhubarb'
689
+ end
657
690
 
658
691
  should 'store fixed meta data' do
659
692
  assert_equal 42, @article.versions.last.answer
@@ -666,6 +699,10 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
666
699
  should 'store dynamic meta data which depends on the item' do
667
700
  assert_equal @article.id, @article.versions.last.article_id
668
701
  end
702
+
703
+ should 'store dynamic meta data based on an attribute of the item prior to the update' do
704
+ assert_equal @initial_title, @article.versions.last.title
705
+ end
669
706
  end
670
707
 
671
708
 
@@ -683,7 +720,10 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
683
720
  should 'store dynamic meta data which depends on the item' do
684
721
  assert_equal @article.id, @article.versions.last.article_id
685
722
  end
686
-
723
+
724
+ should 'store dynamic meta data based on an attribute of the item prior to the destruction' do
725
+ assert_equal @initial_title, @article.versions.last.title
726
+ end
687
727
  end
688
728
  end
689
729
  end
@@ -881,13 +921,14 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
881
921
 
882
922
  context 'which is then saved' do
883
923
  setup { @post.save }
884
- should_change('the number of post versions') { PostVersion.count }
885
- should_not_change('the number of versions') { Version.count }
924
+ should 'change the number of post versions' do assert_equal 1, PostVersion.count end
925
+ should 'not change the number of versions' do assert_equal(0, Version.count) end
886
926
  end
887
927
  end
888
928
 
889
929
  context 'An existing model instance which uses a custom Version class' do
890
930
  setup { @post = Post.create }
931
+ should 'have one post version' do assert_equal(1, PostVersion.count) end
891
932
 
892
933
  context 'on the first version' do
893
934
  setup { @version = @post.versions.first }
@@ -903,8 +944,8 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
903
944
 
904
945
  context 'which is modified' do
905
946
  setup { @post.update_attributes({ :content => "Some new content" }) }
906
- should_change('the number of post versions') { PostVersion.count }
907
- should_not_change('the number of versions') { Version.count }
947
+ should 'change the number of post versions' do assert_equal(2, PostVersion.count) end
948
+ should 'not change the number of versions' do assert_equal(0, Version.count) end
908
949
  should "not have stored changes when object_changes column doesn't exist" do
909
950
  assert_nil @post.versions.last.changeset
910
951
  end