paper_trail 1.3.1 → 1.4.0

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/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