paper_trail 2.0.2 → 2.1.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/.gitignore +3 -0
- data/README.md +21 -1
- data/lib/paper_trail/has_paper_trail.rb +13 -9
- data/lib/paper_trail/version.rb +6 -6
- data/lib/paper_trail/version_number.rb +1 -1
- data/test/dummy/app/models/post.rb +4 -0
- data/test/dummy/app/versions/post_version.rb +3 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +22 -0
- data/test/dummy/db/schema.rb +18 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/unit/model_test.rb +32 -0
- metadata +19 -16
- data/Gemfile.lock +0 -107
data/.gitignore
CHANGED
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
|
-
# :
|
15
|
-
# :
|
16
|
-
# :
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
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
|
-
|
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
|
-
|
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),
|
data/lib/paper_trail/version.rb
CHANGED
@@ -2,16 +2,16 @@ class Version < ActiveRecord::Base
|
|
2
2
|
belongs_to :item, :polymorphic => true
|
3
3
|
validates_presence_of :event
|
4
4
|
|
5
|
-
|
6
|
-
|
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?(
|
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?(
|
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
|
-
|
90
|
+
self.class.with_item_keys(item_type, item_id)
|
91
91
|
end
|
92
92
|
|
93
93
|
def next
|
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
|
data/test/dummy/db/schema.rb
CHANGED
@@ -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
|
data/test/dummy/db/test.sqlite3
CHANGED
Binary file
|
data/test/unit/model_test.rb
CHANGED
@@ -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
|
-
|
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-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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)
|