mongoid-history 0.1.7 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +4 -0
- data/Gemfile +6 -9
- data/README.md +202 -0
- data/Rakefile +0 -19
- data/VERSION +1 -1
- data/lib/mongoid/history/sweeper.rb +22 -6
- data/lib/mongoid/history/trackable.rb +25 -8
- data/lib/mongoid/history/tracker.rb +65 -7
- data/mongoid-history.gemspec +32 -49
- data/spec/integration/integration_spec.rb +123 -18
- metadata +32 -69
- data/.rvmrc +0 -1
- data/README.rdoc +0 -144
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -1,16 +1,13 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
|
3
3
|
gem "easy_diff"
|
4
|
-
gem "mongoid", "
|
4
|
+
gem "mongoid", ">= 2.0.0"
|
5
5
|
|
6
6
|
group :development do
|
7
|
-
gem "bson_ext"
|
8
|
-
gem "rspec"
|
9
|
-
gem "yard"
|
10
|
-
gem "bundler", "
|
11
|
-
gem "jeweler"
|
12
|
-
gem "rcov", ">= 0"
|
13
|
-
gem "reek", "~> 1.2.8"
|
14
|
-
gem "roodi", "~> 2.1.0"
|
7
|
+
gem "bson_ext"
|
8
|
+
gem "rspec"
|
9
|
+
gem "yard"
|
10
|
+
gem "bundler", ">= 1.0.0"
|
11
|
+
gem "jeweler"
|
15
12
|
gem "database_cleaner"
|
16
13
|
end
|
data/README.md
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
mongoid-history
|
2
|
+
===============
|
3
|
+
|
4
|
+
[![Build Status](https://secure.travis-ci.org/aq1018/mongoid-history.png?branch=master)](http://travis-ci.org/aq1018/mongoid-history) [![Dependency Status](https://gemnasium.com/aq1018/mongoid-history.png?travis)](https://gemnasium.com/aq1018/mongoid-history)
|
5
|
+
|
6
|
+
|
7
|
+
In frustration of Mongoid::Versioning, I created this plugin for tracking historical changes for any document, including embedded ones. It achieves this by storing all history tracks in a single collection that you define. (See Usage for more details) Embedded documents are referenced by storing an association path, which is an array of document_name and document_id fields starting from the top most parent document and down to the embedded document that should track history.
|
8
|
+
|
9
|
+
This plugin implements multi-user undo, which allows users to undo any history change in any order. Undoing a document also creates a new history track. This is great for auditing and preventing vandalism, but it is probably not suitable for use cases such as a wiki.
|
10
|
+
|
11
|
+
Upgrading to mongoid-history-0.2.0
|
12
|
+
----------------------------------
|
13
|
+
|
14
|
+
If you are upgrade from 0.1.x to version 0.2.x, you need to run the following code **before** you start to use the 0.2.x. This is due to changes in `Mongoid::History::Tracker`'s `association_chain` field.
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
Mongoid::History.tracker_class.all.each do |tracker|
|
18
|
+
tracker.association_chain[1..-1].each do |node|
|
19
|
+
node['name'] = node['name'].tableize
|
20
|
+
end
|
21
|
+
tracker.save!
|
22
|
+
end
|
23
|
+
```
|
24
|
+
|
25
|
+
Install
|
26
|
+
-------
|
27
|
+
|
28
|
+
Currently this gem supports ruby 1.9.x only. ruby 1.8.7, ree and rubinus are not working right now.
|
29
|
+
|
30
|
+
```
|
31
|
+
gem install mongoid-history
|
32
|
+
```
|
33
|
+
|
34
|
+
Rails 3
|
35
|
+
-------
|
36
|
+
|
37
|
+
In your Gemfile:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
gem 'mongoid-history'
|
41
|
+
```
|
42
|
+
|
43
|
+
Usage
|
44
|
+
-----
|
45
|
+
|
46
|
+
Here is a quick example on how to use this plugin. For more details, please look at spec/integration/integration_spec.rb. It offers more detailed examples on how to use `Mongoid::History`.
|
47
|
+
|
48
|
+
**Create a History Tracker**
|
49
|
+
|
50
|
+
Create a new class to track histories. All histories are stored in this tracker. The name of the class can be anything you like. The only requirement is that it includes `Mongoid::History::Tracker`
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
# app/models/history_tracker.rb
|
54
|
+
class HistoryTracker
|
55
|
+
include Mongoid::History::Tracker
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
**Set Tracker Class Name**
|
60
|
+
|
61
|
+
|
62
|
+
You should manually set the tracker class name to make sure your tracker can be found and loaded properly. You can skip this step if you manually require your tracker before using any trackables. If you don't know what I'm talking about, then you should just follow the example below.
|
63
|
+
|
64
|
+
Here is an example of setting the tracker class name using a rails initializer
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
# config/initializers/mongoid-history.rb
|
68
|
+
# initializer for mongoid-history
|
69
|
+
# assuming HistoryTracker is your tracker class
|
70
|
+
Mongoid::History.tracker_class_name = :history_tracker
|
71
|
+
```
|
72
|
+
|
73
|
+
**Set `#current_user` method name**
|
74
|
+
|
75
|
+
You can set name of method which returns currently logged in user if you don't want to set modifier explicitly on every update.
|
76
|
+
|
77
|
+
Here is an example of setting the current_user_method using a rails initializer
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
# config/initializers/mongoid-history.rb
|
81
|
+
# initializer for mongoid-history
|
82
|
+
# assuming you're using devise/authlogic
|
83
|
+
Mongoid::History.current_user_method = :current_user
|
84
|
+
```
|
85
|
+
|
86
|
+
When current_user_method is set mongoid-history call this method on each update and set it as modifier
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
# Assume that current_user return #<User _id: 1>
|
90
|
+
post = Post.first
|
91
|
+
post.update_attributes(:title => 'New title')
|
92
|
+
|
93
|
+
post.history_tracks.last.modifier #=> #<User _id: 1>
|
94
|
+
```
|
95
|
+
|
96
|
+
***Create Trackable classes and objects***
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
class Post
|
100
|
+
include Mongoid::Document
|
101
|
+
include Mongoid::Timestamps
|
102
|
+
|
103
|
+
# History tracking all Post Documents
|
104
|
+
# Note: Tracking will not work until #track_history is invoked
|
105
|
+
include Mongoid::History::Trackable
|
106
|
+
|
107
|
+
field :title
|
108
|
+
field :body
|
109
|
+
field :rating
|
110
|
+
embeds_many :comments
|
111
|
+
|
112
|
+
# Telling Mongoid::History how you want to track
|
113
|
+
track_history :on => [:title, :body], # I want to track title and body fields only. Default is :all
|
114
|
+
:modifier_field => :modifier, # Adds "referened_in :modifier" to track who made the change. Default is :modifier
|
115
|
+
:version_field => :version, # Adds "field :version, :type => Integer" to track current version. Default is :version
|
116
|
+
:track_create => false, # Do you want to track document creation? Default is false
|
117
|
+
:track_update => true, # Do you want to track document updates? Default is true
|
118
|
+
:track_destroy => false, # Do you want to track document destruction? Default is false
|
119
|
+
end
|
120
|
+
|
121
|
+
class Comment
|
122
|
+
include Mongoid::Document
|
123
|
+
include Mongoid::Timestamps
|
124
|
+
|
125
|
+
# Declare that we want to track comments
|
126
|
+
include Mongoid::History::Trackable
|
127
|
+
|
128
|
+
field :title
|
129
|
+
field :body
|
130
|
+
embedded_in :post, :inverse_of => :comments
|
131
|
+
|
132
|
+
# Track title and body for all comments, scope it to post (the parent)
|
133
|
+
# Also track creation and destruction
|
134
|
+
track_history :on => [:title, :body], :scope => :post, :track_create => true, :track_destroy => true
|
135
|
+
end
|
136
|
+
|
137
|
+
# The modifier can be specified as well
|
138
|
+
class User
|
139
|
+
include Mongoid::Document
|
140
|
+
include Mongoid::Timestamps
|
141
|
+
|
142
|
+
field :name
|
143
|
+
end
|
144
|
+
|
145
|
+
user = User.create(:name => "Aaron")
|
146
|
+
post = Post.create(:title => "Test", :body => "Post", :modifier => user)
|
147
|
+
comment = post.comments.create(:title => "test", :body => "comment", :modifier => user)
|
148
|
+
comment.history_tracks.count # should be 1
|
149
|
+
|
150
|
+
comment.update_attributes(:title => "Test 2")
|
151
|
+
comment.history_tracks.count # should be 2
|
152
|
+
|
153
|
+
track = comment.history_tracks.last
|
154
|
+
|
155
|
+
track.undo! user # comment title should be "Test"
|
156
|
+
|
157
|
+
track.redo! user # comment title should be "Test 2"
|
158
|
+
|
159
|
+
# undo last change
|
160
|
+
comment.undo! user
|
161
|
+
|
162
|
+
# undo versions 1 - 4
|
163
|
+
comment.undo! user, :from => 4, :to => 1
|
164
|
+
|
165
|
+
# undo last 3 versions
|
166
|
+
comment.undo! user, :last => 3
|
167
|
+
|
168
|
+
# redo versions 1 - 4
|
169
|
+
comment.redo! user, :from => 1, :to => 4
|
170
|
+
|
171
|
+
# redo last 3 versions
|
172
|
+
comment.redo! user, :last => 3
|
173
|
+
|
174
|
+
# delete post
|
175
|
+
post.destroy
|
176
|
+
|
177
|
+
# undelete post
|
178
|
+
post.undo! user
|
179
|
+
|
180
|
+
# disable tracking for comments within a block
|
181
|
+
Comment.disable_tracking do
|
182
|
+
comment.update_attributes(:title => "Test 3")
|
183
|
+
end
|
184
|
+
```
|
185
|
+
|
186
|
+
Contributing to mongoid-history
|
187
|
+
-------------------------------
|
188
|
+
|
189
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
190
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
191
|
+
* Fork the project
|
192
|
+
* Start a feature/bugfix branch
|
193
|
+
* Commit and push until you are happy with your contribution
|
194
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
195
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
196
|
+
|
197
|
+
Copyright
|
198
|
+
---------
|
199
|
+
|
200
|
+
Copyright (c) 2011 Aaron Qian. See LICENSE.txt for
|
201
|
+
further details.
|
202
|
+
|
data/Rakefile
CHANGED
@@ -31,25 +31,6 @@ RSpec::Core::RakeTask.new(:spec) do |spec|
|
|
31
31
|
spec.rspec_opts = "--color --format progress"
|
32
32
|
end
|
33
33
|
|
34
|
-
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
35
|
-
spec.pattern = 'spec/**/*_spec.rb'
|
36
|
-
spec.rcov = true
|
37
|
-
spec.rcov_opts = "--exclude ~\/.rvm,spec"
|
38
|
-
end
|
39
|
-
|
40
|
-
require 'reek/rake/task'
|
41
|
-
Reek::Rake::Task.new do |t|
|
42
|
-
t.fail_on_error = true
|
43
|
-
t.verbose = false
|
44
|
-
t.source_files = 'lib/**/*.rb'
|
45
|
-
end
|
46
|
-
|
47
|
-
require 'roodi'
|
48
|
-
require 'roodi_task'
|
49
|
-
RoodiTask.new do |t|
|
50
|
-
t.verbose = false
|
51
|
-
end
|
52
|
-
|
53
34
|
task :default => :spec
|
54
35
|
|
55
36
|
require 'yard'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1
|
1
|
+
0.2.1
|
@@ -1,28 +1,44 @@
|
|
1
1
|
module Mongoid::History
|
2
2
|
class Sweeper < Mongoid::Observer
|
3
|
-
|
3
|
+
def controller
|
4
|
+
Thread.current[:mongoid_history_sweeper_controller]
|
5
|
+
end
|
6
|
+
|
7
|
+
def controller=(value)
|
8
|
+
Thread.current[:mongoid_history_sweeper_controller] = value
|
9
|
+
end
|
4
10
|
|
5
11
|
def self.observed_classes
|
6
12
|
[Mongoid::History.tracker_class]
|
7
13
|
end
|
8
14
|
|
15
|
+
# Hook to ActionController::Base#around_filter.
|
16
|
+
# Runs before a controller action is run.
|
17
|
+
# It should always return true so controller actions
|
18
|
+
# can continue.
|
9
19
|
def before(controller)
|
10
20
|
self.controller = controller
|
11
|
-
true
|
21
|
+
true
|
12
22
|
end
|
13
23
|
|
24
|
+
# Hook to ActionController::Base#around_filter.
|
25
|
+
# Runs after a controller action is run.
|
26
|
+
# Clean up so that the controller can
|
27
|
+
# be collected after this request
|
14
28
|
def after(controller)
|
15
|
-
self.controller = controller
|
16
|
-
# Clean up, so that the controller can be collected after this request
|
17
29
|
self.controller = nil
|
18
30
|
end
|
19
31
|
|
20
32
|
def before_create(track)
|
21
|
-
track.
|
33
|
+
modifier_field = track.trackable.history_trackable_options[:modifier_field]
|
34
|
+
modifier = track.send modifier_field
|
35
|
+
track.send "#{modifier_field}=", current_user unless modifier
|
22
36
|
end
|
23
37
|
|
24
38
|
def current_user
|
25
|
-
|
39
|
+
if controller.respond_to?(Mongoid::History.current_user_method, true)
|
40
|
+
controller.send Mongoid::History.current_user_method
|
41
|
+
end
|
26
42
|
end
|
27
43
|
end
|
28
44
|
end
|
@@ -36,7 +36,7 @@ module Mongoid::History
|
|
36
36
|
field options[:version_field].to_sym, :type => Integer
|
37
37
|
referenced_in options[:modifier_field].to_sym, :class_name => Mongoid::History.modifier_class_name
|
38
38
|
|
39
|
-
include
|
39
|
+
include MyInstanceMethods
|
40
40
|
extend SingletonMethods
|
41
41
|
|
42
42
|
delegate :history_trackable_options, :to => 'self.class'
|
@@ -69,11 +69,11 @@ module Mongoid::History
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
module
|
72
|
+
module MyInstanceMethods
|
73
73
|
def history_tracks
|
74
|
-
@history_tracks ||= Mongoid::History.tracker_class.where(:scope => history_trackable_options[:scope], :association_chain =>
|
74
|
+
@history_tracks ||= Mongoid::History.tracker_class.where(:scope => history_trackable_options[:scope], :association_chain => association_hash)
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
# undo :from => 1, :to => 5
|
78
78
|
# undo 4
|
79
79
|
# undo :last => 10
|
@@ -128,10 +128,27 @@ module Mongoid::History
|
|
128
128
|
|
129
129
|
def traverse_association_chain(node=self)
|
130
130
|
list = node._parent ? traverse_association_chain(node._parent) : []
|
131
|
-
list <<
|
131
|
+
list << association_hash(node)
|
132
132
|
list
|
133
133
|
end
|
134
134
|
|
135
|
+
def association_hash(node=self)
|
136
|
+
# get all reflections of embedded_in association metadata
|
137
|
+
# and find the first association that matches _parent.
|
138
|
+
if node._parent
|
139
|
+
meta = node.reflect_on_all_associations(:embedded_in).find do |meta|
|
140
|
+
node._parent == node.send(meta.key)
|
141
|
+
end
|
142
|
+
|
143
|
+
inverse = node._parent.reflect_on_association(meta.inverse)
|
144
|
+
end
|
145
|
+
|
146
|
+
# if root node has no meta, and should use class name instead
|
147
|
+
name = meta ? meta.inverse.to_s : node.class.name
|
148
|
+
|
149
|
+
{ 'name' => name, 'id' => node.id}
|
150
|
+
end
|
151
|
+
|
135
152
|
def modified_attributes_for_update
|
136
153
|
@modified_attributes_for_update ||= if history_trackable_options[:on] == :all
|
137
154
|
changes.reject do |k, v|
|
@@ -187,7 +204,7 @@ module Mongoid::History
|
|
187
204
|
return unless should_track_update?
|
188
205
|
current_version = (self.send(history_trackable_options[:version_field]) || 0 ) + 1
|
189
206
|
self.send("#{history_trackable_options[:version_field]}=", current_version)
|
190
|
-
Mongoid::History.tracker_class.create!(history_tracker_attributes(:update).merge(:version => current_version, :action => "update"))
|
207
|
+
Mongoid::History.tracker_class.create!(history_tracker_attributes(:update).merge(:version => current_version, :action => "update", :trackable => self))
|
191
208
|
clear_memoization
|
192
209
|
end
|
193
210
|
|
@@ -195,14 +212,14 @@ module Mongoid::History
|
|
195
212
|
return unless track_history?
|
196
213
|
current_version = (self.send(history_trackable_options[:version_field]) || 0 ) + 1
|
197
214
|
self.send("#{history_trackable_options[:version_field]}=", current_version)
|
198
|
-
Mongoid::History.tracker_class.create!(history_tracker_attributes(:create).merge(:version => current_version, :action => "create"))
|
215
|
+
Mongoid::History.tracker_class.create!(history_tracker_attributes(:create).merge(:version => current_version, :action => "create", :trackable => self))
|
199
216
|
clear_memoization
|
200
217
|
end
|
201
218
|
|
202
219
|
def track_destroy
|
203
220
|
return unless track_history?
|
204
221
|
current_version = (self.send(history_trackable_options[:version_field]) || 0 ) + 1
|
205
|
-
Mongoid::History.tracker_class.create!(history_tracker_attributes(:destroy).merge(:version => current_version, :action => "destroy"))
|
222
|
+
Mongoid::History.tracker_class.create!(history_tracker_attributes(:destroy).merge(:version => current_version, :action => "destroy", :trackable => self))
|
206
223
|
clear_memoization
|
207
224
|
end
|
208
225
|
|
@@ -5,6 +5,7 @@ module Mongoid::History
|
|
5
5
|
included do
|
6
6
|
include Mongoid::Document
|
7
7
|
include Mongoid::Timestamps
|
8
|
+
attr_writer :trackable
|
8
9
|
|
9
10
|
field :association_chain, :type => Array, :default => []
|
10
11
|
field :modified, :type => Hash
|
@@ -25,9 +26,9 @@ module Mongoid::History
|
|
25
26
|
|
26
27
|
def undo!(modifier)
|
27
28
|
if action.to_sym == :destroy
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
re_create
|
30
|
+
elsif action.to_sym == :create
|
31
|
+
re_destroy
|
31
32
|
else
|
32
33
|
trackable.update_attributes!(undo_attr(modifier))
|
33
34
|
end
|
@@ -35,7 +36,9 @@ module Mongoid::History
|
|
35
36
|
|
36
37
|
def redo!(modifier)
|
37
38
|
if action.to_sym == :destroy
|
38
|
-
|
39
|
+
re_destroy
|
40
|
+
elsif action.to_sym == :create
|
41
|
+
re_create
|
39
42
|
else
|
40
43
|
trackable.update_attributes!(redo_attr(modifier))
|
41
44
|
end
|
@@ -69,25 +72,80 @@ module Mongoid::History
|
|
69
72
|
@trackable_parents ||= trackable_parents_and_trackable[0, -1]
|
70
73
|
end
|
71
74
|
|
75
|
+
def trackable_parent
|
76
|
+
@trackable_parent ||= trackable_parents_and_trackable[-2]
|
77
|
+
end
|
78
|
+
|
79
|
+
|
72
80
|
def affected
|
73
|
-
@affected ||= (modified.keys | original.keys).inject({}){ |h,k| h[k] =
|
81
|
+
@affected ||= (modified.keys | original.keys).inject({}){ |h,k| h[k] =
|
74
82
|
trackable ? trackable.attributes[k] : modified[k]; h}
|
75
83
|
end
|
76
84
|
|
77
85
|
private
|
86
|
+
|
87
|
+
def re_create
|
88
|
+
association_chain.length > 1 ? create_on_parent : create_standalone
|
89
|
+
end
|
90
|
+
|
91
|
+
def re_destroy
|
92
|
+
trackable.destroy
|
93
|
+
end
|
94
|
+
|
95
|
+
def create_standalone
|
96
|
+
class_name = association_chain.first["name"]
|
97
|
+
restored = class_name.constantize.new(modified)
|
98
|
+
restored.save!
|
99
|
+
end
|
100
|
+
|
101
|
+
def create_on_parent
|
102
|
+
name = association_chain.last["name"]
|
103
|
+
if embeds_one?(trackable_parent, name)
|
104
|
+
trackable_parent.send("create_#{name}!", modified)
|
105
|
+
elsif embeds_many?(trackable_parent, name)
|
106
|
+
trackable_parent.send(name).create!(modified)
|
107
|
+
else
|
108
|
+
raise "This should never happen. Please report bug!"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
78
112
|
def trackable_parents_and_trackable
|
79
113
|
@trackable_parents_and_trackable ||= traverse_association_chain
|
80
114
|
end
|
81
115
|
|
116
|
+
def relation_of(doc, name)
|
117
|
+
meta = doc.reflect_on_association(name)
|
118
|
+
meta ? meta.relation : nil
|
119
|
+
end
|
120
|
+
|
121
|
+
def embeds_one?(doc, name)
|
122
|
+
relation_of(doc, name) == Mongoid::Relations::Embedded::One
|
123
|
+
end
|
124
|
+
|
125
|
+
def embeds_many?(doc, name)
|
126
|
+
relation_of(doc, name) == Mongoid::Relations::Embedded::Many
|
127
|
+
end
|
128
|
+
|
82
129
|
def traverse_association_chain
|
83
130
|
chain = association_chain.dup
|
84
131
|
doc = nil
|
85
132
|
documents = []
|
133
|
+
|
86
134
|
begin
|
87
135
|
node = chain.shift
|
88
136
|
name = node['name']
|
89
|
-
|
90
|
-
doc
|
137
|
+
|
138
|
+
doc = if doc.nil?
|
139
|
+
# root association. First element of the association chain
|
140
|
+
klass = name.classify.constantize
|
141
|
+
klass.where(:_id => node['id']).first
|
142
|
+
elsif embeds_one?(doc, name)
|
143
|
+
doc.send(name)
|
144
|
+
elsif embeds_many?(doc, name)
|
145
|
+
doc.send(name).where(:_id => node['id']).first
|
146
|
+
else
|
147
|
+
raise "This should never happen. Please report bug."
|
148
|
+
end
|
91
149
|
documents << doc
|
92
150
|
end while( !chain.empty? )
|
93
151
|
documents
|
data/mongoid-history.gemspec
CHANGED
@@ -4,27 +4,25 @@
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version = "0.1
|
7
|
+
s.name = "mongoid-history"
|
8
|
+
s.version = "0.2.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = [
|
12
|
-
s.date =
|
13
|
-
s.description =
|
14
|
-
|
15
|
-
This plugin implements multi-user undo, which allows users to undo any history change in any order. Undoing a document also creates a new history track. This is great for auditing and preventing vandalism, but it is probably not suitable for use cases such as a wiki.}
|
16
|
-
s.email = [%q{aq1018@gmail.com}, %q{justin.mgrimes@gmail.com}]
|
11
|
+
s.authors = ["Aaron Qian", "Justin Grimes"]
|
12
|
+
s.date = "2012-03-19"
|
13
|
+
s.description = "In frustration of Mongoid::Versioning, I created this plugin for tracking historical changes for any document, including embedded ones. It achieves this by storing all history tracks in a single collection that you define. (See Usage for more details) Embedded documents are referenced by storing an association path, which is an array of document_name and document_id fields starting from the top most parent document and down to the embedded document that should track history.\n\n This plugin implements multi-user undo, which allows users to undo any history change in any order. Undoing a document also creates a new history track. This is great for auditing and preventing vandalism, but it is probably not suitable for use cases such as a wiki."
|
14
|
+
s.email = ["aq1018@gmail.com", "justin.mgrimes@gmail.com"]
|
17
15
|
s.extra_rdoc_files = [
|
18
16
|
"LICENSE.txt",
|
19
|
-
"README.
|
17
|
+
"README.md"
|
20
18
|
]
|
21
19
|
s.files = [
|
22
20
|
".document",
|
23
21
|
".rspec",
|
24
|
-
".
|
22
|
+
".travis.yml",
|
25
23
|
"Gemfile",
|
26
24
|
"LICENSE.txt",
|
27
|
-
"README.
|
25
|
+
"README.md",
|
28
26
|
"Rakefile",
|
29
27
|
"VERSION",
|
30
28
|
"lib/mongoid-history.rb",
|
@@ -38,57 +36,42 @@ Gem::Specification.new do |s|
|
|
38
36
|
"spec/trackable_spec.rb",
|
39
37
|
"spec/tracker_spec.rb"
|
40
38
|
]
|
41
|
-
s.homepage =
|
42
|
-
s.licenses = [
|
43
|
-
s.require_paths = [
|
44
|
-
s.rubygems_version =
|
45
|
-
s.summary =
|
46
|
-
s.test_files = [
|
47
|
-
"spec/integration/integration_spec.rb",
|
48
|
-
"spec/spec_helper.rb",
|
49
|
-
"spec/trackable_spec.rb",
|
50
|
-
"spec/tracker_spec.rb"
|
51
|
-
]
|
39
|
+
s.homepage = "http://github.com/aq1018/mongoid-history"
|
40
|
+
s.licenses = ["MIT"]
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
s.rubygems_version = "1.8.10"
|
43
|
+
s.summary = "history tracking, auditing, undo, redo for mongoid"
|
52
44
|
|
53
45
|
if s.respond_to? :specification_version then
|
54
46
|
s.specification_version = 3
|
55
47
|
|
56
48
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
57
49
|
s.add_runtime_dependency(%q<easy_diff>, [">= 0"])
|
58
|
-
s.add_runtime_dependency(%q<mongoid>, ["
|
59
|
-
s.add_development_dependency(%q<bson_ext>, [">=
|
60
|
-
s.add_development_dependency(%q<rspec>, ["
|
61
|
-
s.add_development_dependency(%q<yard>, ["
|
62
|
-
s.add_development_dependency(%q<bundler>, ["
|
63
|
-
s.add_development_dependency(%q<jeweler>, ["
|
64
|
-
s.add_development_dependency(%q<rcov>, [">= 0"])
|
65
|
-
s.add_development_dependency(%q<reek>, ["~> 1.2.8"])
|
66
|
-
s.add_development_dependency(%q<roodi>, ["~> 2.1.0"])
|
50
|
+
s.add_runtime_dependency(%q<mongoid>, [">= 2.0.0"])
|
51
|
+
s.add_development_dependency(%q<bson_ext>, [">= 0"])
|
52
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
53
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
54
|
+
s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
|
55
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
67
56
|
s.add_development_dependency(%q<database_cleaner>, [">= 0"])
|
68
57
|
else
|
69
58
|
s.add_dependency(%q<easy_diff>, [">= 0"])
|
70
|
-
s.add_dependency(%q<mongoid>, ["
|
71
|
-
s.add_dependency(%q<bson_ext>, [">=
|
72
|
-
s.add_dependency(%q<rspec>, ["
|
73
|
-
s.add_dependency(%q<yard>, ["
|
74
|
-
s.add_dependency(%q<bundler>, ["
|
75
|
-
s.add_dependency(%q<jeweler>, ["
|
76
|
-
s.add_dependency(%q<rcov>, [">= 0"])
|
77
|
-
s.add_dependency(%q<reek>, ["~> 1.2.8"])
|
78
|
-
s.add_dependency(%q<roodi>, ["~> 2.1.0"])
|
59
|
+
s.add_dependency(%q<mongoid>, [">= 2.0.0"])
|
60
|
+
s.add_dependency(%q<bson_ext>, [">= 0"])
|
61
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
62
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
63
|
+
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
64
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
79
65
|
s.add_dependency(%q<database_cleaner>, [">= 0"])
|
80
66
|
end
|
81
67
|
else
|
82
68
|
s.add_dependency(%q<easy_diff>, [">= 0"])
|
83
|
-
s.add_dependency(%q<mongoid>, ["
|
84
|
-
s.add_dependency(%q<bson_ext>, [">=
|
85
|
-
s.add_dependency(%q<rspec>, ["
|
86
|
-
s.add_dependency(%q<yard>, ["
|
87
|
-
s.add_dependency(%q<bundler>, ["
|
88
|
-
s.add_dependency(%q<jeweler>, ["
|
89
|
-
s.add_dependency(%q<rcov>, [">= 0"])
|
90
|
-
s.add_dependency(%q<reek>, ["~> 1.2.8"])
|
91
|
-
s.add_dependency(%q<roodi>, ["~> 2.1.0"])
|
69
|
+
s.add_dependency(%q<mongoid>, [">= 2.0.0"])
|
70
|
+
s.add_dependency(%q<bson_ext>, [">= 0"])
|
71
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
72
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
73
|
+
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
74
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
92
75
|
s.add_dependency(%q<database_cleaner>, [">= 0"])
|
93
76
|
end
|
94
77
|
end
|
@@ -16,6 +16,7 @@ describe Mongoid::History do
|
|
16
16
|
field :rating
|
17
17
|
|
18
18
|
embeds_many :comments
|
19
|
+
embeds_one :section
|
19
20
|
track_history :on => [:title, :body], :track_destroy => true
|
20
21
|
end
|
21
22
|
|
@@ -26,8 +27,18 @@ describe Mongoid::History do
|
|
26
27
|
|
27
28
|
field :title
|
28
29
|
field :body
|
29
|
-
embedded_in :post
|
30
|
-
track_history :on => [:title, :body], :scope => :post, :track_create => true
|
30
|
+
embedded_in :post
|
31
|
+
track_history :on => [:title, :body], :scope => :post, :track_create => true, :track_destroy => true
|
32
|
+
end
|
33
|
+
|
34
|
+
class Section
|
35
|
+
include Mongoid::Document
|
36
|
+
include Mongoid::Timestamps
|
37
|
+
include Mongoid::History::Trackable
|
38
|
+
|
39
|
+
field :title
|
40
|
+
embedded_in :post
|
41
|
+
track_history :on => [:title], :scope => :post, :track_create => true, :track_destroy => true
|
31
42
|
end
|
32
43
|
|
33
44
|
class User
|
@@ -79,7 +90,11 @@ describe Mongoid::History do
|
|
79
90
|
end
|
80
91
|
|
81
92
|
it "should assign association_chain" do
|
82
|
-
|
93
|
+
expected = [
|
94
|
+
{'id' => @post.id, 'name' => "Post"},
|
95
|
+
{'id' => @comment.id, 'name' => "comments"}
|
96
|
+
]
|
97
|
+
@comment.history_tracks.first.association_chain.should == expected
|
83
98
|
end
|
84
99
|
end
|
85
100
|
|
@@ -117,19 +132,19 @@ describe Mongoid::History do
|
|
117
132
|
|
118
133
|
it "should assign modified fields" do
|
119
134
|
@post.update_attributes(:title => "Another Test")
|
120
|
-
@post.history_tracks.
|
135
|
+
@post.history_tracks.last.modified.should == {
|
121
136
|
"title" => "Another Test"
|
122
137
|
}
|
123
138
|
end
|
124
139
|
|
125
140
|
it "should assign method field" do
|
126
141
|
@post.update_attributes(:title => "Another Test")
|
127
|
-
@post.history_tracks.
|
142
|
+
@post.history_tracks.last.action.should == "update"
|
128
143
|
end
|
129
144
|
|
130
145
|
it "should assign original fields" do
|
131
146
|
@post.update_attributes(:title => "Another Test")
|
132
|
-
@post.history_tracks.
|
147
|
+
@post.history_tracks.last.original.should == {
|
133
148
|
"title" => "Test"
|
134
149
|
}
|
135
150
|
end
|
@@ -156,7 +171,7 @@ describe Mongoid::History do
|
|
156
171
|
|
157
172
|
it "should assign association_chain" do
|
158
173
|
@post.update_attributes(:title => "Another Test")
|
159
|
-
@post.history_tracks.
|
174
|
+
@post.history_tracks.last.association_chain.should == [{'id' => @post.id, 'name' => "Post"}]
|
160
175
|
end
|
161
176
|
|
162
177
|
it "should exclude defined options" do
|
@@ -206,11 +221,11 @@ describe Mongoid::History do
|
|
206
221
|
|
207
222
|
it "should assign modifier" do
|
208
223
|
@post.update_attributes(:title => "Another Test", :modifier => @another_user)
|
209
|
-
@post.history_tracks.
|
224
|
+
@post.history_tracks.last.modifier.should == @another_user
|
210
225
|
end
|
211
226
|
end
|
212
227
|
|
213
|
-
describe "on update embedded" do
|
228
|
+
describe "on update embedded 1..N (embeds_many)" do
|
214
229
|
it "should assign version on comment" do
|
215
230
|
@comment.update_attributes(:title => "Test2")
|
216
231
|
@comment.version.should == 2 # first track generated on creation
|
@@ -235,43 +250,133 @@ describe Mongoid::History do
|
|
235
250
|
}
|
236
251
|
end
|
237
252
|
|
253
|
+
it "should be possible to undo from parent" do
|
254
|
+
@comment.update_attributes(:title => "Test 2")
|
255
|
+
@post.history_tracks.last.undo!(@user)
|
256
|
+
@comment.reload
|
257
|
+
@comment.title.should == "test"
|
258
|
+
end
|
259
|
+
|
238
260
|
it "should assign modifier" do
|
239
261
|
@post.update_attributes(:title => "Another Test", :modifier => @another_user)
|
240
|
-
@post.history_tracks.
|
262
|
+
@post.history_tracks.last.modifier.should == @another_user
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
describe "on update embedded 1..1 (embeds_one)" do
|
267
|
+
before(:each) do
|
268
|
+
@section = Section.new(:title => 'Technology')
|
269
|
+
@post.section = @section
|
270
|
+
@post.save!
|
271
|
+
@post.reload
|
272
|
+
@section = @post.section
|
273
|
+
end
|
274
|
+
|
275
|
+
it "should assign version on create section" do
|
276
|
+
@section.version.should == 1
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should assign version on section" do
|
280
|
+
@section.update_attributes(:title => 'Technology 2')
|
281
|
+
@section.version.should == 2 # first track generated on creation
|
282
|
+
end
|
283
|
+
|
284
|
+
it "should create a history track of version 2" do
|
285
|
+
@section.update_attributes(:title => 'Technology 2')
|
286
|
+
@section.history_tracks.where(:version => 2).first.should_not be_nil
|
287
|
+
end
|
288
|
+
|
289
|
+
it "should assign modified fields" do
|
290
|
+
@section.update_attributes(:title => 'Technology 2')
|
291
|
+
@section.history_tracks.where(:version => 2).first.modified.should == {
|
292
|
+
"title" => "Technology 2"
|
293
|
+
}
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should assign original fields" do
|
297
|
+
@section.update_attributes(:title => 'Technology 2')
|
298
|
+
@section.history_tracks.where(:version => 2).first.original.should == {
|
299
|
+
"title" => "Technology"
|
300
|
+
}
|
301
|
+
end
|
302
|
+
|
303
|
+
it "should be possible to undo from parent" do
|
304
|
+
@section.update_attributes(:title => 'Technology 2')
|
305
|
+
@post.history_tracks.last.undo!(@user)
|
306
|
+
@section.reload
|
307
|
+
@section.title.should == "Technology"
|
308
|
+
end
|
309
|
+
|
310
|
+
it "should assign modifier" do
|
311
|
+
@section.update_attributes(:title => "Business", :modifier => @another_user)
|
312
|
+
@post.history_tracks.last.modifier.should == @another_user
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
describe "on destroy embedded" do
|
317
|
+
it "should be possible to re-create destroyed embedded" do
|
318
|
+
@comment.destroy
|
319
|
+
@comment.history_tracks.last.undo!(@user)
|
320
|
+
@post.reload
|
321
|
+
@post.comments.first.title.should == "test"
|
322
|
+
end
|
323
|
+
|
324
|
+
it "should be possible to re-create destroyed embedded from parent" do
|
325
|
+
@comment.destroy
|
326
|
+
@post.history_tracks.last.undo!(@user)
|
327
|
+
@post.reload
|
328
|
+
@post.comments.first.title.should == "test"
|
329
|
+
end
|
330
|
+
|
331
|
+
it "should be possible to destroy after re-create embedded from parent" do
|
332
|
+
@comment.destroy
|
333
|
+
@post.history_tracks.last.undo!(@user)
|
334
|
+
@post.history_tracks.last.undo!(@user)
|
335
|
+
@post.reload
|
336
|
+
@post.comments.count.should == 0
|
337
|
+
end
|
338
|
+
|
339
|
+
it "should be possible to create with redo after undo create embedded from parent" do
|
340
|
+
@post.comments.create!(:title => "The second one")
|
341
|
+
@track = @post.history_tracks.last
|
342
|
+
@track.undo!(@user)
|
343
|
+
@track.redo!(@user)
|
344
|
+
@post.reload
|
345
|
+
@post.comments.count.should == 2
|
241
346
|
end
|
242
347
|
end
|
243
348
|
|
244
349
|
describe "non-embedded" do
|
245
350
|
it "should undo changes" do
|
246
351
|
@post.update_attributes(:title => "Test2")
|
247
|
-
@post.history_tracks.where(:version => 1).
|
352
|
+
@post.history_tracks.where(:version => 1).last.undo!(@user)
|
248
353
|
@post.reload
|
249
354
|
@post.title.should == "Test"
|
250
355
|
end
|
251
356
|
|
252
357
|
it "should undo destruction" do
|
253
358
|
@post.destroy
|
254
|
-
@post.history_tracks.where(:version => 1).
|
359
|
+
@post.history_tracks.where(:version => 1).last.undo!(@user)
|
255
360
|
Post.find(@post.id).title.should == "Test"
|
256
361
|
end
|
257
362
|
|
258
363
|
it "should create a new history track after undo" do
|
259
364
|
@post.update_attributes(:title => "Test2")
|
260
|
-
@post.history_tracks.
|
365
|
+
@post.history_tracks.last.undo!(@user)
|
261
366
|
@post.reload
|
262
|
-
@post.history_tracks.count.should ==
|
367
|
+
@post.history_tracks.count.should == 3
|
263
368
|
end
|
264
369
|
|
265
370
|
it "should assign @user as the modifier of the newly created history track" do
|
266
371
|
@post.update_attributes(:title => "Test2")
|
267
|
-
@post.history_tracks.where(:version => 1).
|
372
|
+
@post.history_tracks.where(:version => 1).last.undo!(@user)
|
268
373
|
@post.reload
|
269
|
-
@post.history_tracks.where(:version => 2).
|
374
|
+
@post.history_tracks.where(:version => 2).last.modifier.should == @user
|
270
375
|
end
|
271
376
|
|
272
377
|
it "should stay the same after undo and redo" do
|
273
378
|
@post.update_attributes(:title => "Test2")
|
274
|
-
@track = @post.history_tracks.
|
379
|
+
@track = @post.history_tracks.last
|
275
380
|
@track.undo!(@user)
|
276
381
|
@track.redo!(@user)
|
277
382
|
@post2 = Post.where(:_id => @post.id).first
|
@@ -281,7 +386,7 @@ describe Mongoid::History do
|
|
281
386
|
|
282
387
|
it "should be destroyed after undo and redo" do
|
283
388
|
@post.destroy
|
284
|
-
@track = @post.history_tracks.where(:version => 1).
|
389
|
+
@track = @post.history_tracks.where(:version => 1).last
|
285
390
|
@track.undo!(@user)
|
286
391
|
@track.redo!(@user)
|
287
392
|
Post.where(:_id => @post.id).first.should == nil
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid-history
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2012-03-19 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: easy_diff
|
17
|
-
requirement: &
|
17
|
+
requirement: &10925040 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,76 +22,65 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *10925040
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: mongoid
|
28
|
-
requirement: &
|
28
|
+
requirement: &10924060 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ! '>='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 2.0.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *10924060
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: bson_ext
|
39
|
-
requirement: &
|
39
|
+
requirement: &10923100 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ! '>='
|
43
43
|
- !ruby/object:Gem::Version
|
44
|
-
version:
|
44
|
+
version: '0'
|
45
45
|
type: :development
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *10923100
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: rspec
|
50
|
-
requirement: &
|
50
|
+
requirement: &10922320 !ruby/object:Gem::Requirement
|
51
51
|
none: false
|
52
52
|
requirements:
|
53
|
-
- -
|
53
|
+
- - ! '>='
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version:
|
55
|
+
version: '0'
|
56
56
|
type: :development
|
57
57
|
prerelease: false
|
58
|
-
version_requirements: *
|
58
|
+
version_requirements: *10922320
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
60
|
name: yard
|
61
|
-
requirement: &
|
61
|
+
requirement: &10921560 !ruby/object:Gem::Requirement
|
62
62
|
none: false
|
63
63
|
requirements:
|
64
|
-
- -
|
64
|
+
- - ! '>='
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
version: 0
|
66
|
+
version: '0'
|
67
67
|
type: :development
|
68
68
|
prerelease: false
|
69
|
-
version_requirements: *
|
69
|
+
version_requirements: *10921560
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: bundler
|
72
|
-
requirement: &
|
72
|
+
requirement: &10920720 !ruby/object:Gem::Requirement
|
73
73
|
none: false
|
74
74
|
requirements:
|
75
|
-
- -
|
75
|
+
- - ! '>='
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: 1.0.0
|
78
78
|
type: :development
|
79
79
|
prerelease: false
|
80
|
-
version_requirements: *
|
80
|
+
version_requirements: *10920720
|
81
81
|
- !ruby/object:Gem::Dependency
|
82
82
|
name: jeweler
|
83
|
-
requirement: &
|
84
|
-
none: false
|
85
|
-
requirements:
|
86
|
-
- - ~>
|
87
|
-
- !ruby/object:Gem::Version
|
88
|
-
version: 1.5.2
|
89
|
-
type: :development
|
90
|
-
prerelease: false
|
91
|
-
version_requirements: *19707120
|
92
|
-
- !ruby/object:Gem::Dependency
|
93
|
-
name: rcov
|
94
|
-
requirement: &19706560 !ruby/object:Gem::Requirement
|
83
|
+
requirement: &10935520 !ruby/object:Gem::Requirement
|
95
84
|
none: false
|
96
85
|
requirements:
|
97
86
|
- - ! '>='
|
@@ -99,32 +88,10 @@ dependencies:
|
|
99
88
|
version: '0'
|
100
89
|
type: :development
|
101
90
|
prerelease: false
|
102
|
-
version_requirements: *
|
103
|
-
- !ruby/object:Gem::Dependency
|
104
|
-
name: reek
|
105
|
-
requirement: &19705940 !ruby/object:Gem::Requirement
|
106
|
-
none: false
|
107
|
-
requirements:
|
108
|
-
- - ~>
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: 1.2.8
|
111
|
-
type: :development
|
112
|
-
prerelease: false
|
113
|
-
version_requirements: *19705940
|
114
|
-
- !ruby/object:Gem::Dependency
|
115
|
-
name: roodi
|
116
|
-
requirement: &19705280 !ruby/object:Gem::Requirement
|
117
|
-
none: false
|
118
|
-
requirements:
|
119
|
-
- - ~>
|
120
|
-
- !ruby/object:Gem::Version
|
121
|
-
version: 2.1.0
|
122
|
-
type: :development
|
123
|
-
prerelease: false
|
124
|
-
version_requirements: *19705280
|
91
|
+
version_requirements: *10935520
|
125
92
|
- !ruby/object:Gem::Dependency
|
126
93
|
name: database_cleaner
|
127
|
-
requirement: &
|
94
|
+
requirement: &10934420 !ruby/object:Gem::Requirement
|
128
95
|
none: false
|
129
96
|
requirements:
|
130
97
|
- - ! '>='
|
@@ -132,7 +99,7 @@ dependencies:
|
|
132
99
|
version: '0'
|
133
100
|
type: :development
|
134
101
|
prerelease: false
|
135
|
-
version_requirements: *
|
102
|
+
version_requirements: *10934420
|
136
103
|
description: ! "In frustration of Mongoid::Versioning, I created this plugin for tracking
|
137
104
|
historical changes for any document, including embedded ones. It achieves this by
|
138
105
|
storing all history tracks in a single collection that you define. (See Usage for
|
@@ -150,14 +117,14 @@ executables: []
|
|
150
117
|
extensions: []
|
151
118
|
extra_rdoc_files:
|
152
119
|
- LICENSE.txt
|
153
|
-
- README.
|
120
|
+
- README.md
|
154
121
|
files:
|
155
122
|
- .document
|
156
123
|
- .rspec
|
157
|
-
- .
|
124
|
+
- .travis.yml
|
158
125
|
- Gemfile
|
159
126
|
- LICENSE.txt
|
160
|
-
- README.
|
127
|
+
- README.md
|
161
128
|
- Rakefile
|
162
129
|
- VERSION
|
163
130
|
- lib/mongoid-history.rb
|
@@ -185,7 +152,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
185
152
|
version: '0'
|
186
153
|
segments:
|
187
154
|
- 0
|
188
|
-
hash: -
|
155
|
+
hash: -4147881344335982311
|
189
156
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
190
157
|
none: false
|
191
158
|
requirements:
|
@@ -194,12 +161,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
194
161
|
version: '0'
|
195
162
|
requirements: []
|
196
163
|
rubyforge_project:
|
197
|
-
rubygems_version: 1.8.
|
164
|
+
rubygems_version: 1.8.10
|
198
165
|
signing_key:
|
199
166
|
specification_version: 3
|
200
167
|
summary: history tracking, auditing, undo, redo for mongoid
|
201
|
-
test_files:
|
202
|
-
- spec/integration/integration_spec.rb
|
203
|
-
- spec/spec_helper.rb
|
204
|
-
- spec/trackable_spec.rb
|
205
|
-
- spec/tracker_spec.rb
|
168
|
+
test_files: []
|
data/.rvmrc
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
rvm 1.9.2@
|
data/README.rdoc
DELETED
@@ -1,144 +0,0 @@
|
|
1
|
-
= mongoid-history
|
2
|
-
|
3
|
-
In frustration of Mongoid::Versioning, I created this plugin for tracking historical changes for any document, including embedded ones. It achieves this by storing all history tracks in a single collection that you define. (See Usage for more details) Embedded documents are referenced by storing an association path, which is an array of document_name and document_id fields starting from the top most parent document and down to the embedded document that should track history.
|
4
|
-
|
5
|
-
This plugin implements multi-user undo, which allows users to undo any history change in any order. Undoing a document also creates a new history track. This is great for auditing and preventing vandalism, but it is probably not suitable for use cases such as a wiki.
|
6
|
-
|
7
|
-
== Install
|
8
|
-
|
9
|
-
gem install mongoid-history
|
10
|
-
|
11
|
-
== Rails 3
|
12
|
-
|
13
|
-
In your Gemfile:
|
14
|
-
|
15
|
-
gem 'mongoid-history'
|
16
|
-
|
17
|
-
== Usage
|
18
|
-
|
19
|
-
Here is a quick example on how to use this plugin. For more details, please look at spec/integration/integration_spec.rb. It offers more detailed examples on how to use Mongoid::History.
|
20
|
-
|
21
|
-
=== Create a History Tracker
|
22
|
-
|
23
|
-
Create a new class to track histories. All histories are stored in this tracker. The name of the class can be anything you like. The only requirement is that it includes `Mongoid::History::Tracker`
|
24
|
-
|
25
|
-
# app/models/history_tracker.rb
|
26
|
-
class HistoryTracker
|
27
|
-
include Mongoid::History::Tracker
|
28
|
-
end
|
29
|
-
|
30
|
-
=== Set Tracker Class Name
|
31
|
-
|
32
|
-
You should manually set the tracker class name to make sure your tracker can be found and loaded properly. You can skip this step if you manually require your tracker before using any trackables. If you don't know what I'm talking about, then you should just follow the example below.
|
33
|
-
|
34
|
-
Here is an example of setting the tracker class name using a rails initializer
|
35
|
-
|
36
|
-
# config/initializers/mongoid-history.rb
|
37
|
-
# initializer for mongoid-history
|
38
|
-
# assuming HistoryTracker is your tracker class
|
39
|
-
Mongoid::History.tracker_class_name = :history_tracker
|
40
|
-
|
41
|
-
|
42
|
-
=== Create Trackable classes and objects
|
43
|
-
|
44
|
-
class Post
|
45
|
-
include Mongoid::Document
|
46
|
-
include Mongoid::Timestamps
|
47
|
-
|
48
|
-
# History tracking all Post Documents
|
49
|
-
# Note: Tracking will not work until #track_history is invoked
|
50
|
-
include Mongoid::History::Trackable
|
51
|
-
|
52
|
-
field :title
|
53
|
-
field :body
|
54
|
-
field :rating
|
55
|
-
embeds_many :comments
|
56
|
-
|
57
|
-
# Telling Mongoid::History how you want to track
|
58
|
-
track_history :on => [:title, :body], # I want to track title and body fields only. Default is :all
|
59
|
-
:modifier_field => :modifier, # Adds "referened_in :modifier" to track who made the change. Default is :modifier
|
60
|
-
:version_field => :version, # Adds "field :version, :type => Integer" to track current version. Default is :version
|
61
|
-
:track_create => false, # Do you want to track document creation? Default is false
|
62
|
-
:track_update => true, # Do you want to track document updates? Default is true
|
63
|
-
:track_destroy => false, # Do you want to track document destruction? Default is false
|
64
|
-
end
|
65
|
-
|
66
|
-
class Comment
|
67
|
-
include Mongoid::Document
|
68
|
-
include Mongoid::Timestamps
|
69
|
-
|
70
|
-
# Declare that we want to track comments
|
71
|
-
include Mongoid::History::Trackable
|
72
|
-
|
73
|
-
field :title
|
74
|
-
field :body
|
75
|
-
embedded_in :post, :inverse_of => :comments
|
76
|
-
|
77
|
-
# Track title and body for all comments, scope it to post (the parent)
|
78
|
-
# Also track creation and destruction
|
79
|
-
track_history :on => [:title, :body], :scope => :post, :track_create => true, :track_destroy => true
|
80
|
-
end
|
81
|
-
|
82
|
-
# The modifier can be specified as well
|
83
|
-
class User
|
84
|
-
include Mongoid::Document
|
85
|
-
include Mongoid::Timestamps
|
86
|
-
|
87
|
-
field :name
|
88
|
-
end
|
89
|
-
|
90
|
-
user = User.create(:name => "Aaron")
|
91
|
-
post = Post.create(:title => "Test", :body => "Post", :modifier => user)
|
92
|
-
comment = post.comments.create(:title => "test", :body => "comment", :modifier => user)
|
93
|
-
comment.history_tracks.count # should be 1
|
94
|
-
|
95
|
-
comment.update_attributes(:title => "Test 2")
|
96
|
-
comment.history_tracks.count # should be 2
|
97
|
-
|
98
|
-
track = comment.history_tracks.last
|
99
|
-
|
100
|
-
track.undo! user # comment title should be "Test"
|
101
|
-
|
102
|
-
track.redo! user # comment title should be "Test 2"
|
103
|
-
|
104
|
-
# undo last change
|
105
|
-
comment.undo! user
|
106
|
-
|
107
|
-
# undo versions 1 - 4
|
108
|
-
comment.undo! user, :from => 4, :to => 1
|
109
|
-
|
110
|
-
# undo last 3 versions
|
111
|
-
comment.undo! user, :last => 3
|
112
|
-
|
113
|
-
# redo versions 1 - 4
|
114
|
-
comment.redo! user, :from => 1, :to => 4
|
115
|
-
|
116
|
-
# redo last 3 versions
|
117
|
-
comment.redo! user, :last => 3
|
118
|
-
|
119
|
-
# delete post
|
120
|
-
post.destroy
|
121
|
-
|
122
|
-
# undelete post
|
123
|
-
post.undo! user
|
124
|
-
|
125
|
-
# disable tracking for comments within a block
|
126
|
-
Comment.disable_tracking do
|
127
|
-
comment.update_attributes(:title => "Test 3")
|
128
|
-
end
|
129
|
-
|
130
|
-
== Contributing to mongoid-history
|
131
|
-
|
132
|
-
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
133
|
-
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
134
|
-
* Fork the project
|
135
|
-
* Start a feature/bugfix branch
|
136
|
-
* Commit and push until you are happy with your contribution
|
137
|
-
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
138
|
-
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
139
|
-
|
140
|
-
== Copyright
|
141
|
-
|
142
|
-
Copyright (c) 2011 Aaron Qian. See LICENSE.txt for
|
143
|
-
further details.
|
144
|
-
|