airblade-paper_trail 1.0.1 → 1.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 +2 -0
- data/MIT-LICENSE +1 -1
- data/README.md +3 -1
- data/generators/paper_trail/templates/create_versions.rb +2 -0
- data/lib/paper_trail/version.rb +43 -4
- data/paper_trail.gemspec +4 -3
- data/test/paper_trail_model_test.rb +126 -7
- data/test/paper_trail_schema_test.rb +3 -2
- data/test/schema.rb +12 -5
- metadata +3 -2
data/.gitignore
ADDED
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
|
@@ -127,9 +127,11 @@ And on again like this:
|
|
|
127
127
|
|
|
128
128
|
`config.gem 'airblade-paper_trail', :lib => 'paper_trail', :source => 'http://gems.github.com'`
|
|
129
129
|
|
|
130
|
+
or:
|
|
131
|
+
|
|
130
132
|
`script/plugin install git://github.com/airblade/paper_trail.git`
|
|
131
133
|
|
|
132
|
-
2. Generate a migration which
|
|
134
|
+
2. Generate a migration which will add a `versions` table to your database.
|
|
133
135
|
|
|
134
136
|
`script/generate paper_trail`
|
|
135
137
|
|
|
@@ -8,9 +8,11 @@ class CreateVersions < ActiveRecord::Migration
|
|
|
8
8
|
t.text :object
|
|
9
9
|
t.datetime :created_at
|
|
10
10
|
end
|
|
11
|
+
add_index :versions, [:item_type, :item_id]
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def self.down
|
|
15
|
+
remove_index :versions, [:item_type, :item_id]
|
|
14
16
|
drop_table :versions
|
|
15
17
|
end
|
|
16
18
|
end
|
data/lib/paper_trail/version.rb
CHANGED
|
@@ -4,17 +4,56 @@ class Version < ActiveRecord::Base
|
|
|
4
4
|
|
|
5
5
|
def reify
|
|
6
6
|
unless object.nil?
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
# Attributes
|
|
8
|
+
|
|
9
|
+
attrs = YAML::load object
|
|
10
|
+
|
|
11
|
+
# Normally a polymorphic belongs_to relationship allows us
|
|
12
|
+
# to get the object we belong to by calling, in this case,
|
|
13
|
+
# +item+. However this returns nil if +item+ has been
|
|
14
|
+
# destroyed, and we need to be able to retrieve destroyed
|
|
15
|
+
# objects.
|
|
16
|
+
#
|
|
17
|
+
# In this situation we constantize the +item_type+ to get hold of
|
|
18
|
+
# the class...except when the stored object's attributes
|
|
19
|
+
# include a +type+ key. If this is the case, the object
|
|
20
|
+
# we belong to is using single table inheritance and the
|
|
21
|
+
# +item_type+ will be the base class, not the actual subclass.
|
|
22
|
+
# If +type+ is present but empty, the class is the base class.
|
|
23
|
+
|
|
24
|
+
if item
|
|
25
|
+
model = item
|
|
26
|
+
else
|
|
27
|
+
class_name = attrs['type'].blank? ? item_type : attrs['type']
|
|
28
|
+
klass = class_name.constantize
|
|
29
|
+
model = klass.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
attrs.each do |k, v|
|
|
11
33
|
begin
|
|
12
34
|
model.send "#{k}=", v
|
|
13
35
|
rescue NoMethodError
|
|
14
36
|
RAILS_DEFAULT_LOGGER.warn "Attribute #{k} does not exist on #{item_type} (Version id: #{id})."
|
|
15
37
|
end
|
|
16
38
|
end
|
|
39
|
+
|
|
17
40
|
model
|
|
18
41
|
end
|
|
19
42
|
end
|
|
43
|
+
|
|
44
|
+
def next
|
|
45
|
+
Version.first :conditions => ["id > ? AND item_type = ? AND item_id = ?", id, item_type, item_id],
|
|
46
|
+
:order => 'id ASC'
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def previous
|
|
50
|
+
Version.first :conditions => ["id < ? AND item_type = ? AND item_id = ?", id, item_type, item_id],
|
|
51
|
+
:order => 'id DESC'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def index
|
|
55
|
+
Version.all(:conditions => ["item_type = ? AND item_id = ?", item_type, item_id],
|
|
56
|
+
:order => 'id ASC').index(self)
|
|
57
|
+
end
|
|
58
|
+
|
|
20
59
|
end
|
data/paper_trail.gemspec
CHANGED
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |s|
|
|
4
4
|
s.name = %q{paper_trail}
|
|
5
|
-
s.version = "1.0
|
|
5
|
+
s.version = "1.1.0"
|
|
6
6
|
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
8
8
|
s.authors = ["Andy Stewart"]
|
|
9
|
-
s.date = %q{2009-
|
|
9
|
+
s.date = %q{2009-06-22}
|
|
10
10
|
s.email = %q{boss@airbladesoftware.com}
|
|
11
11
|
s.extra_rdoc_files = [
|
|
12
12
|
"README.md"
|
|
13
13
|
]
|
|
14
14
|
s.files = [
|
|
15
|
-
"
|
|
15
|
+
".gitignore",
|
|
16
|
+
"MIT-LICENSE",
|
|
16
17
|
"README.md",
|
|
17
18
|
"Rakefile",
|
|
18
19
|
"VERSION",
|
|
@@ -2,6 +2,19 @@ require File.dirname(__FILE__) + '/test_helper.rb'
|
|
|
2
2
|
|
|
3
3
|
class Widget < ActiveRecord::Base
|
|
4
4
|
has_paper_trail
|
|
5
|
+
has_one :wotsit
|
|
6
|
+
has_many :fluxors, :order => :name
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class FooWidget < Widget
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class Wotsit < ActiveRecord::Base
|
|
13
|
+
belongs_to :widget
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Fluxor < ActiveRecord::Base
|
|
17
|
+
belongs_to :widget
|
|
5
18
|
end
|
|
6
19
|
|
|
7
20
|
|
|
@@ -65,22 +78,63 @@ class HasPaperTrailModelTest < Test::Unit::TestCase
|
|
|
65
78
|
assert_match /update/i, @widget.versions.last.event
|
|
66
79
|
end
|
|
67
80
|
|
|
81
|
+
|
|
82
|
+
context 'and has one associated object' do
|
|
83
|
+
setup do
|
|
84
|
+
@wotsit = @widget.create_wotsit :name => 'John'
|
|
85
|
+
@reified_widget = @widget.versions.last.reify
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
should 'copy the has_one association when reifying' do
|
|
89
|
+
assert_equal @wotsit, @reified_widget.wotsit
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
context 'and has many associated objects' do
|
|
95
|
+
setup do
|
|
96
|
+
@f0 = @widget.fluxors.create :name => 'f-zero'
|
|
97
|
+
@f1 = @widget.fluxors.create :name => 'f-one'
|
|
98
|
+
@reified_widget = @widget.versions.last.reify
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
should 'copy the has_many associations when reifying' do
|
|
102
|
+
assert_equal @widget.fluxors.length, @reified_widget.fluxors.length
|
|
103
|
+
assert_same_elements @widget.fluxors, @reified_widget.fluxors
|
|
104
|
+
|
|
105
|
+
assert_equal @widget.versions.length, @reified_widget.versions.length
|
|
106
|
+
assert_same_elements @widget.versions, @reified_widget.versions
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
68
110
|
|
|
69
111
|
context 'and then destroyed' do
|
|
70
|
-
setup
|
|
112
|
+
setup do
|
|
113
|
+
@fluxor = @widget.fluxors.create :name => 'flux'
|
|
114
|
+
@widget.destroy
|
|
115
|
+
@reified_widget = @widget.versions.last.reify
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
should 'record the correct event' do
|
|
119
|
+
assert_match /destroy/i, @widget.versions.last.event
|
|
120
|
+
end
|
|
71
121
|
|
|
72
122
|
should 'have three previous versions' do
|
|
73
123
|
assert_equal 3, @widget.versions.length
|
|
74
124
|
end
|
|
75
125
|
|
|
76
126
|
should 'be available in its previous version' do
|
|
77
|
-
|
|
78
|
-
assert_equal @widget.
|
|
79
|
-
assert_equal @widget.attributes, widget.attributes
|
|
127
|
+
assert_equal @widget.id, @reified_widget.id
|
|
128
|
+
assert_equal @widget.attributes, @reified_widget.attributes
|
|
80
129
|
end
|
|
81
130
|
|
|
82
|
-
should '
|
|
83
|
-
|
|
131
|
+
should 'be re-creatable from its previous version' do
|
|
132
|
+
assert @reified_widget.save
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
should 'restore its associations on its previous version' do
|
|
136
|
+
@reified_widget.save
|
|
137
|
+
assert_equal 1, @reified_widget.fluxors.length
|
|
84
138
|
end
|
|
85
139
|
end
|
|
86
140
|
end
|
|
@@ -88,7 +142,7 @@ class HasPaperTrailModelTest < Test::Unit::TestCase
|
|
|
88
142
|
end
|
|
89
143
|
|
|
90
144
|
|
|
91
|
-
# Test the serialisation and
|
|
145
|
+
# Test the serialisation and deserialisation.
|
|
92
146
|
# TODO: binary
|
|
93
147
|
context "A record's papertrail" do
|
|
94
148
|
setup do
|
|
@@ -248,4 +302,69 @@ class HasPaperTrailModelTest < Test::Unit::TestCase
|
|
|
248
302
|
end
|
|
249
303
|
end
|
|
250
304
|
end
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
context 'A subclass' do
|
|
308
|
+
setup do
|
|
309
|
+
@foo = FooWidget.create
|
|
310
|
+
@foo.update_attributes :name => 'Fooey'
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
should 'reify with the correct type' do
|
|
314
|
+
thing = @foo.versions.last.reify
|
|
315
|
+
assert_kind_of FooWidget, thing
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
context 'when destroyed' do
|
|
320
|
+
setup { @foo.destroy }
|
|
321
|
+
|
|
322
|
+
should 'reify with the correct type' do
|
|
323
|
+
thing = @foo.versions.last.reify
|
|
324
|
+
assert_kind_of FooWidget, thing
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
context 'An item with versions' do
|
|
331
|
+
setup do
|
|
332
|
+
@widget = Widget.create :name => 'Widget'
|
|
333
|
+
@widget.update_attributes :name => 'Fidget'
|
|
334
|
+
@widget.update_attributes :name => 'Digit'
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
context 'on the first version' do
|
|
338
|
+
setup { @version = @widget.versions.first }
|
|
339
|
+
|
|
340
|
+
should 'have a nil previous version' do
|
|
341
|
+
assert_nil @version.previous
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
should 'return the next version' do
|
|
345
|
+
assert_equal @widget.versions[1], @version.next
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
should 'return the correct index' do
|
|
349
|
+
assert_equal 0, @version.index
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
context 'on the last version' do
|
|
354
|
+
setup { @version = @widget.versions.last }
|
|
355
|
+
|
|
356
|
+
should 'return the previous version' do
|
|
357
|
+
assert_equal @widget.versions[@widget.versions.length - 2], @version.previous
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
should 'have a nil next version' do
|
|
361
|
+
assert_nil @version.next
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
should 'return the correct index' do
|
|
365
|
+
assert_equal @widget.versions.length - 1, @version.index
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
251
370
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require 'test_helper'
|
|
2
2
|
|
|
3
|
-
class
|
|
3
|
+
class PaperTrailSchemaTest < ActiveSupport::TestCase
|
|
4
4
|
def setup
|
|
5
5
|
load_schema
|
|
6
6
|
end
|
|
@@ -8,6 +8,7 @@ class PaperTrailTest < ActiveSupport::TestCase
|
|
|
8
8
|
def test_schema_has_loaded_correctly
|
|
9
9
|
assert_equal [], Widget.all
|
|
10
10
|
assert_equal [], Version.all
|
|
11
|
-
assert_equal [],
|
|
11
|
+
assert_equal [], Wotsit.all
|
|
12
|
+
assert_equal [], Fluxor.all
|
|
12
13
|
end
|
|
13
14
|
end
|
data/test/schema.rb
CHANGED
|
@@ -12,11 +12,7 @@ ActiveRecord::Schema.define(:version => 0) do
|
|
|
12
12
|
t.boolean :a_boolean
|
|
13
13
|
t.datetime :created_at, :updated_at
|
|
14
14
|
t.string :sacrificial_column
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
create_table :users, :force => true do |t|
|
|
18
|
-
t.string :name
|
|
19
|
-
t.datetime :created_at, :updated_at
|
|
15
|
+
t.string :type
|
|
20
16
|
end
|
|
21
17
|
|
|
22
18
|
create_table :versions, :force => true do |t|
|
|
@@ -27,5 +23,16 @@ ActiveRecord::Schema.define(:version => 0) do
|
|
|
27
23
|
t.text :object
|
|
28
24
|
t.datetime :created_at
|
|
29
25
|
end
|
|
26
|
+
add_index :versions, [:item_type, :item_id]
|
|
27
|
+
|
|
28
|
+
create_table :wotsits, :force => true do |t|
|
|
29
|
+
t.integer :widget_id
|
|
30
|
+
t.string :name
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
create_table :fluxors, :force => true do |t|
|
|
34
|
+
t.integer :widget_id
|
|
35
|
+
t.string :name
|
|
36
|
+
end
|
|
30
37
|
|
|
31
38
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: airblade-paper_trail
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.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-
|
|
12
|
+
date: 2009-06-22 00:00:00 -07:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies: []
|
|
15
15
|
|
|
@@ -22,6 +22,7 @@ extensions: []
|
|
|
22
22
|
extra_rdoc_files:
|
|
23
23
|
- README.md
|
|
24
24
|
files:
|
|
25
|
+
- .gitignore
|
|
25
26
|
- MIT-LICENSE
|
|
26
27
|
- README.md
|
|
27
28
|
- Rakefile
|