paper_trail 2.6.3 → 2.6.4

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