paper_trail 1.4.0 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +14 -0
- data/VERSION +1 -1
- data/init.rb +1 -1
- data/install.rb +1 -1
- data/lib/paper_trail/has_paper_trail.rb +13 -1
- data/paper_trail.gemspec +3 -5
- data/test/paper_trail_controller_test.rb +5 -4
- data/test/paper_trail_model_test.rb +44 -3
- data/test/test_helper.rb +24 -30
- metadata +2 -4
- data/rails/init.rb +0 -1
- data/test/database.yml +0 -18
data/README.md
CHANGED
@@ -10,6 +10,7 @@ PaperTrail lets you track changes to your models' data. It's good for auditing
|
|
10
10
|
* Does not store updates which only change attributes you are ignoring.
|
11
11
|
* Allows you to get at every version, including the original, even once destroyed.
|
12
12
|
* Allows you to get at every version even if the schema has since changed.
|
13
|
+
* Allows you to get at the version as of a particular time.
|
13
14
|
* Automatically records who was responsible if your controller has a `current_user` method.
|
14
15
|
* Allows you to set who is responsible at model-level (useful for migrations).
|
15
16
|
* Allows you to store arbitrary metadata with each version (useful for filtering versions).
|
@@ -118,6 +119,13 @@ PaperTrail makes reverting to a previous version easy:
|
|
118
119
|
>> widget = widget.versions.last.reify # the widget as it was before the update
|
119
120
|
>> widget.save # reverted
|
120
121
|
|
122
|
+
Alternatively you can find the version at a given time:
|
123
|
+
|
124
|
+
>> widget = widget.version_at(1.day.ago) # the widget as it was one day ago
|
125
|
+
>> widget.save # reverted
|
126
|
+
|
127
|
+
Note `version_at` gives you the object, not a version, so you don't need to call `reify`.
|
128
|
+
|
121
129
|
Undeleting is just as simple:
|
122
130
|
|
123
131
|
>> widget = Widget.find 42
|
@@ -201,6 +209,11 @@ And on again like this:
|
|
201
209
|
PaperTrail has a thorough suite of tests. Thanks to [Zachery Hostens](http://github.com/zacheryph) for making them able to run standalone, i.e. without needing PaperTrail to be sitting in a Rails app.
|
202
210
|
|
203
211
|
|
212
|
+
## Articles
|
213
|
+
|
214
|
+
[Keep a Paper Trail with PaperTrail](http://www.linux-mag.com/id/7528), Linux Magazine, 16th September 2009.
|
215
|
+
|
216
|
+
|
204
217
|
## Problems
|
205
218
|
|
206
219
|
Please use GitHub's [issue tracker](http://github.com/airblade/paper_trail/issues).
|
@@ -211,6 +224,7 @@ Please use GitHub's [issue tracker](http://github.com/airblade/paper_trail/issue
|
|
211
224
|
Many thanks to:
|
212
225
|
|
213
226
|
* [Zachery Hostens](http://github.com/zacheryph)
|
227
|
+
* [Jeremy Weiskotten](http://github.com/jeremyw)
|
214
228
|
|
215
229
|
|
216
230
|
## Inspirations
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.4.
|
1
|
+
1.4.1
|
data/init.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
require 'paper_trail'
|
data/install.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
puts 'You have chosen wisely.'
|
@@ -19,7 +19,7 @@ module PaperTrail
|
|
19
19
|
|
20
20
|
cattr_accessor :meta
|
21
21
|
self.meta = options[:meta] || {}
|
22
|
-
|
22
|
+
|
23
23
|
cattr_accessor :paper_trail_active
|
24
24
|
self.paper_trail_active = true
|
25
25
|
|
@@ -63,6 +63,18 @@ module PaperTrail
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
+
# Returns the object (not a Version) as it was at the given timestamp.
|
67
|
+
def version_at(timestamp)
|
68
|
+
# Short-circuit if the current state is applicable.
|
69
|
+
return self if self.updated_at <= timestamp
|
70
|
+
# Look for the first version created after, rather than before, the
|
71
|
+
# timestamp because a version stores how the object looked before the
|
72
|
+
# change.
|
73
|
+
version = versions.first :conditions => ['created_at > ?', timestamp],
|
74
|
+
:order => 'created_at ASC'
|
75
|
+
version.reify if version
|
76
|
+
end
|
77
|
+
|
66
78
|
private
|
67
79
|
|
68
80
|
def merge_metadata(data)
|
data/paper_trail.gemspec
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# Generated by jeweler
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{paper_trail}
|
8
|
-
s.version = "1.4.
|
8
|
+
s.version = "1.4.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-03-18}
|
13
13
|
s.email = %q{boss@airbladesoftware.com}
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"README.md"
|
@@ -29,9 +29,7 @@ Gem::Specification.new do |s|
|
|
29
29
|
"lib/paper_trail/has_paper_trail.rb",
|
30
30
|
"lib/paper_trail/version.rb",
|
31
31
|
"paper_trail.gemspec",
|
32
|
-
"rails/init.rb",
|
33
32
|
"tasks/paper_trail_tasks.rake",
|
34
|
-
"test/database.yml",
|
35
33
|
"test/paper_trail_controller_test.rb",
|
36
34
|
"test/paper_trail_model_test.rb",
|
37
35
|
"test/paper_trail_schema_test.rb",
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'test_helper'
|
2
2
|
|
3
3
|
class ApplicationController < ActionController::Base
|
4
4
|
def rescue_action(e)
|
@@ -32,10 +32,11 @@ end
|
|
32
32
|
|
33
33
|
|
34
34
|
class PaperTrailControllerTest < ActionController::TestCase #Test::Unit::TestCase
|
35
|
+
tests WidgetsController
|
35
36
|
def setup
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
#@controller = WidgetsController.new
|
38
|
+
#@request = ActionController::TestRequest.new
|
39
|
+
#@response = ActionController::TestResponse.new
|
39
40
|
|
40
41
|
ActionController::Routing::Routes.draw do |map|
|
41
42
|
map.resources :widgets
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'test_helper'
|
2
2
|
|
3
3
|
class Widget < ActiveRecord::Base
|
4
4
|
has_paper_trail
|
@@ -30,7 +30,7 @@ class HasPaperTrailModelTest < Test::Unit::TestCase
|
|
30
30
|
|
31
31
|
context 'A record' do
|
32
32
|
setup { @article = Article.create }
|
33
|
-
|
33
|
+
|
34
34
|
context 'which updates an ignored column' do
|
35
35
|
setup { @article.update_attributes :title => 'My first title' }
|
36
36
|
should_not_change('the number of versions') { Version.count }
|
@@ -100,7 +100,7 @@ class HasPaperTrailModelTest < Test::Unit::TestCase
|
|
100
100
|
assert_match /update/i, @widget.versions.last.event
|
101
101
|
end
|
102
102
|
|
103
|
-
|
103
|
+
|
104
104
|
context 'and has one associated object' do
|
105
105
|
setup do
|
106
106
|
@wotsit = @widget.create_wotsit :name => 'John'
|
@@ -356,6 +356,47 @@ class HasPaperTrailModelTest < Test::Unit::TestCase
|
|
356
356
|
@widget.update_attributes :name => 'Digit'
|
357
357
|
end
|
358
358
|
|
359
|
+
context 'which were created over time' do
|
360
|
+
setup do
|
361
|
+
@created = 2.days.ago
|
362
|
+
@first_update = 1.day.ago
|
363
|
+
@second_update = 1.hour.ago
|
364
|
+
@widget.versions[0].update_attributes :created_at => @created
|
365
|
+
@widget.versions[1].update_attributes :created_at => @first_update
|
366
|
+
@widget.versions[2].update_attributes :created_at => @second_update
|
367
|
+
@widget.update_attribute :updated_at, @second_update
|
368
|
+
end
|
369
|
+
|
370
|
+
should 'return nil for version_at before it was created' do
|
371
|
+
assert_nil @widget.version_at(@created - 1)
|
372
|
+
end
|
373
|
+
|
374
|
+
should 'return how it looked when created for version_at its creation' do
|
375
|
+
assert_equal 'Widget', @widget.version_at(@created).name
|
376
|
+
end
|
377
|
+
|
378
|
+
should "return how it looked when created for version_at just before its first update" do
|
379
|
+
assert_equal 'Widget', @widget.version_at(@first_update - 1).name
|
380
|
+
end
|
381
|
+
|
382
|
+
should "return how it looked when first updated for version_at its first update" do
|
383
|
+
assert_equal 'Fidget', @widget.version_at(@first_update).name
|
384
|
+
end
|
385
|
+
|
386
|
+
should 'return how it looked when first updated for version_at just before its second update' do
|
387
|
+
assert_equal 'Fidget', @widget.version_at(@second_update - 1).name
|
388
|
+
end
|
389
|
+
|
390
|
+
should 'return how it looked when subsequently updated for version_at its second update' do
|
391
|
+
assert_equal 'Digit', @widget.version_at(@second_update).name
|
392
|
+
end
|
393
|
+
|
394
|
+
should 'return the current object for version_at after latest update' do
|
395
|
+
assert_equal 'Digit', @widget.version_at(1.day.from_now).name
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
|
359
400
|
context 'on the first version' do
|
360
401
|
setup { @version = @widget.versions.first }
|
361
402
|
|
data/test/test_helper.rb
CHANGED
@@ -1,43 +1,37 @@
|
|
1
|
-
require '
|
2
|
-
RAILS_ROOT = File.join(File.dirname(__FILE__), %w{.. .. .. ..})
|
3
|
-
$:.unshift(File.join(File.dirname(__FILE__), %w{.. lib}))
|
4
|
-
|
5
|
-
unless defined?(ActiveRecord)
|
6
|
-
if File.directory? RAILS_ROOT + 'config'
|
7
|
-
puts 'using config/boot.rb'
|
8
|
-
ENV['RAILS_ENV'] = 'test'
|
9
|
-
require File.join(RAILS_ROOT, 'config', 'boot.rb')
|
10
|
-
else
|
11
|
-
# simply use installed gems if available
|
12
|
-
puts 'using rubygems'
|
13
|
-
require 'rubygems'
|
14
|
-
gem 'actionpack'; gem 'activerecord'; gem 'activesupport'; gem 'rails'
|
15
|
-
end
|
1
|
+
require 'rubygems'
|
16
2
|
|
17
|
-
|
18
|
-
end
|
3
|
+
require 'test/unit'
|
19
4
|
require 'shoulda'
|
20
|
-
require 'paper_trail'
|
21
|
-
|
22
|
-
def connect_to_database
|
23
|
-
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
24
|
-
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
25
5
|
|
26
|
-
|
6
|
+
require 'active_record'
|
7
|
+
require 'action_controller'
|
8
|
+
require 'action_controller/test_process'
|
9
|
+
require 'active_support'
|
10
|
+
require 'active_support/test_case'
|
27
11
|
|
28
|
-
|
29
|
-
raise "No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite or Sqlite3."
|
30
|
-
end
|
12
|
+
require 'lib/paper_trail'
|
31
13
|
|
32
|
-
|
14
|
+
def connect_to_database
|
15
|
+
ActiveRecord::Base.establish_connection(
|
16
|
+
:adapter => "sqlite3",
|
17
|
+
:database => ":memory:"
|
18
|
+
)
|
19
|
+
ActiveRecord::Migration.verbose = false
|
33
20
|
end
|
34
21
|
|
35
22
|
def load_schema
|
36
23
|
connect_to_database
|
37
|
-
load
|
38
|
-
require File.dirname(__FILE__) + '/../rails/init.rb'
|
24
|
+
load File.dirname(__FILE__) + '/schema.rb'
|
39
25
|
end
|
40
26
|
|
41
27
|
def change_schema
|
42
|
-
load
|
28
|
+
load File.dirname(__FILE__) + '/schema_change.rb'
|
29
|
+
end
|
30
|
+
|
31
|
+
class ActiveRecord::Base
|
32
|
+
def logger
|
33
|
+
@logger ||= Logger.new(nil)
|
34
|
+
end
|
43
35
|
end
|
36
|
+
|
37
|
+
load_schema
|
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.4.
|
4
|
+
version: 1.4.1
|
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: 2010-
|
12
|
+
date: 2010-03-18 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -36,9 +36,7 @@ files:
|
|
36
36
|
- lib/paper_trail/has_paper_trail.rb
|
37
37
|
- lib/paper_trail/version.rb
|
38
38
|
- paper_trail.gemspec
|
39
|
-
- rails/init.rb
|
40
39
|
- tasks/paper_trail_tasks.rake
|
41
|
-
- test/database.yml
|
42
40
|
- test/paper_trail_controller_test.rb
|
43
41
|
- test/paper_trail_model_test.rb
|
44
42
|
- test/paper_trail_schema_test.rb
|
data/rails/init.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require 'paper_trail'
|
data/test/database.yml
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
sqlite3:
|
2
|
-
adapter: sqlite3
|
3
|
-
database: ":memory:"
|
4
|
-
|
5
|
-
postgresql:
|
6
|
-
adapter: postgresql
|
7
|
-
username: postgres
|
8
|
-
password: postgres
|
9
|
-
database: paper_trail_plugin_test
|
10
|
-
min_messages: ERROR
|
11
|
-
|
12
|
-
mysql:
|
13
|
-
adapter: mysql
|
14
|
-
host: localhost
|
15
|
-
username: andy
|
16
|
-
password:
|
17
|
-
database: paper_trail_plugin_test
|
18
|
-
socket: /tmp/mysql.sock
|