paper_trail 1.5.0 → 1.5.1
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 +31 -0
- data/VERSION +1 -1
- data/lib/paper_trail.rb +1 -0
- data/lib/paper_trail/has_paper_trail.rb +25 -6
- data/lib/paper_trail/version.rb +1 -2
- data/paper_trail.gemspec +4 -4
- data/test/paper_trail_model_test.rb +67 -1
- data/test/schema.rb +2 -2
- metadata +17 -5
data/README.md
CHANGED
@@ -140,6 +140,37 @@ Undeleting is just as simple:
|
|
140
140
|
In fact you could use PaperTrail to implement an undo system, though I haven't had the opportunity yet to do it myself.
|
141
141
|
|
142
142
|
|
143
|
+
## Navigating Versions
|
144
|
+
|
145
|
+
You can call `previous_version` and `next_version` on an item to get it as it was/became. Note that these methods reify the item for you.
|
146
|
+
|
147
|
+
>> widget = Widget.find 42
|
148
|
+
>> widget.versions.length # 4 for example
|
149
|
+
>> widget = widget.previous_version # => widget == widget.versions.last.reify
|
150
|
+
>> widget = widget.previous_version # => widget == widget.versions[-2].reify
|
151
|
+
>> widget.next_version # => widget == widget.versions.last.reify
|
152
|
+
>> widget.next_version # nil
|
153
|
+
|
154
|
+
As an aside, I'm undecided about whether `widget.versions.last.next_version` should return `nil` or `self` (i.e. `widget`). Let me know if you have a view.
|
155
|
+
|
156
|
+
If instead you have a particular `version` of an item you can navigate to the previous and next versions.
|
157
|
+
|
158
|
+
>> widget = Widget.find 42
|
159
|
+
>> version = widget.versions[-2] # assuming widget has several versions
|
160
|
+
>> previous = version.previous
|
161
|
+
>> next = version.next
|
162
|
+
|
163
|
+
You can find out which of an item's versions yours is:
|
164
|
+
|
165
|
+
>> current_version_number = version.index # 0-based
|
166
|
+
|
167
|
+
Finally, if you got an item by reifying one of its versions, you can navigate back to the version it came from:
|
168
|
+
|
169
|
+
>> latest_version = Widget.find(42).versions.last
|
170
|
+
>> widget = latest_version.reify
|
171
|
+
>> widget.version == latest_version # true
|
172
|
+
|
173
|
+
|
143
174
|
## Finding Out Who Was Responsible For A Change
|
144
175
|
|
145
176
|
If your `ApplicationController` has a `current_user` method, PaperTrail will store the value it returns in the `version`'s `whodunnit` column. Note that this column is a string so you will have to convert it to an integer if it's an id and you want to look up the user later on:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.5.
|
1
|
+
1.5.1
|
data/lib/paper_trail.rb
CHANGED
@@ -3,11 +3,13 @@ module PaperTrail
|
|
3
3
|
|
4
4
|
def self.included(base)
|
5
5
|
base.send :extend, ClassMethods
|
6
|
+
|
7
|
+
# The version this instance was reified from.
|
8
|
+
attr_accessor :version
|
6
9
|
end
|
7
10
|
|
8
11
|
|
9
12
|
module ClassMethods
|
10
|
-
|
11
13
|
# Declare this in your model to track every create, update, and destroy. Each version of
|
12
14
|
# the model is available in the `versions` association.
|
13
15
|
#
|
@@ -18,6 +20,8 @@ module PaperTrail
|
|
18
20
|
# trail). See `PaperTrail::Controller.info_for_paper_trail` for how to store data from
|
19
21
|
# the controller.
|
20
22
|
def has_paper_trail(options = {})
|
23
|
+
# Lazily include the instance methods so we don't clutter up
|
24
|
+
# any more ActiveRecord models than we have to.
|
21
25
|
send :include, InstanceMethods
|
22
26
|
|
23
27
|
cattr_accessor :ignore
|
@@ -49,7 +53,8 @@ module PaperTrail
|
|
49
53
|
end
|
50
54
|
end
|
51
55
|
|
52
|
-
|
56
|
+
# Wrap the following methods in a module so we can include them only in the
|
57
|
+
# ActiveRecord models that declare `has_paper_trail`.
|
53
58
|
module InstanceMethods
|
54
59
|
def record_create
|
55
60
|
if switched_on?
|
@@ -60,7 +65,7 @@ module PaperTrail
|
|
60
65
|
def record_update
|
61
66
|
if switched_on? && changed_and_we_care?
|
62
67
|
versions.build merge_metadata(:event => 'update',
|
63
|
-
:object => object_to_string(
|
68
|
+
:object => object_to_string(item_before_change),
|
64
69
|
:whodunnit => PaperTrail.whodunnit)
|
65
70
|
end
|
66
71
|
end
|
@@ -68,7 +73,7 @@ module PaperTrail
|
|
68
73
|
def record_destroy
|
69
74
|
if switched_on?
|
70
75
|
versions.create merge_metadata(:event => 'destroy',
|
71
|
-
:object => object_to_string(
|
76
|
+
:object => object_to_string(item_before_change),
|
72
77
|
:whodunnit => PaperTrail.whodunnit)
|
73
78
|
end
|
74
79
|
end
|
@@ -81,10 +86,24 @@ module PaperTrail
|
|
81
86
|
# timestamp because a version stores how the object looked before the
|
82
87
|
# change.
|
83
88
|
version = versions.first :conditions => ['created_at > ?', timestamp],
|
84
|
-
|
89
|
+
:order => 'created_at ASC'
|
85
90
|
version.reify if version
|
86
91
|
end
|
87
92
|
|
93
|
+
# Returns the object (not a Version) as it was most recently.
|
94
|
+
def previous_version
|
95
|
+
last_version = version ? version.previous : versions.last
|
96
|
+
last_version.reify if last_version
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns the object (not a Version) as it became next.
|
100
|
+
def next_version
|
101
|
+
# NOTE: if self (the item) was not reified from a version, i.e. it is the
|
102
|
+
# "live" item, we return nil. Perhaps we should return self instead?
|
103
|
+
subsequent_version = version ? version.next : nil
|
104
|
+
subsequent_version.reify if subsequent_version
|
105
|
+
end
|
106
|
+
|
88
107
|
private
|
89
108
|
|
90
109
|
def merge_metadata(data)
|
@@ -96,7 +115,7 @@ module PaperTrail
|
|
96
115
|
data.merge(PaperTrail.controller_info || {})
|
97
116
|
end
|
98
117
|
|
99
|
-
def
|
118
|
+
def item_before_change
|
100
119
|
previous = self.clone
|
101
120
|
previous.id = id
|
102
121
|
changes.each do |attr, ary|
|
data/lib/paper_trail/version.rb
CHANGED
@@ -4,8 +4,6 @@ class Version < ActiveRecord::Base
|
|
4
4
|
|
5
5
|
def reify
|
6
6
|
unless object.nil?
|
7
|
-
# Attributes
|
8
|
-
|
9
7
|
attrs = YAML::load object
|
10
8
|
|
11
9
|
# Normally a polymorphic belongs_to relationship allows us
|
@@ -37,6 +35,7 @@ class Version < ActiveRecord::Base
|
|
37
35
|
end
|
38
36
|
end
|
39
37
|
|
38
|
+
model.version = self
|
40
39
|
model
|
41
40
|
end
|
42
41
|
end
|
data/paper_trail.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{paper_trail}
|
8
|
-
s.version = "1.5.
|
8
|
+
s.version = "1.5.1"
|
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{2010-
|
12
|
+
s.date = %q{2010-06-22}
|
13
13
|
s.email = %q{boss@airbladesoftware.com}
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"README.md"
|
@@ -44,7 +44,7 @@ Gem::Specification.new do |s|
|
|
44
44
|
s.homepage = %q{http://github.com/airblade/paper_trail}
|
45
45
|
s.rdoc_options = ["--charset=UTF-8"]
|
46
46
|
s.require_paths = ["lib"]
|
47
|
-
s.rubygems_version = %q{1.3.
|
47
|
+
s.rubygems_version = %q{1.3.7}
|
48
48
|
s.summary = %q{Track changes to your models' data. Good for auditing or versioning.}
|
49
49
|
s.test_files = [
|
50
50
|
"test/paper_trail_controller_test.rb",
|
@@ -60,7 +60,7 @@ Gem::Specification.new do |s|
|
|
60
60
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
61
61
|
s.specification_version = 3
|
62
62
|
|
63
|
-
if Gem::Version.new(Gem::
|
63
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
64
64
|
else
|
65
65
|
end
|
66
66
|
else
|
@@ -481,7 +481,7 @@ class HasPaperTrailModelTest < Test::Unit::TestCase
|
|
481
481
|
end
|
482
482
|
|
483
483
|
|
484
|
-
context 'and
|
484
|
+
context 'and destroyed' do
|
485
485
|
setup { @article.destroy }
|
486
486
|
|
487
487
|
should 'store fixed meta data' do
|
@@ -500,5 +500,71 @@ class HasPaperTrailModelTest < Test::Unit::TestCase
|
|
500
500
|
end
|
501
501
|
end
|
502
502
|
|
503
|
+
context 'A reified item' do
|
504
|
+
setup do
|
505
|
+
widget = Widget.create :name => 'Bob'
|
506
|
+
%w( Tom Dick Jane ).each { |name| widget.update_attributes :name => name }
|
507
|
+
@version = widget.versions.last
|
508
|
+
@widget = @version.reify
|
509
|
+
end
|
510
|
+
|
511
|
+
should 'know which version it came from' do
|
512
|
+
assert_equal @version, @widget.version
|
513
|
+
end
|
514
|
+
|
515
|
+
should 'return its previous self' do
|
516
|
+
assert_equal @widget.versions[-2].reify, @widget.previous_version
|
517
|
+
end
|
518
|
+
|
519
|
+
end
|
520
|
+
|
521
|
+
|
522
|
+
context 'A non-reified item' do
|
523
|
+
setup { @widget = Widget.new }
|
524
|
+
|
525
|
+
should 'not have a previous version' do
|
526
|
+
assert_nil @widget.previous_version
|
527
|
+
end
|
528
|
+
|
529
|
+
should 'not have a next version' do
|
530
|
+
assert_nil @widget.next_version
|
531
|
+
end
|
532
|
+
|
533
|
+
context 'with versions' do
|
534
|
+
setup do
|
535
|
+
@widget.save
|
536
|
+
%w( Tom Dick Jane ).each { |name| @widget.update_attributes :name => name }
|
537
|
+
end
|
538
|
+
|
539
|
+
should 'have a previous version' do
|
540
|
+
assert_equal @widget.versions.last.reify, @widget.previous_version
|
541
|
+
end
|
542
|
+
|
543
|
+
should 'have a next version' do
|
544
|
+
assert_nil @widget.next_version
|
545
|
+
end
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
549
|
+
context 'A reified item' do
|
550
|
+
setup do
|
551
|
+
widget = Widget.create :name => 'Bob'
|
552
|
+
%w( Tom Dick Jane ).each { |name| widget.update_attributes :name => name }
|
553
|
+
@versions = widget.versions
|
554
|
+
@second_widget = @versions[1].reify # first widget is null
|
555
|
+
@last_widget = @versions.last.reify
|
556
|
+
end
|
557
|
+
|
558
|
+
should 'have a previous version' do
|
559
|
+
assert_nil @second_widget.previous_version
|
560
|
+
assert_equal @versions[-2].reify, @last_widget.previous_version
|
561
|
+
end
|
562
|
+
|
563
|
+
should 'have a next version' do
|
564
|
+
assert_equal @versions[2].reify, @second_widget.next_version
|
565
|
+
assert_nil @last_widget.next_version
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
503
569
|
|
504
570
|
end
|
data/test/schema.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paper_trail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 1
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 5
|
9
|
+
- 1
|
10
|
+
version: 1.5.1
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Andy Stewart
|
@@ -9,7 +15,7 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date: 2010-
|
18
|
+
date: 2010-06-22 00:00:00 +01:00
|
13
19
|
default_executable:
|
14
20
|
dependencies: []
|
15
21
|
|
@@ -57,21 +63,27 @@ rdoc_options:
|
|
57
63
|
require_paths:
|
58
64
|
- lib
|
59
65
|
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
60
67
|
requirements:
|
61
68
|
- - ">="
|
62
69
|
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
63
73
|
version: "0"
|
64
|
-
version:
|
65
74
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
66
76
|
requirements:
|
67
77
|
- - ">="
|
68
78
|
- !ruby/object:Gem::Version
|
79
|
+
hash: 3
|
80
|
+
segments:
|
81
|
+
- 0
|
69
82
|
version: "0"
|
70
|
-
version:
|
71
83
|
requirements: []
|
72
84
|
|
73
85
|
rubyforge_project:
|
74
|
-
rubygems_version: 1.3.
|
86
|
+
rubygems_version: 1.3.7
|
75
87
|
signing_key:
|
76
88
|
specification_version: 3
|
77
89
|
summary: Track changes to your models' data. Good for auditing or versioning.
|