paper_trail 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -6,11 +6,13 @@ PaperTrail lets you track changes to your models' data. It's good for auditing
6
6
  ## Features
7
7
 
8
8
  * Stores every create, update and destroy.
9
- * Does not store updates which don't change anything (or which only change attributes you are ignoring).
9
+ * Does not store updates which don't change anything.
10
+ * Does not store updates which only change attributes you are ignoring.
10
11
  * Allows you to get at every version, including the original, even once destroyed.
11
12
  * Allows you to get at every version even if the schema has since changed.
12
13
  * Automatically records who was responsible if your controller has a `current_user` method.
13
14
  * Allows you to set who is responsible at model-level (useful for migrations).
15
+ * Allows you to store arbitrary metadata with each version (useful for filtering versions).
14
16
  * Can be turned off/on (useful for migrations).
15
17
  * No configuration necessary.
16
18
  * Stores everything in a single database table (generates migration for you).
@@ -141,6 +143,23 @@ In a migration or in `script/console` you can set who is responsible like this:
141
143
  >> widget.versions.last.whodunnit # Andy Stewart
142
144
 
143
145
 
146
+ ## Storing metadata
147
+
148
+ You can store arbitrary metadata alongside each version like this:
149
+
150
+ class Article < ActiveRecord::Base
151
+ belongs_to :author
152
+ has_paper_trail :meta => { :author_id => Proc.new { |article| article.author_id },
153
+ :answer => 42 }
154
+ end
155
+
156
+ PaperTrail will call your proc with the current article and store the result in the `author_id` column of the `versions` table. (Remember to add your metadata columns to the table.)
157
+
158
+ Why would you do this? In this example, `author_id` is an attribute of `Article` and PaperTrail will store it anyway in serialized (YAML) form in the `object` column of the `version` record. But let's say you wanted to pull out all versions for a particular author; without the metadata you would have to deserialize (reify) each `version` object to see if belonged to the author in question. Clearly this is inefficient. Using the metadata you can find just those versions you want:
159
+
160
+ Version.all(:conditions => ['author_id = ?', author_id])
161
+
162
+
144
163
  ## Turning PaperTrail Off/On
145
164
 
146
165
  Sometimes you don't want to store changes. Perhaps you are only interested in changes made
@@ -158,9 +177,9 @@ And on again like this:
158
177
 
159
178
  ## Installation
160
179
 
161
- 1. Install PaperTrail either as a gem or as a plugin:
180
+ 1. Install PaperTrail either as a gem (from Gemcutter; the ones on GitHub are obsolete) or as a plugin:
162
181
 
163
- `config.gem 'airblade-paper_trail', :lib => 'paper_trail', :source => 'http://gems.github.com'`
182
+ `config.gem 'paper_trail', :source => 'http://gemcutter.org'`
164
183
 
165
184
  or:
166
185
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.1
1
+ 1.4.0
@@ -8,11 +8,17 @@ module PaperTrail
8
8
  module ClassMethods
9
9
  # Options:
10
10
  # :ignore an array of attributes for which a new +Version+ will not be created if only they change.
11
+ # :meta a hash of extra data to store. You must add a column to the versions table for each key.
12
+ # Values are objects or procs (which are called with +self+, i.e. the model with the paper
13
+ # trail).
11
14
  def has_paper_trail(options = {})
12
15
  send :include, InstanceMethods
13
16
 
14
17
  cattr_accessor :ignore
15
18
  self.ignore = (options[:ignore] || []).map &:to_s
19
+
20
+ cattr_accessor :meta
21
+ self.meta = options[:meta] || {}
16
22
 
17
23
  cattr_accessor :paper_trail_active
18
24
  self.paper_trail_active = true
@@ -36,26 +42,36 @@ module PaperTrail
36
42
 
37
43
  module InstanceMethods
38
44
  def record_create
39
- versions.create(:event => 'create',
40
- :whodunnit => PaperTrail.whodunnit) if self.class.paper_trail_active
45
+ if self.class.paper_trail_active
46
+ versions.create merge_metadata(:event => 'create', :whodunnit => PaperTrail.whodunnit)
47
+ end
41
48
  end
42
49
 
43
50
  def record_update
44
51
  if changed_and_we_care? and self.class.paper_trail_active
45
- versions.build :event => 'update',
46
- :object => object_to_string(previous_version),
47
- :whodunnit => PaperTrail.whodunnit
52
+ versions.build merge_metadata(:event => 'update',
53
+ :object => object_to_string(previous_version),
54
+ :whodunnit => PaperTrail.whodunnit)
48
55
  end
49
56
  end
50
57
 
51
58
  def record_destroy
52
- versions.create(:event => 'destroy',
53
- :object => object_to_string(previous_version),
54
- :whodunnit => PaperTrail.whodunnit) if self.class.paper_trail_active
59
+ if self.class.paper_trail_active
60
+ versions.create merge_metadata(:event => 'destroy',
61
+ :object => object_to_string(previous_version),
62
+ :whodunnit => PaperTrail.whodunnit)
63
+ end
55
64
  end
56
65
 
57
66
  private
58
67
 
68
+ def merge_metadata(data)
69
+ meta.each do |k,v|
70
+ data[k] = v.respond_to?(:call) ? v.call(self) : v
71
+ end
72
+ data
73
+ end
74
+
59
75
  def previous_version
60
76
  previous = self.clone
61
77
  previous.id = id
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{paper_trail}
8
- s.version = "1.3.1"
8
+ s.version = "1.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Andy Stewart"]
12
- s.date = %q{2009-11-24}
12
+ s.date = %q{2010-01-06}
13
13
  s.email = %q{boss@airbladesoftware.com}
14
14
  s.extra_rdoc_files = [
15
15
  "README.md"
@@ -18,7 +18,10 @@ class Fluxor < ActiveRecord::Base
18
18
  end
19
19
 
20
20
  class Article < ActiveRecord::Base
21
- has_paper_trail :ignore => [:title]
21
+ has_paper_trail :ignore => [:title],
22
+ :meta => {:answer => 42,
23
+ :question => Proc.new { "31 + 11 = #{31 + 11}" },
24
+ :article_id => Proc.new { |article| article.id } }
22
25
  end
23
26
 
24
27
 
@@ -37,9 +40,9 @@ class HasPaperTrailModelTest < Test::Unit::TestCase
37
40
  setup { @article.update_attributes :title => 'My first title', :content => 'Some text here.' }
38
41
  should_change('the number of versions', :by => 1) { Version.count }
39
42
  end
40
-
41
43
  end
42
44
 
45
+
43
46
  context 'A new record' do
44
47
  setup { @widget = Widget.new }
45
48
 
@@ -386,4 +389,60 @@ class HasPaperTrailModelTest < Test::Unit::TestCase
386
389
  end
387
390
  end
388
391
 
392
+
393
+ context 'An item' do
394
+ setup { @article = Article.new }
395
+
396
+ context 'which is created' do
397
+ setup { @article.save }
398
+
399
+ should 'store fixed meta data' do
400
+ assert_equal 42, @article.versions.last.answer
401
+ end
402
+
403
+ should 'store dynamic meta data which is independent of the item' do
404
+ assert_equal '31 + 11 = 42', @article.versions.last.question
405
+ end
406
+
407
+ should 'store dynamic meta data which depends on the item' do
408
+ assert_equal @article.id, @article.versions.last.article_id
409
+ end
410
+
411
+
412
+ context 'and updated' do
413
+ setup { @article.update_attributes! :content => 'Better text.' }
414
+
415
+ should 'store fixed meta data' do
416
+ assert_equal 42, @article.versions.last.answer
417
+ end
418
+
419
+ should 'store dynamic meta data which is independent of the item' do
420
+ assert_equal '31 + 11 = 42', @article.versions.last.question
421
+ end
422
+
423
+ should 'store dynamic meta data which depends on the item' do
424
+ assert_equal @article.id, @article.versions.last.article_id
425
+ end
426
+ end
427
+
428
+
429
+ context 'and destroyd' do
430
+ setup { @article.destroy }
431
+
432
+ should 'store fixed meta data' do
433
+ assert_equal 42, @article.versions.last.answer
434
+ end
435
+
436
+ should 'store dynamic meta data which is independent of the item' do
437
+ assert_equal '31 + 11 = 42', @article.versions.last.question
438
+ end
439
+
440
+ should 'store dynamic meta data which depends on the item' do
441
+ assert_equal @article.id, @article.versions.last.article_id
442
+ end
443
+
444
+ end
445
+ end
446
+ end
447
+
389
448
  end
@@ -22,6 +22,11 @@ ActiveRecord::Schema.define(:version => 0) do
22
22
  t.string :whodunnit
23
23
  t.text :object
24
24
  t.datetime :created_at
25
+
26
+ # Metadata columns.
27
+ t.integer :answer
28
+ t.string :question
29
+ t.integer :article_id
25
30
  end
26
31
  add_index :versions, [:item_type, :item_id]
27
32
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paper_trail
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Stewart
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-24 00:00:00 +00:00
12
+ date: 2010-01-06 00:00:00 +00:00
13
13
  default_executable:
14
14
  dependencies: []
15
15