paper_trail 2.0.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,6 +1,9 @@
1
1
  test/debug.log
2
2
  test/paper_trail_plugin.sqlite3.db
3
+ test/dummy/log/*
4
+ test/dummy/tmp/*
3
5
  coverage
4
6
  pkg/*
5
7
  *.gem
6
8
  .bundle
9
+ Gemfile.lock
data/README.md CHANGED
@@ -20,7 +20,8 @@ There's an excellent [Railscast on implementing Undo with Paper Trail](http://ra
20
20
  * Can be turned off/on per class (useful for migrations).
21
21
  * Can be turned off/on globally (useful for testing).
22
22
  * No configuration necessary.
23
- * Stores everything in a single database table (generates migration for you).
23
+ * Stores everything in a single database table by default (generates migration for you), or can use separate tables for separate models.
24
+ * Supports custom version classes so different models' versions can have different behaviour.
24
25
  * Thoroughly tested.
25
26
  * Threadsafe.
26
27
 
@@ -308,6 +309,24 @@ To find out who made a `version`'s object look that way, use `version.originator
308
309
  >> last_version.terminator # 'Bob'
309
310
 
310
311
 
312
+ ## Custom Version Classes
313
+
314
+ You can specify custom version subclasses with the `:class_name` option:
315
+
316
+ class Post < ActiveRecord::Base
317
+ has_paper_trail :class_name => 'PostVersion'
318
+ end
319
+
320
+ class PostVersion < Version
321
+ # custom behaviour, e.g:
322
+ set_table_name :post_versions
323
+ end
324
+
325
+ This allows you to store each model's versions in a separate table, which is useful if you have a lot of versions being created.
326
+
327
+ Alternatively you could store certain metadata for one type of version, and other metadata for other versions.
328
+
329
+
311
330
  ## Associations
312
331
 
313
332
  I haven't yet found a good way to get PaperTrail to automatically restore associations when you reify a model. See [here for a little more info](http://airbladesoftware.com/notes/undo-and-redo-with-papertrail).
@@ -562,6 +581,7 @@ Many thanks to:
562
581
  * [Franco Catena](https://github.com/francocatena)
563
582
  * [Emmanuel Gomez](https://github.com/emmanuel)
564
583
  * [Matthew MacLeod](https://github.com/mattmacleod)
584
+ * [benzittlau](https://github.com/benzittlau)
565
585
 
566
586
 
567
587
  ## Inspirations
@@ -11,12 +11,13 @@ module PaperTrail
11
11
  # the model is available in the `versions` association.
12
12
  #
13
13
  # Options:
14
- # :ignore an array of attributes for which a new `Version` will not be created if only they change.
15
- # :only inverse of `ignore` - a new `Version` will be created only for these attributes if supplied
16
- # :meta a hash of extra data to store. You must add a column to the `versions` table for each key.
17
- # Values are objects or procs (which are called with `self`, i.e. the model with the paper
18
- # trail). See `PaperTrail::Controller.info_for_paper_trail` for how to store data from
19
- # the controller.
14
+ # :class_name the name of a custom Version class. This class should inherit from Version.
15
+ # :ignore an array of attributes for which a new `Version` will not be created if only they change.
16
+ # :only inverse of `ignore` - a new `Version` will be created only for these attributes if supplied
17
+ # :meta a hash of extra data to store. You must add a column to the `versions` table for each key.
18
+ # Values are objects or procs (which are called with `self`, i.e. the model with the paper
19
+ # trail). See `PaperTrail::Controller.info_for_paper_trail` for how to store data from
20
+ # the controller.
20
21
  def has_paper_trail(options = {})
21
22
  # Lazily include the instance methods so we don't clutter up
22
23
  # any more ActiveRecord models than we have to.
@@ -24,6 +25,9 @@ module PaperTrail
24
25
 
25
26
  # The version this instance was reified from.
26
27
  attr_accessor :version
28
+
29
+ cattr_accessor :version_class_name
30
+ self.version_class_name = options[:class_name] || "Version"
27
31
 
28
32
  cattr_accessor :ignore
29
33
  self.ignore = ([options[:ignore]].flatten.compact || []).map &:to_s
@@ -39,7 +43,7 @@ module PaperTrail
39
43
  cattr_accessor :paper_trail_active
40
44
  self.paper_trail_active = true
41
45
 
42
- has_many :versions, :as => :item, :order => 'created_at ASC, id ASC'
46
+ has_many :versions, :class_name => version_class_name, :as => :item, :order => 'created_at ASC, id ASC'
43
47
 
44
48
  after_create :record_create
45
49
  before_update :record_update
@@ -68,7 +72,7 @@ module PaperTrail
68
72
 
69
73
  # Returns who put the object into its current state.
70
74
  def originator
71
- Version.with_item_keys(self.class.name, id).last.try :whodunnit
75
+ version_class_name.constantize.with_item_keys(self.class.name, id).last.try :whodunnit
72
76
  end
73
77
 
74
78
  # Returns the object (not a Version) as it was at the given timestamp.
@@ -111,7 +115,7 @@ module PaperTrail
111
115
 
112
116
  def record_destroy
113
117
  if switched_on? and not new_record?
114
- Version.create merge_metadata(:item_id => self.id,
118
+ version_class_name.constantize.create merge_metadata(:item_id => self.id,
115
119
  :item_type => self.class.name,
116
120
  :event => 'destroy',
117
121
  :object => object_to_string(item_before_change),
@@ -2,16 +2,16 @@ class Version < ActiveRecord::Base
2
2
  belongs_to :item, :polymorphic => true
3
3
  validates_presence_of :event
4
4
 
5
- scope :with_item_keys, lambda { |item_type, item_id|
6
- where(:item_type => item_type, :item_id => item_id)
7
- }
5
+ def self.with_item_keys(item_type, item_id)
6
+ scoped(:conditions => { :item_type => item_type, :item_id => item_id })
7
+ end
8
8
 
9
9
  scope :subsequent, lambda { |version|
10
- where(["id > ?", version.is_a?(Version) ? version.id : version]).order("id ASC")
10
+ where(["id > ?", version.is_a?(self) ? version.id : version]).order("id ASC")
11
11
  }
12
12
 
13
13
  scope :preceding, lambda { |version|
14
- where(["id < ?", version.is_a?(Version) ? version.id : version]).order("id DESC")
14
+ where(["id < ?", version.is_a?(self) ? version.id : version]).order("id DESC")
15
15
  }
16
16
 
17
17
  scope :after, lambda { |timestamp|
@@ -87,7 +87,7 @@ class Version < ActiveRecord::Base
87
87
  end
88
88
 
89
89
  def sibling_versions
90
- Version.with_item_keys(item_type, item_id)
90
+ self.class.with_item_keys(item_type, item_id)
91
91
  end
92
92
 
93
93
  def next
@@ -1,3 +1,3 @@
1
1
  module PaperTrail
2
- VERSION = '2.0.2'
2
+ VERSION = '2.1.0'
3
3
  end
@@ -0,0 +1,4 @@
1
+ class Post < ActiveRecord::Base
2
+ has_paper_trail :class_name => "PostVersion"
3
+
4
+ end
@@ -0,0 +1,3 @@
1
+ class PostVersion < Version
2
+ set_table_name :post_versions
3
+ end
Binary file
@@ -34,6 +34,20 @@ class SetUpTestTables < ActiveRecord::Migration
34
34
  t.string :user_agent
35
35
  end
36
36
  add_index :versions, [:item_type, :item_id]
37
+
38
+ create_table :post_versions, :force => true do |t|
39
+ t.string :item_type, :null => false
40
+ t.integer :item_id, :null => false
41
+ t.string :event, :null => false
42
+ t.string :whodunnit
43
+ t.text :object
44
+ t.datetime :created_at
45
+
46
+ # Controller info columns.
47
+ t.string :ip
48
+ t.string :user_agent
49
+ end
50
+ add_index :post_versions, [:item_type, :item_id]
37
51
 
38
52
  create_table :wotsits, :force => true do |t|
39
53
  t.integer :widget_id
@@ -68,9 +82,15 @@ class SetUpTestTables < ActiveRecord::Migration
68
82
  create_table :songs, :force => true do |t|
69
83
  t.integer :length
70
84
  end
85
+
86
+ create_table :posts, :force => true do |t|
87
+ t.string :title
88
+ t.string :content
89
+ end
71
90
  end
72
91
 
73
92
  def self.down
93
+ drop_table :posts
74
94
  drop_table :songs
75
95
  drop_table :people
76
96
  drop_table :authorships
@@ -78,6 +98,8 @@ class SetUpTestTables < ActiveRecord::Migration
78
98
  drop_table :articles
79
99
  drop_table :fluxors
80
100
  drop_table :wotsits
101
+ remove_index :post_versions, :column => [:item_type, :item_id]
102
+ drop_table :post_versions
81
103
  remove_index :versions, :column => [:item_type, :item_id]
82
104
  drop_table :versions
83
105
  drop_table :widgets
@@ -36,6 +36,24 @@ ActiveRecord::Schema.define(:version => 20110208155312) do
36
36
  t.string "name"
37
37
  end
38
38
 
39
+ create_table "post_versions", :force => true do |t|
40
+ t.string "item_type", :null => false
41
+ t.integer "item_id", :null => false
42
+ t.string "event", :null => false
43
+ t.string "whodunnit"
44
+ t.text "object"
45
+ t.datetime "created_at"
46
+ t.string "ip"
47
+ t.string "user_agent"
48
+ end
49
+
50
+ add_index "post_versions", ["item_type", "item_id"], :name => "index_post_versions_on_item_type_and_item_id"
51
+
52
+ create_table "posts", :force => true do |t|
53
+ t.string "title"
54
+ t.string "content"
55
+ end
56
+
39
57
  create_table "songs", :force => true do |t|
40
58
  t.integer "length"
41
59
  end
Binary file
@@ -710,6 +710,38 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
710
710
  end
711
711
  end
712
712
 
713
+ context 'A new model instance which uses a custom Version class' do
714
+ setup { @post = Post.new }
715
+
716
+ context 'which is then saved' do
717
+ setup { @post.save }
718
+ should_change('the number of post versions') { PostVersion.count }
719
+ should_not_change('the number of versions') { Version.count }
720
+ end
721
+ end
722
+
723
+ context 'An existing model instance which uses a custom Version class' do
724
+ setup { @post = Post.create }
725
+
726
+ context 'on the first version' do
727
+ setup { @version = @post.versions.first }
728
+
729
+ should 'have the correct index' do
730
+ assert_equal 0, @version.index
731
+ end
732
+ end
733
+
734
+ should 'should have versions of the custom class' do
735
+ assert_equal "PostVersion", @post.versions.first.class.name
736
+ end
737
+
738
+ context 'which is modified' do
739
+ setup { @post.update_attributes({ :content => "Some new content" }) }
740
+ should_change('the number of post versions') { PostVersion.count }
741
+ should_not_change('the number of versions') { Version.count }
742
+ end
743
+ end
744
+
713
745
 
714
746
  context 'An overwritten default accessor' do
715
747
  setup do
metadata CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
5
5
  prerelease: false
6
6
  segments:
7
7
  - 2
8
+ - 1
8
9
  - 0
9
- - 2
10
- version: 2.0.2
10
+ version: 2.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Andy Stewart
@@ -15,12 +15,13 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-16 00:00:00 +01:00
18
+ date: 2011-03-31 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
+ name: rails
22
23
  prerelease: false
23
- version_requirements: &id001 !ruby/object:Gem::Requirement
24
+ requirement: &id001 !ruby/object:Gem::Requirement
24
25
  none: false
25
26
  requirements:
26
27
  - - ~>
@@ -29,12 +30,12 @@ dependencies:
29
30
  segments:
30
31
  - 3
31
32
  version: "3"
32
- requirement: *id001
33
- name: rails
34
33
  type: :runtime
34
+ version_requirements: *id001
35
35
  - !ruby/object:Gem::Dependency
36
+ name: shoulda
36
37
  prerelease: false
37
- version_requirements: &id002 !ruby/object:Gem::Requirement
38
+ requirement: &id002 !ruby/object:Gem::Requirement
38
39
  none: false
39
40
  requirements:
40
41
  - - "="
@@ -45,12 +46,12 @@ dependencies:
45
46
  - 10
46
47
  - 3
47
48
  version: 2.10.3
48
- requirement: *id002
49
- name: shoulda
50
49
  type: :development
50
+ version_requirements: *id002
51
51
  - !ruby/object:Gem::Dependency
52
+ name: sqlite3-ruby
52
53
  prerelease: false
53
- version_requirements: &id003 !ruby/object:Gem::Requirement
54
+ requirement: &id003 !ruby/object:Gem::Requirement
54
55
  none: false
55
56
  requirements:
56
57
  - - ~>
@@ -60,12 +61,12 @@ dependencies:
60
61
  - 1
61
62
  - 2
62
63
  version: "1.2"
63
- requirement: *id003
64
- name: sqlite3-ruby
65
64
  type: :development
65
+ version_requirements: *id003
66
66
  - !ruby/object:Gem::Dependency
67
+ name: capybara
67
68
  prerelease: false
68
- version_requirements: &id004 !ruby/object:Gem::Requirement
69
+ requirement: &id004 !ruby/object:Gem::Requirement
69
70
  none: false
70
71
  requirements:
71
72
  - - ">="
@@ -76,9 +77,8 @@ dependencies:
76
77
  - 4
77
78
  - 0
78
79
  version: 0.4.0
79
- requirement: *id004
80
- name: capybara
81
80
  type: :development
81
+ version_requirements: *id004
82
82
  description: Track changes to your models' data. Good for auditing or versioning.
83
83
  email: boss@airbladesoftware.com
84
84
  executables: []
@@ -90,7 +90,6 @@ extra_rdoc_files: []
90
90
  files:
91
91
  - .gitignore
92
92
  - Gemfile
93
- - Gemfile.lock
94
93
  - MIT-LICENSE
95
94
  - README.md
96
95
  - Rakefile
@@ -115,9 +114,11 @@ files:
115
114
  - test/dummy/app/models/fluxor.rb
116
115
  - test/dummy/app/models/foo_widget.rb
117
116
  - test/dummy/app/models/person.rb
117
+ - test/dummy/app/models/post.rb
118
118
  - test/dummy/app/models/song.rb
119
119
  - test/dummy/app/models/widget.rb
120
120
  - test/dummy/app/models/wotsit.rb
121
+ - test/dummy/app/versions/post_version.rb
121
122
  - test/dummy/app/views/layouts/application.html.erb
122
123
  - test/dummy/config.ru
123
124
  - test/dummy/config/application.rb
@@ -203,9 +204,11 @@ test_files:
203
204
  - test/dummy/app/models/fluxor.rb
204
205
  - test/dummy/app/models/foo_widget.rb
205
206
  - test/dummy/app/models/person.rb
207
+ - test/dummy/app/models/post.rb
206
208
  - test/dummy/app/models/song.rb
207
209
  - test/dummy/app/models/widget.rb
208
210
  - test/dummy/app/models/wotsit.rb
211
+ - test/dummy/app/versions/post_version.rb
209
212
  - test/dummy/app/views/layouts/application.html.erb
210
213
  - test/dummy/config.ru
211
214
  - test/dummy/config/application.rb
data/Gemfile.lock DELETED
@@ -1,107 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- paper_trail (2.0.2)
5
- rails (~> 3)
6
-
7
- GEM
8
- remote: http://rubygems.org/
9
- specs:
10
- abstract (1.0.0)
11
- actionmailer (3.0.4)
12
- actionpack (= 3.0.4)
13
- mail (~> 2.2.15)
14
- actionpack (3.0.4)
15
- activemodel (= 3.0.4)
16
- activesupport (= 3.0.4)
17
- builder (~> 2.1.2)
18
- erubis (~> 2.6.6)
19
- i18n (~> 0.4)
20
- rack (~> 1.2.1)
21
- rack-mount (~> 0.6.13)
22
- rack-test (~> 0.5.7)
23
- tzinfo (~> 0.3.23)
24
- activemodel (3.0.4)
25
- activesupport (= 3.0.4)
26
- builder (~> 2.1.2)
27
- i18n (~> 0.4)
28
- activerecord (3.0.4)
29
- activemodel (= 3.0.4)
30
- activesupport (= 3.0.4)
31
- arel (~> 2.0.2)
32
- tzinfo (~> 0.3.23)
33
- activeresource (3.0.4)
34
- activemodel (= 3.0.4)
35
- activesupport (= 3.0.4)
36
- activesupport (3.0.4)
37
- arel (2.0.9)
38
- builder (2.1.2)
39
- capybara (0.4.1.1)
40
- celerity (>= 0.7.9)
41
- culerity (>= 0.2.4)
42
- mime-types (>= 1.16)
43
- nokogiri (>= 1.3.3)
44
- rack (>= 1.0.0)
45
- rack-test (>= 0.5.4)
46
- selenium-webdriver (>= 0.0.27)
47
- xpath (~> 0.1.3)
48
- celerity (0.8.7)
49
- childprocess (0.1.6)
50
- ffi (~> 0.6.3)
51
- culerity (0.2.15)
52
- erubis (2.6.6)
53
- abstract (>= 1.0.0)
54
- ffi (0.6.3)
55
- rake (>= 0.8.7)
56
- i18n (0.5.0)
57
- json_pure (1.5.1)
58
- mail (2.2.15)
59
- activesupport (>= 2.3.6)
60
- i18n (>= 0.4.0)
61
- mime-types (~> 1.16)
62
- treetop (~> 1.4.8)
63
- mime-types (1.16)
64
- nokogiri (1.4.4)
65
- polyglot (0.3.1)
66
- rack (1.2.1)
67
- rack-mount (0.6.13)
68
- rack (>= 1.0.0)
69
- rack-test (0.5.7)
70
- rack (>= 1.0)
71
- rails (3.0.4)
72
- actionmailer (= 3.0.4)
73
- actionpack (= 3.0.4)
74
- activerecord (= 3.0.4)
75
- activeresource (= 3.0.4)
76
- activesupport (= 3.0.4)
77
- bundler (~> 1.0)
78
- railties (= 3.0.4)
79
- railties (3.0.4)
80
- actionpack (= 3.0.4)
81
- activesupport (= 3.0.4)
82
- rake (>= 0.8.7)
83
- thor (~> 0.14.4)
84
- rake (0.8.7)
85
- rubyzip (0.9.4)
86
- selenium-webdriver (0.1.2)
87
- childprocess (~> 0.1.5)
88
- ffi (~> 0.6.3)
89
- json_pure
90
- rubyzip
91
- shoulda (2.10.3)
92
- sqlite3-ruby (1.3.1)
93
- thor (0.14.6)
94
- treetop (1.4.9)
95
- polyglot (>= 0.3.1)
96
- tzinfo (0.3.24)
97
- xpath (0.1.3)
98
- nokogiri (~> 1.3)
99
-
100
- PLATFORMS
101
- ruby
102
-
103
- DEPENDENCIES
104
- capybara (>= 0.4.0)
105
- paper_trail!
106
- shoulda (= 2.10.3)
107
- sqlite3-ruby (~> 1.2)