mongoid_delorean 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +44 -0
- data/LICENSE.txt +22 -0
- data/README.md +102 -0
- data/Rakefile +9 -0
- data/lib/mongoid/delorean/history.rb +16 -0
- data/lib/mongoid/delorean/trackable.rb +121 -0
- data/lib/mongoid/delorean/version.rb +5 -0
- data/lib/mongoid_delorean.rb +11 -0
- data/mongoid_delorean.gemspec +22 -0
- data/spec/config.yml +6 -0
- data/spec/mongoid/delorean/trackable_spec.rb +273 -0
- data/spec/spec_helper.rb +17 -0
- metadata +78 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
mongoid_delorean (1.0.0)
|
5
|
+
mongoid (>= 3.0.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (3.2.8)
|
11
|
+
activesupport (= 3.2.8)
|
12
|
+
builder (~> 3.0.0)
|
13
|
+
activesupport (3.2.8)
|
14
|
+
i18n (~> 0.6)
|
15
|
+
multi_json (~> 1.0)
|
16
|
+
builder (3.0.0)
|
17
|
+
database_cleaner (0.8.0)
|
18
|
+
diff-lcs (1.1.3)
|
19
|
+
i18n (0.6.1)
|
20
|
+
mongoid (3.0.5)
|
21
|
+
activemodel (~> 3.1)
|
22
|
+
moped (~> 1.1)
|
23
|
+
origin (~> 1.0)
|
24
|
+
tzinfo (~> 0.3.22)
|
25
|
+
moped (1.2.1)
|
26
|
+
multi_json (1.3.6)
|
27
|
+
origin (1.0.8)
|
28
|
+
rspec (2.11.0)
|
29
|
+
rspec-core (~> 2.11.0)
|
30
|
+
rspec-expectations (~> 2.11.0)
|
31
|
+
rspec-mocks (~> 2.11.0)
|
32
|
+
rspec-core (2.11.1)
|
33
|
+
rspec-expectations (2.11.3)
|
34
|
+
diff-lcs (~> 1.1.3)
|
35
|
+
rspec-mocks (2.11.2)
|
36
|
+
tzinfo (0.3.33)
|
37
|
+
|
38
|
+
PLATFORMS
|
39
|
+
ruby
|
40
|
+
|
41
|
+
DEPENDENCIES
|
42
|
+
database_cleaner
|
43
|
+
mongoid_delorean!
|
44
|
+
rspec
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Mark Bates
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# Mongoid::Delorean
|
2
|
+
|
3
|
+
A simple Mongoid 3 versioning system that works with embedded documents.
|
4
|
+
|
5
|
+
Tracking document changes can be really important to a lot of systems, unfortunately all of the Mongoid versioning plugins either only work with Mongoid 2.x, don't handle embedded document changes, or worse, just don't work. <code>Mongoid::Delorean</code> solves those problems.
|
6
|
+
|
7
|
+
<code>Mongoid::Delorean</code> is a simple plugin that does just what it sets out to do. It stores each version of your document as you make changes and then allows you to revert to earlier versions of the document.
|
8
|
+
|
9
|
+
If this wasn't great already, <code>Mongoid::Delorean</code> will even track changes made to any embedded documents, or documents that those embedded documents may have, and so on.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem 'mongoid_delorean'
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install mongoid_delorean
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
Using Mongoid::Delorean is very simple. Just include the <code>Mongoid::Delorean::Trackable</code> module into the <code>Mongoid::Document</code> you want to track. There is no need to include it in embedded documents, the parent document will handle that for you.
|
28
|
+
|
29
|
+
Now, when you save, or update, the parent (or any of the embedded documents) a new version will be saved for you.
|
30
|
+
|
31
|
+
### Example
|
32
|
+
|
33
|
+
<pre><code>
|
34
|
+
class Article
|
35
|
+
include Mongoid::Document
|
36
|
+
include Mongoid::Timestamps
|
37
|
+
include Mongoid::Delorean::Trackable
|
38
|
+
|
39
|
+
field :name, type: String
|
40
|
+
field :summary, type: String
|
41
|
+
|
42
|
+
embeds_many :pages
|
43
|
+
end
|
44
|
+
|
45
|
+
class Page
|
46
|
+
include Mongoid::Document
|
47
|
+
include Mongoid::Timestamps
|
48
|
+
|
49
|
+
field :name, type: String
|
50
|
+
|
51
|
+
embedded_in :article, inverse_of: :pages
|
52
|
+
embeds_many :sections
|
53
|
+
end
|
54
|
+
|
55
|
+
class Section
|
56
|
+
include Mongoid::Document
|
57
|
+
include Mongoid::Timestamps
|
58
|
+
|
59
|
+
field :name, type: String
|
60
|
+
field :body, type: String
|
61
|
+
|
62
|
+
embedded_in :page, inverse_of: :sections
|
63
|
+
end
|
64
|
+
|
65
|
+
a = Article.create(name: "Article 1")
|
66
|
+
a.version # => 1
|
67
|
+
page = a.pages.create(name: "Page 1")
|
68
|
+
a.version # => 2
|
69
|
+
page.update_attributes(name: "The 1st Page")
|
70
|
+
a.version # => 3
|
71
|
+
a.revert! # revert to the last version
|
72
|
+
page.reload
|
73
|
+
page.name # => "Page 1"
|
74
|
+
a.version # => 4
|
75
|
+
page.sections.create(name: "Section 1")
|
76
|
+
a.version # => 5
|
77
|
+
a.revert!(2) # revert to version 2
|
78
|
+
page.reload
|
79
|
+
a.version # => 6
|
80
|
+
page.name # => "Page 1"
|
81
|
+
page.sections.size # => 0
|
82
|
+
a.versions.size # => 6
|
83
|
+
</code></pre>
|
84
|
+
|
85
|
+
If you don't want to track changes you can wrap it with <code>without_history_tracking</code>:
|
86
|
+
|
87
|
+
<pre><code>
|
88
|
+
a = Article.create(name: "Article 1")
|
89
|
+
a.without_history_tracking do
|
90
|
+
a.update_attributes(name: "The Article 1")
|
91
|
+
end
|
92
|
+
a.version # => 1
|
93
|
+
</code></pre>
|
94
|
+
|
95
|
+
## Contributing
|
96
|
+
|
97
|
+
1. Fork it
|
98
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
99
|
+
3. Write your tests
|
100
|
+
4. Commit your changes (`git commit -am 'Add some feature'`)
|
101
|
+
5. Push to the branch (`git push origin my-new-feature`)
|
102
|
+
6. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Delorean
|
3
|
+
class History
|
4
|
+
|
5
|
+
include Mongoid::Document
|
6
|
+
include Mongoid::Timestamps
|
7
|
+
|
8
|
+
field :original_class, type: String
|
9
|
+
field :original_class_id, type: String
|
10
|
+
field :version, type: Integer, default: 0
|
11
|
+
field :altered_attributes, type: Hash, default: {}
|
12
|
+
field :full_attributes, type: Hash, default: {}
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Delorean
|
3
|
+
module Trackable
|
4
|
+
|
5
|
+
def self.included(klass)
|
6
|
+
super
|
7
|
+
klass.field :version, type: Integer, default: 0
|
8
|
+
klass.before_save :save_version
|
9
|
+
end
|
10
|
+
|
11
|
+
def versions
|
12
|
+
Mongoid::Delorean::History.where(original_class: self.class.name, original_class_id: self.id)
|
13
|
+
end
|
14
|
+
|
15
|
+
def save_version
|
16
|
+
if self.track_history?
|
17
|
+
unless self.kind_of?(Mongoid::Delorean::Trackable::CommonInstanceMethods)
|
18
|
+
extend Mongoid::Delorean::Trackable::CommonInstanceMethods
|
19
|
+
end
|
20
|
+
last_version = self.versions.last
|
21
|
+
_version = last_version ? last_version.version + 1 : 1
|
22
|
+
|
23
|
+
_attributes = self.attributes_with_relations
|
24
|
+
_attributes.merge!("version" => _version)
|
25
|
+
_changes = self.changes_with_relations.dup
|
26
|
+
_changes.merge!("version" => [self.version_was, _version])
|
27
|
+
|
28
|
+
Mongoid::Delorean::History.create(original_class: self.class.name, original_class_id: self.id, version: _version, altered_attributes: _changes, full_attributes: _attributes).inspect
|
29
|
+
self.without_history_tracking do
|
30
|
+
self.update_attributes(version: _version)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def track_history?
|
36
|
+
@__track_changes.nil? ? true : @__track_changes
|
37
|
+
end
|
38
|
+
|
39
|
+
def without_history_tracking
|
40
|
+
@__track_changes = false
|
41
|
+
yield
|
42
|
+
@__track_changes = true
|
43
|
+
end
|
44
|
+
|
45
|
+
def revert!(version = (self.version - 1))
|
46
|
+
old_version = self.versions.where(version: version).first
|
47
|
+
if old_version
|
48
|
+
old_version.full_attributes.each do |key, value|
|
49
|
+
self.send("#{key}=", value)
|
50
|
+
end
|
51
|
+
self.save!
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module CommonInstanceMethods
|
56
|
+
|
57
|
+
def self.extended(klass)
|
58
|
+
super
|
59
|
+
included(klass)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.included(klass)
|
63
|
+
super
|
64
|
+
klass.embedded_relations.each do |name, details|
|
65
|
+
_klass = Kernel.const_get(details.class_name)
|
66
|
+
_klass.send(:include, Mongoid::Delorean::Trackable::CommonInstanceMethods)
|
67
|
+
_klass.after_save :save_parent_version
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def save_parent_version
|
72
|
+
if self.embedded?
|
73
|
+
self._parent.save_parent_version
|
74
|
+
else
|
75
|
+
self.save_version
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def changes_with_relations
|
80
|
+
_changes = self.changes.dup
|
81
|
+
|
82
|
+
%w{version updated_at created_at}.each do |col|
|
83
|
+
_changes.delete(col)
|
84
|
+
_changes.delete(col.to_sym)
|
85
|
+
end
|
86
|
+
|
87
|
+
relation_changes = {}
|
88
|
+
self.embedded_relations.each do |name, details|
|
89
|
+
relation = self.send(name)
|
90
|
+
relation_changes[name] = []
|
91
|
+
r_changes = relation.map {|o| o.changes_with_relations}
|
92
|
+
relation_changes[name] << r_changes unless r_changes.empty?
|
93
|
+
relation_changes[name].flatten!
|
94
|
+
relation_changes.delete(name) if relation_changes[name].empty?
|
95
|
+
end
|
96
|
+
|
97
|
+
_changes.merge!(relation_changes)
|
98
|
+
return _changes
|
99
|
+
end
|
100
|
+
|
101
|
+
def attributes_with_relations
|
102
|
+
_attributes = self.attributes.dup
|
103
|
+
|
104
|
+
relation_attrs = {}
|
105
|
+
self.embedded_relations.each do |name, details|
|
106
|
+
relation = self.send(name)
|
107
|
+
relation_attrs[name] = []
|
108
|
+
r_attrs = relation.map {|o| o.attributes_with_relations}
|
109
|
+
relation_attrs[name] << r_attrs unless r_attrs.empty?
|
110
|
+
r_changes = relation.map {|o| o.changes}
|
111
|
+
relation_attrs[name].flatten!
|
112
|
+
end
|
113
|
+
_attributes.merge!(relation_attrs)
|
114
|
+
return _attributes
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mongoid/delorean/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "mongoid_delorean"
|
8
|
+
gem.version = Mongoid::Delorean::VERSION
|
9
|
+
gem.authors = ["Mark Bates"]
|
10
|
+
gem.email = ["mark@markbates.com"]
|
11
|
+
gem.description = %q{A simple Mongoid 3 versioning system that works with embedded documents.}
|
12
|
+
gem.summary = %q{A simple Mongoid 3 versioning system that works with embedded documents.}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency("mongoid", ">=3.0.0")
|
21
|
+
|
22
|
+
end
|
data/spec/config.yml
ADDED
@@ -0,0 +1,273 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mongoid::Delorean::Trackable do
|
4
|
+
|
5
|
+
class Article
|
6
|
+
include Mongoid::Document
|
7
|
+
include Mongoid::Timestamps
|
8
|
+
include Mongoid::Delorean::Trackable
|
9
|
+
|
10
|
+
field :name, type: String
|
11
|
+
field :summary, type: String
|
12
|
+
|
13
|
+
embeds_many :pages
|
14
|
+
end
|
15
|
+
|
16
|
+
class Page
|
17
|
+
include Mongoid::Document
|
18
|
+
include Mongoid::Timestamps
|
19
|
+
|
20
|
+
field :name, type: String
|
21
|
+
|
22
|
+
embedded_in :article, inverse_of: :pages
|
23
|
+
embeds_many :sections
|
24
|
+
end
|
25
|
+
|
26
|
+
class Section
|
27
|
+
include Mongoid::Document
|
28
|
+
include Mongoid::Timestamps
|
29
|
+
|
30
|
+
field :name, type: String
|
31
|
+
field :body, type: String
|
32
|
+
|
33
|
+
embedded_in :page, inverse_of: :sections
|
34
|
+
end
|
35
|
+
|
36
|
+
class User
|
37
|
+
include Mongoid::Document
|
38
|
+
include Mongoid::Timestamps
|
39
|
+
include Mongoid::Delorean::Trackable
|
40
|
+
|
41
|
+
field :name, type: String
|
42
|
+
field :age, type: Integer
|
43
|
+
end
|
44
|
+
|
45
|
+
context "simple document" do
|
46
|
+
|
47
|
+
it "creates a history object" do
|
48
|
+
expect {
|
49
|
+
expect {
|
50
|
+
u = User.create!(name: "Mark")
|
51
|
+
}.to change(User, :count).by(1)
|
52
|
+
}.to change(Mongoid::Delorean::History, :count).by(1)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "sets the first version to 1" do
|
56
|
+
u = User.create!(name: "Mark")
|
57
|
+
u.version.should eql(1)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "returns a list of the versions" do
|
61
|
+
u = User.create!(name: "Mark")
|
62
|
+
versions = u.versions
|
63
|
+
versions.size.should eql(1)
|
64
|
+
versions.first.version.should eql(1)
|
65
|
+
|
66
|
+
u.save!
|
67
|
+
versions = u.versions
|
68
|
+
versions.size.should eql(2)
|
69
|
+
versions.last.version.should eql(2)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "does not track created_at changes" do
|
73
|
+
u = User.create!(name: "Mark")
|
74
|
+
version = u.versions.first
|
75
|
+
version.altered_attributes.should_not include("created_at")
|
76
|
+
end
|
77
|
+
|
78
|
+
it "does not track updated_at changes" do
|
79
|
+
u = User.create!(name: "Mark")
|
80
|
+
u.update_attributes(age: 36)
|
81
|
+
version = u.versions.last
|
82
|
+
version.altered_attributes.should_not include("updated_at")
|
83
|
+
end
|
84
|
+
|
85
|
+
it "tracks the changes that were made" do
|
86
|
+
u = User.create!(name: "Mark")
|
87
|
+
version = u.versions.first
|
88
|
+
version.altered_attributes.should eql({"_id"=>[nil, u.id], "version"=>[nil, 1], "name"=>[nil, "Mark"]})
|
89
|
+
|
90
|
+
u.update_attributes(age: 36)
|
91
|
+
version = u.versions.last
|
92
|
+
version.altered_attributes.should eql({"version"=>[1, 2], "age"=>[nil, 36]})
|
93
|
+
end
|
94
|
+
|
95
|
+
it "tracks the full set of attributes at the time of saving" do
|
96
|
+
u = User.create!(name: "Mark")
|
97
|
+
|
98
|
+
version = u.versions.first
|
99
|
+
version.full_attributes.should eql({"_id"=>u.id, "version"=>1, "name"=>"Mark"})
|
100
|
+
|
101
|
+
u.update_attributes(age: 36)
|
102
|
+
|
103
|
+
version = u.versions.last
|
104
|
+
version.full_attributes.except("created_at", "updated_at").should eql({"_id"=>u.id, "version"=>2, "name"=>"Mark", "age"=>36})
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#without_history_tracking" do
|
108
|
+
|
109
|
+
it "it doesn't track the history of the save" do
|
110
|
+
expect {
|
111
|
+
expect {
|
112
|
+
u = User.new(name: "Mark")
|
113
|
+
u.without_history_tracking do
|
114
|
+
u.save!
|
115
|
+
end
|
116
|
+
}.to change(User, :count).by(1)
|
117
|
+
}.to_not change(Mongoid::Delorean::History, :count).by(1)
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
describe '#revert!' do
|
123
|
+
|
124
|
+
it "reverts to the last version" do
|
125
|
+
u = User.create!(name: "Mark")
|
126
|
+
u.update_attributes(age: 36)
|
127
|
+
u.update_attributes(name: "Mark Bates")
|
128
|
+
u.versions.size.should eql(3)
|
129
|
+
u.version.should eql(3)
|
130
|
+
u.revert!
|
131
|
+
u.version.should eql(4)
|
132
|
+
u.name.should eql("Mark")
|
133
|
+
u.age.should eql(36)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "reverts to a specific version" do
|
137
|
+
u = User.create!(name: "Mark")
|
138
|
+
u.update_attributes(age: 36)
|
139
|
+
u.update_attributes(name: "Mark Bates")
|
140
|
+
u.versions.size.should eql(3)
|
141
|
+
u.version.should eql(3)
|
142
|
+
u.revert!(2)
|
143
|
+
u.version.should eql(4)
|
144
|
+
u.name.should eql("Mark")
|
145
|
+
u.age.should eql(36)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "does nothing if the specific version doesn't exist" do
|
149
|
+
u = User.create!(name: "Mark")
|
150
|
+
u.update_attributes(age: 36)
|
151
|
+
u.update_attributes(name: "Mark Bates")
|
152
|
+
u.versions.size.should eql(3)
|
153
|
+
u.version.should eql(3)
|
154
|
+
u.revert!(20)
|
155
|
+
u.version.should eql(3)
|
156
|
+
u.name.should eql("Mark Bates")
|
157
|
+
u.age.should eql(36)
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
context "complex documents with embeds" do
|
165
|
+
|
166
|
+
it "tracks the changes" do
|
167
|
+
a = Article.create!(name: "My Article")
|
168
|
+
|
169
|
+
version = a.versions.first
|
170
|
+
version.altered_attributes.should eql({"_id"=>[nil, a.id], "version"=>[nil, 1], "name"=>[nil, "My Article"]})
|
171
|
+
end
|
172
|
+
|
173
|
+
it "tracks the changes including embedded docs" do
|
174
|
+
a = Article.new(name: "My Article")
|
175
|
+
page = a.pages.build(name: "Page 1")
|
176
|
+
a.save!
|
177
|
+
|
178
|
+
version = a.versions.first
|
179
|
+
version.altered_attributes.should eql({"_id"=>[nil, a.id], "version"=>[nil, 1], "name"=>[nil, "My Article"], "pages"=>[{"_id"=>[nil, page.id], "name"=>[nil, "Page 1"]}]})
|
180
|
+
|
181
|
+
page.name = "The Page 1"
|
182
|
+
a.save!
|
183
|
+
|
184
|
+
version = a.versions.last
|
185
|
+
version.altered_attributes.should eql({"pages"=>[{"name"=>["Page 1", "The Page 1"]}], "version"=>[1, 2]})
|
186
|
+
|
187
|
+
section = page.sections.build(body: "some body text")
|
188
|
+
a.save!
|
189
|
+
|
190
|
+
version = a.versions.last
|
191
|
+
version.altered_attributes.should eql({"pages"=>[{"sections"=>[{"_id"=>[nil, section.id], "body"=>[nil, "some body text"]}]}], "version"=>[2, 3]})
|
192
|
+
end
|
193
|
+
|
194
|
+
it "tracks the full set of attributes at the time of saving" do
|
195
|
+
a = Article.create!(name: "My Article")
|
196
|
+
|
197
|
+
version = a.versions.first
|
198
|
+
version.full_attributes.should eql({"_id"=>a.id, "version"=>1, "name"=>"My Article", "pages"=>[]})
|
199
|
+
|
200
|
+
a.update_attributes(summary: "Summary about the article")
|
201
|
+
|
202
|
+
version = a.versions.last
|
203
|
+
version.full_attributes.except("created_at", "updated_at").should eql({"_id"=>a.id, "version"=>2, "name"=>"My Article", "summary"=>"Summary about the article", "pages"=>[]})
|
204
|
+
end
|
205
|
+
|
206
|
+
it "tracks the full set of attributes including embeds at the time of saving" do
|
207
|
+
a = Article.new(name: "My Article")
|
208
|
+
page = a.pages.build(name: "Page 1")
|
209
|
+
a.save!
|
210
|
+
|
211
|
+
version = a.versions.first
|
212
|
+
version.full_attributes.should eql({"_id"=>a.id, "version"=>1, "name"=>"My Article", "pages"=>[{"_id"=>page.id, "name"=>"Page 1", "sections"=>[]}]})
|
213
|
+
|
214
|
+
a.update_attributes(summary: "Summary about the article")
|
215
|
+
|
216
|
+
version = a.versions.last
|
217
|
+
version.full_attributes.except("created_at", "updated_at").should eql({"_id"=>a.id, "version"=>2, "name"=>"My Article", "pages"=>[{"_id"=>page.id, "name"=>"Page 1", "sections"=>[]}], "summary"=>"Summary about the article"})
|
218
|
+
end
|
219
|
+
|
220
|
+
it "tracks changes when an embedded document is saved" do
|
221
|
+
a = Article.new(name: "Article 1")
|
222
|
+
page = a.pages.build(name: "Page 1")
|
223
|
+
a.save!
|
224
|
+
a.version.should eql(1)
|
225
|
+
page.name = "The 1st Page"
|
226
|
+
page.save!
|
227
|
+
a.reload
|
228
|
+
a.version.should eql(2)
|
229
|
+
page = a.pages.first
|
230
|
+
page.name.should eql("The 1st Page")
|
231
|
+
end
|
232
|
+
|
233
|
+
describe '#revert!' do
|
234
|
+
|
235
|
+
it "reverts to the last version" do
|
236
|
+
a = Article.create!(name: "My Article")
|
237
|
+
a.pages.create!(name: "Page 1")
|
238
|
+
a.pages.should_not be_empty
|
239
|
+
a.revert!
|
240
|
+
a.name.should eql("My Article")
|
241
|
+
a.pages.should be_empty
|
242
|
+
end
|
243
|
+
|
244
|
+
it "reverts to a specific version" do
|
245
|
+
a = Article.create!(name: "My Article")
|
246
|
+
a.pages.build(name: "Page 1")
|
247
|
+
a.save!
|
248
|
+
a.pages.should_not be_empty
|
249
|
+
page = a.pages.first
|
250
|
+
page.name = "The 1st Page"
|
251
|
+
a.save!
|
252
|
+
page.sections.build(name: "Section 1")
|
253
|
+
a.save!
|
254
|
+
a.pages.first.sections.first.name.should eql("Section 1")
|
255
|
+
a.revert!(3)
|
256
|
+
page.reload
|
257
|
+
page.name.should eql("The 1st Page")
|
258
|
+
page.sections.should be_empty
|
259
|
+
end
|
260
|
+
|
261
|
+
it "does nothing if the specific version doesn't exist" do
|
262
|
+
a = Article.create!(name: "My Article")
|
263
|
+
a.pages.build(name: "Page 1")
|
264
|
+
a.save!
|
265
|
+
a.revert!(20)
|
266
|
+
a.version.should eql(2)
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
end
|
272
|
+
|
273
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
|
3
|
+
require 'mongoid_delorean' # and any other gems you need
|
4
|
+
|
5
|
+
require 'database_cleaner'
|
6
|
+
|
7
|
+
Mongoid.load!(File.join(File.dirname(__FILE__), "config.yml"), :test)
|
8
|
+
|
9
|
+
DatabaseCleaner[:mongoid].strategy = :truncation
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
|
13
|
+
config.before(:each) do
|
14
|
+
DatabaseCleaner.clean
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mongoid_delorean
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Mark Bates
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-09 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mongoid
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.0.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.0.0
|
30
|
+
description: A simple Mongoid 3 versioning system that works with embedded documents.
|
31
|
+
email:
|
32
|
+
- mark@markbates.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- .gitignore
|
38
|
+
- Gemfile
|
39
|
+
- Gemfile.lock
|
40
|
+
- LICENSE.txt
|
41
|
+
- README.md
|
42
|
+
- Rakefile
|
43
|
+
- lib/mongoid/delorean/history.rb
|
44
|
+
- lib/mongoid/delorean/trackable.rb
|
45
|
+
- lib/mongoid/delorean/version.rb
|
46
|
+
- lib/mongoid_delorean.rb
|
47
|
+
- mongoid_delorean.gemspec
|
48
|
+
- spec/config.yml
|
49
|
+
- spec/mongoid/delorean/trackable_spec.rb
|
50
|
+
- spec/spec_helper.rb
|
51
|
+
homepage: ''
|
52
|
+
licenses: []
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
requirements: []
|
70
|
+
rubyforge_project:
|
71
|
+
rubygems_version: 1.8.24
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: A simple Mongoid 3 versioning system that works with embedded documents.
|
75
|
+
test_files:
|
76
|
+
- spec/config.yml
|
77
|
+
- spec/mongoid/delorean/trackable_spec.rb
|
78
|
+
- spec/spec_helper.rb
|