mongoid-history 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +19 -1
- data/Gemfile +1 -2
- data/README.md +123 -1
- data/VERSION +1 -1
- data/lib/mongoid/history.rb +14 -0
- data/lib/mongoid/history/trackable.rb +142 -59
- data/lib/mongoid/history/tracker.rb +77 -27
- data/mongoid-history.gemspec +2 -4
- data/spec/integration/integration_spec.rb +382 -238
- data/spec/integration/multi_relation_spec.rb +9 -2
- data/spec/integration/nested_embedded_documents_spec.rb +7 -5
- data/spec/spec_helper.rb +1 -3
- data/spec/support/mongoid.rb +8 -10
- data/spec/trackable_spec.rb +168 -41
- metadata +3 -5
- data/config/mongoid.yml +0 -8
- data/spec/support/database_cleaner.rb +0 -9
|
@@ -82,13 +82,77 @@ module Mongoid::History
|
|
|
82
82
|
@trackable_parent ||= trackable_parents_and_trackable[-2]
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
+
# Outputs a :from, :to hash for each affected field. Intentionally excludes fields
|
|
86
|
+
# which are not tracked, even if there are tracked values for such fields
|
|
87
|
+
# present in the database.
|
|
88
|
+
#
|
|
89
|
+
# @return [ HashWithIndifferentAccess ] a change set in the format:
|
|
90
|
+
# { field_1: {to: new_val}, field_2: {from: old_val, to: new_val} }
|
|
91
|
+
def tracked_changes
|
|
92
|
+
@tracked_changes ||= (modified.keys | original.keys).inject(HashWithIndifferentAccess.new) do |h,k|
|
|
93
|
+
h[k] = {from: original[k], to: modified[k]}.delete_if{|k,v| v.nil?}
|
|
94
|
+
h
|
|
95
|
+
end.delete_if{|k,v| v.blank? || !trackable_parent_class.tracked_field?(k)}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Outputs summary of edit actions performed: :add, :modify, :remove, or :array.
|
|
99
|
+
# Does deep comparison of arrays. Useful for creating human-readable representations
|
|
100
|
+
# of the history tracker. Considers changing a value to 'blank' to be a removal.
|
|
101
|
+
#
|
|
102
|
+
# @return [ HashWithIndifferentAccess ] a change set in the format:
|
|
103
|
+
# { add: { field_1: new_val, ... },
|
|
104
|
+
# modify: { field_2: {from: old_val, to: new_val}, ... },
|
|
105
|
+
# remove: { field_3: old_val },
|
|
106
|
+
# array: { field_4: {add: ['foo', 'bar'], remove: ['baz']} } }
|
|
107
|
+
def tracked_edits
|
|
108
|
+
@tracked_edits ||= tracked_changes.inject(HashWithIndifferentAccess.new) do |h,(k,v)|
|
|
109
|
+
unless v[:from].blank? && v[:to].blank?
|
|
110
|
+
if v[:from].blank?
|
|
111
|
+
h[:add] ||={}
|
|
112
|
+
h[:add][k] = v[:to]
|
|
113
|
+
elsif v[:to].blank?
|
|
114
|
+
h[:remove] ||={}
|
|
115
|
+
h[:remove][k] = v[:from]
|
|
116
|
+
else
|
|
117
|
+
if v[:from].is_a?(Array) && v[:to].is_a?(Array)
|
|
118
|
+
h[:array] ||={}
|
|
119
|
+
old_values = v[:from] - v[:to]
|
|
120
|
+
new_values = v[:to] - v[:from]
|
|
121
|
+
h[:array][k] = {add: new_values, remove: old_values}.delete_if{|k,v| v.blank?}
|
|
122
|
+
else
|
|
123
|
+
h[:modify] ||={}
|
|
124
|
+
h[:modify][k] = v
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
h
|
|
129
|
+
end
|
|
130
|
+
end
|
|
85
131
|
|
|
132
|
+
# Similar to #tracked_changes, but contains only a single value for each
|
|
133
|
+
# affected field:
|
|
134
|
+
# - :create and :update return the modified values
|
|
135
|
+
# - :destroy returns original values
|
|
136
|
+
# Included for legacy compatibility.
|
|
137
|
+
#
|
|
138
|
+
# @deprecated
|
|
139
|
+
#
|
|
140
|
+
# @return [ HashWithIndifferentAccess ] a change set in the format:
|
|
141
|
+
# { field_1: value, field_2: value }
|
|
86
142
|
def affected
|
|
87
|
-
|
|
88
|
-
|
|
143
|
+
target = action.to_sym == :destroy ? :from : :to
|
|
144
|
+
@affected ||= tracked_changes.inject(HashWithIndifferentAccess.new){|h,(k,v)| h[k] = v[target]; h}
|
|
89
145
|
end
|
|
90
146
|
|
|
91
|
-
|
|
147
|
+
# Returns the class of the trackable, irrespective of whether the trackable object
|
|
148
|
+
# has been destroyed.
|
|
149
|
+
#
|
|
150
|
+
# @return [ Class ] the class of the trackable
|
|
151
|
+
def trackable_parent_class
|
|
152
|
+
association_chain.first["name"].constantize
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
private
|
|
92
156
|
|
|
93
157
|
def re_create
|
|
94
158
|
association_chain.length > 1 ? create_on_parent : create_standalone
|
|
@@ -99,18 +163,17 @@ private
|
|
|
99
163
|
end
|
|
100
164
|
|
|
101
165
|
def create_standalone
|
|
102
|
-
|
|
103
|
-
restored =
|
|
104
|
-
restored.id = modified["_id"]
|
|
166
|
+
restored = trackable_parent_class.new(localize_keys(original))
|
|
167
|
+
restored.id = original["_id"]
|
|
105
168
|
restored.save!
|
|
106
169
|
end
|
|
107
170
|
|
|
108
171
|
def create_on_parent
|
|
109
172
|
name = association_chain.last["name"]
|
|
110
|
-
if embeds_one?(
|
|
111
|
-
trackable_parent.
|
|
112
|
-
elsif embeds_many?(
|
|
113
|
-
|
|
173
|
+
if trackable_parent.class.embeds_one?(name)
|
|
174
|
+
trackable_parent.create_embedded(name, localize_keys(original))
|
|
175
|
+
elsif trackable_parent.class.embeds_many?(name)
|
|
176
|
+
trackable_parent.get_embedded(name).create!(localize_keys(original))
|
|
114
177
|
else
|
|
115
178
|
raise "This should never happen. Please report bug!"
|
|
116
179
|
end
|
|
@@ -120,19 +183,6 @@ private
|
|
|
120
183
|
@trackable_parents_and_trackable ||= traverse_association_chain
|
|
121
184
|
end
|
|
122
185
|
|
|
123
|
-
def relation_of(doc, name)
|
|
124
|
-
meta = doc.reflect_on_association(name)
|
|
125
|
-
meta ? meta.relation : nil
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def embeds_one?(doc, name)
|
|
129
|
-
relation_of(doc, name) == Mongoid::Relations::Embedded::One
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def embeds_many?(doc, name)
|
|
133
|
-
relation_of(doc, name) == Mongoid::Relations::Embedded::Many
|
|
134
|
-
end
|
|
135
|
-
|
|
136
186
|
def traverse_association_chain
|
|
137
187
|
chain = association_chain.dup
|
|
138
188
|
doc = nil
|
|
@@ -146,10 +196,10 @@ private
|
|
|
146
196
|
# root association. First element of the association chain
|
|
147
197
|
klass = name.classify.constantize
|
|
148
198
|
klass.where(:_id => node['id']).first
|
|
149
|
-
elsif embeds_one?(
|
|
150
|
-
doc.
|
|
151
|
-
elsif embeds_many?(
|
|
152
|
-
doc.
|
|
199
|
+
elsif doc.class.embeds_one?(name)
|
|
200
|
+
doc.get_embedded(name)
|
|
201
|
+
elsif doc.class.embeds_many?(name)
|
|
202
|
+
doc.get_embedded(name).where(:_id => node['id']).first
|
|
153
203
|
else
|
|
154
204
|
raise "This should never happen. Please report bug."
|
|
155
205
|
end
|
data/mongoid-history.gemspec
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
Gem::Specification.new do |s|
|
|
7
7
|
s.name = "mongoid-history"
|
|
8
|
-
s.version = "0.
|
|
8
|
+
s.version = "0.4.0"
|
|
9
9
|
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
11
|
s.authors = ["Aaron Qian", "Justin Grimes"]
|
|
12
|
-
s.date = "2013-
|
|
12
|
+
s.date = "2013-07-12"
|
|
13
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
14
|
s.email = ["aq1018@gmail.com", "justin.mgrimes@gmail.com"]
|
|
15
15
|
s.extra_rdoc_files = [
|
|
@@ -26,7 +26,6 @@ Gem::Specification.new do |s|
|
|
|
26
26
|
"README.md",
|
|
27
27
|
"Rakefile",
|
|
28
28
|
"VERSION",
|
|
29
|
-
"config/mongoid.yml",
|
|
30
29
|
"lib/mongoid-history.rb",
|
|
31
30
|
"lib/mongoid/history.rb",
|
|
32
31
|
"lib/mongoid/history/sweeper.rb",
|
|
@@ -37,7 +36,6 @@ Gem::Specification.new do |s|
|
|
|
37
36
|
"spec/integration/multi_relation_spec.rb",
|
|
38
37
|
"spec/integration/nested_embedded_documents_spec.rb",
|
|
39
38
|
"spec/spec_helper.rb",
|
|
40
|
-
"spec/support/database_cleaner.rb",
|
|
41
39
|
"spec/support/mongoid.rb",
|
|
42
40
|
"spec/support/mongoid_history.rb",
|
|
43
41
|
"spec/trackable_spec.rb",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe Mongoid::History do
|
|
4
|
-
before :
|
|
4
|
+
before :all do
|
|
5
5
|
class Post
|
|
6
6
|
include Mongoid::Document
|
|
7
7
|
include Mongoid::Timestamps
|
|
@@ -11,9 +11,9 @@ describe Mongoid::History do
|
|
|
11
11
|
field :body
|
|
12
12
|
field :rating
|
|
13
13
|
|
|
14
|
-
embeds_many :comments
|
|
15
|
-
embeds_one :section
|
|
16
|
-
embeds_many :tags
|
|
14
|
+
embeds_many :comments, store_as: :coms
|
|
15
|
+
embeds_one :section, store_as: :sec
|
|
16
|
+
embeds_many :tags, :cascade_callbacks => true
|
|
17
17
|
|
|
18
18
|
accepts_nested_attributes_for :tags, :allow_destroy => true
|
|
19
19
|
|
|
@@ -25,9 +25,9 @@ describe Mongoid::History do
|
|
|
25
25
|
include Mongoid::Timestamps
|
|
26
26
|
include Mongoid::History::Trackable
|
|
27
27
|
|
|
28
|
-
field :title
|
|
28
|
+
field :t, as: :title
|
|
29
29
|
field :body
|
|
30
|
-
embedded_in :
|
|
30
|
+
embedded_in :commentable, polymorphic: true
|
|
31
31
|
track_history :on => [:title, :body], :scope => :post, :track_create => true, :track_destroy => true
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -36,7 +36,7 @@ describe Mongoid::History do
|
|
|
36
36
|
include Mongoid::Timestamps
|
|
37
37
|
include Mongoid::History::Trackable
|
|
38
38
|
|
|
39
|
-
field :title
|
|
39
|
+
field :t, as: :title
|
|
40
40
|
embedded_in :post
|
|
41
41
|
track_history :on => [:title], :scope => :post, :track_create => true, :track_destroy => true
|
|
42
42
|
end
|
|
@@ -46,15 +46,19 @@ describe Mongoid::History do
|
|
|
46
46
|
include Mongoid::Timestamps
|
|
47
47
|
include Mongoid::History::Trackable
|
|
48
48
|
|
|
49
|
-
field :
|
|
50
|
-
field :
|
|
49
|
+
field :n, as: :name
|
|
50
|
+
field :em, as: :email
|
|
51
|
+
field :phone
|
|
52
|
+
field :address
|
|
53
|
+
field :city
|
|
54
|
+
field :country
|
|
51
55
|
field :aliases, :type => Array
|
|
52
|
-
track_history :except => [:email]
|
|
56
|
+
track_history :except => [:email, :updated_at]
|
|
53
57
|
end
|
|
54
58
|
|
|
55
59
|
class Tag
|
|
56
60
|
include Mongoid::Document
|
|
57
|
-
include Mongoid::Timestamps
|
|
61
|
+
# include Mongoid::Timestamps (see: https://github.com/mongoid/mongoid/issues/3078)
|
|
58
62
|
include Mongoid::History::Trackable
|
|
59
63
|
|
|
60
64
|
belongs_to :updated_by, :class_name => "User"
|
|
@@ -63,144 +67,151 @@ describe Mongoid::History do
|
|
|
63
67
|
track_history :on => [:title], :scope => :post, :track_create => true, :track_destroy => true, :modifier_field => :updated_by
|
|
64
68
|
end
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
@
|
|
70
|
+
class Foo < Comment
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
@persisted_history_options = Mongoid::History.trackable_class_options
|
|
70
74
|
end
|
|
71
75
|
|
|
76
|
+
before(:each){ Mongoid::History.trackable_class_options = @persisted_history_options }
|
|
77
|
+
let(:user){ User.create(name: "Aaron", email: "aaron@randomemail.com", aliases: ['bob'], country: 'Canada', city: 'Toronto', address: '21 Jump Street') }
|
|
78
|
+
let(:another_user){ User.create(:name => "Another Guy", :email => "anotherguy@randomemail.com") }
|
|
79
|
+
let(:post){ Post.create(:title => "Test", :body => "Post", :modifier => user, :views => 100) }
|
|
80
|
+
let(:comment){ post.comments.create(:title => "test", :body => "comment", :modifier => user) }
|
|
81
|
+
let(:tag){ Tag.create(:title => "test") }
|
|
82
|
+
|
|
72
83
|
describe "track" do
|
|
73
84
|
describe "on creation" do
|
|
74
85
|
it "should have one history track in comment" do
|
|
75
|
-
|
|
86
|
+
comment.history_tracks.count.should == 1
|
|
76
87
|
end
|
|
77
88
|
|
|
78
89
|
it "should assign title and body on modified" do
|
|
79
|
-
|
|
90
|
+
comment.history_tracks.first.modified.should == {'t' => "test", 'body' => "comment"}
|
|
80
91
|
end
|
|
81
92
|
|
|
82
93
|
it "should not assign title and body on original" do
|
|
83
|
-
|
|
94
|
+
comment.history_tracks.first.original.should == {}
|
|
84
95
|
end
|
|
85
96
|
|
|
86
97
|
it "should assign modifier" do
|
|
87
|
-
|
|
98
|
+
comment.history_tracks.first.modifier.should == user
|
|
88
99
|
end
|
|
89
100
|
|
|
90
101
|
it "should assign version" do
|
|
91
|
-
|
|
102
|
+
comment.history_tracks.first.version.should == 1
|
|
92
103
|
end
|
|
93
104
|
|
|
94
105
|
it "should assign scope" do
|
|
95
|
-
|
|
106
|
+
comment.history_tracks.first.scope.should == "post"
|
|
96
107
|
end
|
|
97
108
|
|
|
98
109
|
it "should assign method" do
|
|
99
|
-
|
|
110
|
+
comment.history_tracks.first.action.should == "create"
|
|
100
111
|
end
|
|
101
112
|
|
|
102
113
|
it "should assign association_chain" do
|
|
103
114
|
expected = [
|
|
104
|
-
{'id' =>
|
|
105
|
-
{'id' =>
|
|
115
|
+
{'id' => post.id, 'name' => "Post"},
|
|
116
|
+
{'id' => comment.id, 'name' => "coms"}
|
|
106
117
|
]
|
|
107
|
-
|
|
118
|
+
comment.history_tracks.first.association_chain.should == expected
|
|
108
119
|
end
|
|
109
120
|
end
|
|
110
121
|
|
|
111
122
|
describe "on destruction" do
|
|
112
123
|
it "should have two history track records in post" do
|
|
113
124
|
lambda {
|
|
114
|
-
|
|
125
|
+
post.destroy
|
|
115
126
|
}.should change(Tracker, :count).by(1)
|
|
116
127
|
end
|
|
117
128
|
|
|
118
129
|
it "should assign destroy on track record" do
|
|
119
|
-
|
|
120
|
-
|
|
130
|
+
post.destroy
|
|
131
|
+
post.history_tracks.last.action.should == "destroy"
|
|
121
132
|
end
|
|
122
133
|
|
|
123
134
|
it "should return affected attributes from track record" do
|
|
124
|
-
|
|
125
|
-
|
|
135
|
+
post.destroy
|
|
136
|
+
post.history_tracks.last.affected["title"].should == "Test"
|
|
126
137
|
end
|
|
127
138
|
end
|
|
128
139
|
|
|
129
140
|
describe "on update non-embedded" do
|
|
130
141
|
it "should create a history track if changed attributes match tracked attributes" do
|
|
131
142
|
lambda {
|
|
132
|
-
|
|
143
|
+
post.update_attributes(:title => "Another Test")
|
|
133
144
|
}.should change(Tracker, :count).by(1)
|
|
134
145
|
end
|
|
135
146
|
|
|
136
147
|
it "should not create a history track if changed attributes do not match tracked attributes" do
|
|
137
148
|
lambda {
|
|
138
|
-
|
|
149
|
+
post.update_attributes(:rating => "untracked")
|
|
139
150
|
}.should change(Tracker, :count).by(0)
|
|
140
151
|
end
|
|
141
152
|
|
|
142
153
|
it "should assign modified fields" do
|
|
143
|
-
|
|
144
|
-
|
|
154
|
+
post.update_attributes(:title => "Another Test")
|
|
155
|
+
post.history_tracks.last.modified.should == {
|
|
145
156
|
"title" => "Another Test"
|
|
146
157
|
}
|
|
147
158
|
end
|
|
148
159
|
|
|
149
160
|
it "should assign method field" do
|
|
150
|
-
|
|
151
|
-
|
|
161
|
+
post.update_attributes(:title => "Another Test")
|
|
162
|
+
post.history_tracks.last.action.should == "update"
|
|
152
163
|
end
|
|
153
164
|
|
|
154
165
|
it "should assign original fields" do
|
|
155
|
-
|
|
156
|
-
|
|
166
|
+
post.update_attributes(:title => "Another Test")
|
|
167
|
+
post.history_tracks.last.original.should == {
|
|
157
168
|
"title" => "Test"
|
|
158
169
|
}
|
|
159
170
|
end
|
|
160
171
|
|
|
161
172
|
it "should assign modifier" do
|
|
162
|
-
|
|
163
|
-
|
|
173
|
+
post.update_attributes(:title => "Another Test")
|
|
174
|
+
post.history_tracks.first.modifier.should == user
|
|
164
175
|
end
|
|
165
176
|
|
|
166
177
|
it "should assign version on history tracks" do
|
|
167
|
-
|
|
168
|
-
|
|
178
|
+
post.update_attributes(:title => "Another Test")
|
|
179
|
+
post.history_tracks.first.version.should == 1
|
|
169
180
|
end
|
|
170
181
|
|
|
171
182
|
it "should assign version on post" do
|
|
172
|
-
|
|
173
|
-
|
|
183
|
+
post.update_attributes(:title => "Another Test")
|
|
184
|
+
post.version.should == 1
|
|
174
185
|
end
|
|
175
186
|
|
|
176
187
|
it "should assign scope" do
|
|
177
|
-
|
|
178
|
-
|
|
188
|
+
post.update_attributes(:title => "Another Test")
|
|
189
|
+
post.history_tracks.first.scope.should == "post"
|
|
179
190
|
end
|
|
180
191
|
|
|
181
192
|
it "should assign association_chain" do
|
|
182
|
-
|
|
183
|
-
|
|
193
|
+
post.update_attributes(:title => "Another Test")
|
|
194
|
+
post.history_tracks.last.association_chain.should == [{'id' => post.id, 'name' => "Post"}]
|
|
184
195
|
end
|
|
185
196
|
|
|
186
197
|
it "should exclude defined options" do
|
|
187
|
-
name =
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
198
|
+
name = user.name
|
|
199
|
+
user.update_attributes(:name => "Aaron2", :email => "aaronsnewemail@randomemail.com")
|
|
200
|
+
user.history_tracks.first.original.keys.should == [ "n" ]
|
|
201
|
+
user.history_tracks.first.original["n"].should == name
|
|
202
|
+
user.history_tracks.first.modified.keys.should == [ "n" ]
|
|
203
|
+
user.history_tracks.first.modified["n"].should == user.name
|
|
193
204
|
end
|
|
194
205
|
|
|
195
206
|
it "should undo field changes" do
|
|
196
|
-
name =
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
207
|
+
name = user.name
|
|
208
|
+
user.update_attributes(:name => "Aaron2", :email => "aaronsnewemail@randomemail.com")
|
|
209
|
+
user.history_tracks.first.undo! nil
|
|
210
|
+
user.reload.name.should == name
|
|
200
211
|
end
|
|
201
212
|
|
|
202
213
|
it "should undo non-existing field changes" do
|
|
203
|
-
post = Post.create(:modifier =>
|
|
214
|
+
post = Post.create(:modifier => user, :views => 100)
|
|
204
215
|
post.reload.title.should == nil
|
|
205
216
|
post.update_attributes(:title => "Aaron2")
|
|
206
217
|
post.reload.title.should == "Aaron2"
|
|
@@ -209,368 +220,463 @@ describe Mongoid::History do
|
|
|
209
220
|
end
|
|
210
221
|
|
|
211
222
|
it "should track array changes" do
|
|
212
|
-
aliases =
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
223
|
+
aliases = user.aliases
|
|
224
|
+
user.update_attributes(:aliases => [ 'bob', 'joe' ])
|
|
225
|
+
user.history_tracks.first.original["aliases"].should == aliases
|
|
226
|
+
user.history_tracks.first.modified["aliases"].should == user.aliases
|
|
216
227
|
end
|
|
217
228
|
|
|
218
229
|
it "should undo array changes" do
|
|
219
|
-
aliases =
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
230
|
+
aliases = user.aliases
|
|
231
|
+
user.update_attributes(:aliases => [ 'bob', 'joe' ])
|
|
232
|
+
user.history_tracks.first.undo! nil
|
|
233
|
+
user.reload.aliases.should == aliases
|
|
223
234
|
end
|
|
235
|
+
end
|
|
224
236
|
|
|
237
|
+
describe "#tracked_changes" do
|
|
238
|
+
context "create action" do
|
|
239
|
+
subject{ tag.history_tracks.first.tracked_changes }
|
|
240
|
+
it "consider all fields values as :to" do
|
|
241
|
+
subject[:title].should == {to: "test"}.with_indifferent_access
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
context "destroy action" do
|
|
245
|
+
subject{ tag.destroy; tag.history_tracks.last.tracked_changes }
|
|
246
|
+
it "consider all fields values as :from" do
|
|
247
|
+
subject[:title].should == {from: "test"}.with_indifferent_access
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
context "update action" do
|
|
251
|
+
subject{ user.history_tracks.first.tracked_changes }
|
|
252
|
+
before do
|
|
253
|
+
user.update_attributes(name: "Aaron2", email: nil, country: '', city: nil, phone: '867-5309', aliases: ['','bill','james'])
|
|
254
|
+
end
|
|
255
|
+
it{ should be_a HashWithIndifferentAccess }
|
|
256
|
+
it "should track changed field" do
|
|
257
|
+
subject[:n].should == {from: "Aaron", to:"Aaron2"}.with_indifferent_access
|
|
258
|
+
end
|
|
259
|
+
it "should track added field" do
|
|
260
|
+
subject[:phone].should == {to: "867-5309"}.with_indifferent_access
|
|
261
|
+
end
|
|
262
|
+
it "should track removed field" do
|
|
263
|
+
subject[:city].should == {from: "Toronto"}.with_indifferent_access
|
|
264
|
+
end
|
|
265
|
+
it "should not consider blank as removed" do
|
|
266
|
+
subject[:country].should == {from: "Canada", to: ''}.with_indifferent_access
|
|
267
|
+
end
|
|
268
|
+
it "should track changed array field" do
|
|
269
|
+
subject[:aliases].should == {from: ["bob"], to: ["", "bill", "james"]}.with_indifferent_access
|
|
270
|
+
end
|
|
271
|
+
it "should not track unmodified field" do
|
|
272
|
+
subject[:address].should be_nil
|
|
273
|
+
end
|
|
274
|
+
it "should not track untracked fields" do
|
|
275
|
+
subject[:email].should be_nil
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
describe "#tracked_edits" do
|
|
281
|
+
context "create action" do
|
|
282
|
+
subject{ tag.history_tracks.first.tracked_edits }
|
|
283
|
+
it "consider all edits as ;add" do
|
|
284
|
+
subject[:add].should == {title: "test"}.with_indifferent_access
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
context "destroy action" do
|
|
288
|
+
subject{ tag.destroy; tag.history_tracks.last.tracked_edits }
|
|
289
|
+
it "consider all edits as ;remove" do
|
|
290
|
+
subject[:remove].should == {title: "test"}.with_indifferent_access
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
context "update action" do
|
|
294
|
+
subject{ user.history_tracks.first.tracked_edits }
|
|
295
|
+
before do
|
|
296
|
+
user.update_attributes(name: "Aaron2", email: nil, country: '', city: nil, phone: '867-5309', aliases: ['','bill','james'])
|
|
297
|
+
end
|
|
298
|
+
it{ should be_a HashWithIndifferentAccess }
|
|
299
|
+
it "should track changed field" do
|
|
300
|
+
subject[:modify].should == {n: {from: "Aaron", to:"Aaron2"}}.with_indifferent_access
|
|
301
|
+
end
|
|
302
|
+
it "should track added field" do
|
|
303
|
+
subject[:add].should == {phone: "867-5309"}.with_indifferent_access
|
|
304
|
+
end
|
|
305
|
+
it "should track removed field and consider blank as removed" do
|
|
306
|
+
subject[:remove].should == {city: "Toronto", country: "Canada"}.with_indifferent_access
|
|
307
|
+
end
|
|
308
|
+
it "should track changed array field" do
|
|
309
|
+
subject[:array].should == {aliases: {remove: ["bob"], add: ["", "bill", "james"]}}.with_indifferent_access
|
|
310
|
+
end
|
|
311
|
+
it "should not track unmodified field" do
|
|
312
|
+
%w(add modify remove array).each do |edit|
|
|
313
|
+
subject[edit][:address].should be_nil
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
it "should not track untracked fields" do
|
|
317
|
+
%w(add modify remove array).each do |edit|
|
|
318
|
+
subject[edit][:email].should be_nil
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
context "with empty values" do
|
|
323
|
+
subject{ Tracker.new }
|
|
324
|
+
it "should skip empty values" do
|
|
325
|
+
subject.stub(:tracked_changes){ {name:{to:'',from:[]}, city:{to:'Toronto',from:''}} }
|
|
326
|
+
subject.tracked_edits.should == {add: {city: "Toronto"}}.with_indifferent_access
|
|
327
|
+
end
|
|
328
|
+
end
|
|
225
329
|
end
|
|
226
330
|
|
|
227
331
|
describe "on update non-embedded twice" do
|
|
228
332
|
it "should assign version on post" do
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
333
|
+
post.update_attributes(:title => "Test2")
|
|
334
|
+
post.update_attributes(:title => "Test3")
|
|
335
|
+
post.version.should == 2
|
|
232
336
|
end
|
|
233
337
|
|
|
234
338
|
it "should create a history track if changed attributes match tracked attributes" do
|
|
235
339
|
lambda {
|
|
236
|
-
|
|
237
|
-
|
|
340
|
+
post.update_attributes(:title => "Test2")
|
|
341
|
+
post.update_attributes(:title => "Test3")
|
|
238
342
|
}.should change(Tracker, :count).by(2)
|
|
239
343
|
end
|
|
240
344
|
|
|
241
345
|
it "should create a history track of version 2" do
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
346
|
+
post.update_attributes(:title => "Test2")
|
|
347
|
+
post.update_attributes(:title => "Test3")
|
|
348
|
+
post.history_tracks.where(:version => 2).first.should_not be_nil
|
|
245
349
|
end
|
|
246
350
|
|
|
247
351
|
it "should assign modified fields" do
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
352
|
+
post.update_attributes(:title => "Test2")
|
|
353
|
+
post.update_attributes(:title => "Test3")
|
|
354
|
+
post.history_tracks.where(:version => 2).first.modified.should == {
|
|
251
355
|
"title" => "Test3"
|
|
252
356
|
}
|
|
253
357
|
end
|
|
254
358
|
|
|
255
359
|
it "should assign original fields" do
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
360
|
+
post.update_attributes(:title => "Test2")
|
|
361
|
+
post.update_attributes(:title => "Test3")
|
|
362
|
+
post.history_tracks.where(:version => 2).first.original.should == {
|
|
259
363
|
"title" => "Test2"
|
|
260
364
|
}
|
|
261
365
|
end
|
|
262
366
|
|
|
263
367
|
|
|
264
368
|
it "should assign modifier" do
|
|
265
|
-
|
|
266
|
-
|
|
369
|
+
post.update_attributes(:title => "Another Test", :modifier => another_user)
|
|
370
|
+
post.history_tracks.last.modifier.should == another_user
|
|
267
371
|
end
|
|
268
372
|
end
|
|
269
373
|
|
|
270
374
|
describe "on update embedded 1..N (embeds_many)" do
|
|
271
375
|
it "should assign version on comment" do
|
|
272
|
-
|
|
273
|
-
|
|
376
|
+
comment.update_attributes(:title => "Test2")
|
|
377
|
+
comment.version.should == 2 # first track generated on creation
|
|
274
378
|
end
|
|
275
379
|
|
|
276
380
|
it "should create a history track of version 2" do
|
|
277
|
-
|
|
278
|
-
|
|
381
|
+
comment.update_attributes(:title => "Test2")
|
|
382
|
+
comment.history_tracks.where(:version => 2).first.should_not be_nil
|
|
279
383
|
end
|
|
280
384
|
|
|
281
385
|
it "should assign modified fields" do
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
"
|
|
386
|
+
comment.update_attributes(:t => "Test2")
|
|
387
|
+
comment.history_tracks.where(:version => 2).first.modified.should == {
|
|
388
|
+
"t" => "Test2"
|
|
285
389
|
}
|
|
286
390
|
end
|
|
287
391
|
|
|
288
392
|
it "should assign original fields" do
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
"
|
|
393
|
+
comment.update_attributes(:title => "Test2")
|
|
394
|
+
comment.history_tracks.where(:version => 2).first.original.should == {
|
|
395
|
+
"t" => "test"
|
|
292
396
|
}
|
|
293
397
|
end
|
|
294
398
|
|
|
295
399
|
it "should be possible to undo from parent" do
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
400
|
+
comment.update_attributes(:title => "Test 2")
|
|
401
|
+
user
|
|
402
|
+
post.history_tracks.last.undo!(user)
|
|
403
|
+
comment.reload
|
|
404
|
+
comment.title.should == "test"
|
|
300
405
|
end
|
|
301
406
|
|
|
302
407
|
it "should assign modifier" do
|
|
303
|
-
|
|
304
|
-
|
|
408
|
+
post.update_attributes(:title => "Another Test", :modifier => another_user)
|
|
409
|
+
post.history_tracks.last.modifier.should == another_user
|
|
305
410
|
end
|
|
306
411
|
end
|
|
307
412
|
|
|
308
413
|
describe "on update embedded 1..1 (embeds_one)" do
|
|
414
|
+
let(:section){ Section.new(:title => 'Technology') }
|
|
415
|
+
|
|
309
416
|
before(:each) do
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
@section = @post.section
|
|
417
|
+
post.section = section
|
|
418
|
+
post.save!
|
|
419
|
+
post.reload
|
|
420
|
+
section = post.section
|
|
315
421
|
end
|
|
316
422
|
|
|
317
423
|
it "should assign version on create section" do
|
|
318
|
-
|
|
424
|
+
section.version.should == 1
|
|
319
425
|
end
|
|
320
426
|
|
|
321
427
|
it "should assign version on section" do
|
|
322
|
-
|
|
323
|
-
|
|
428
|
+
section.update_attributes(:title => 'Technology 2')
|
|
429
|
+
section.version.should == 2 # first track generated on creation
|
|
324
430
|
end
|
|
325
431
|
|
|
326
432
|
it "should create a history track of version 2" do
|
|
327
|
-
|
|
328
|
-
|
|
433
|
+
section.update_attributes(:title => 'Technology 2')
|
|
434
|
+
section.history_tracks.where(:version => 2).first.should_not be_nil
|
|
329
435
|
end
|
|
330
436
|
|
|
331
437
|
it "should assign modified fields" do
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
"
|
|
438
|
+
section.update_attributes(:title => 'Technology 2')
|
|
439
|
+
section.history_tracks.where(:version => 2).first.modified.should == {
|
|
440
|
+
"t" => "Technology 2"
|
|
335
441
|
}
|
|
336
442
|
end
|
|
337
443
|
|
|
338
444
|
it "should assign original fields" do
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
"
|
|
445
|
+
section.update_attributes(:title => 'Technology 2')
|
|
446
|
+
section.history_tracks.where(:version => 2).first.original.should == {
|
|
447
|
+
"t" => "Technology"
|
|
342
448
|
}
|
|
343
449
|
end
|
|
344
450
|
|
|
345
451
|
it "should be possible to undo from parent" do
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
452
|
+
section.update_attributes(:title => 'Technology 2')
|
|
453
|
+
post.history_tracks.last.undo!(user)
|
|
454
|
+
section.reload
|
|
455
|
+
section.title.should == "Technology"
|
|
350
456
|
end
|
|
351
457
|
|
|
352
458
|
it "should assign modifier" do
|
|
353
|
-
|
|
354
|
-
|
|
459
|
+
section.update_attributes(:title => "Business", :modifier => another_user)
|
|
460
|
+
post.history_tracks.last.modifier.should == another_user
|
|
355
461
|
end
|
|
356
462
|
end
|
|
357
463
|
|
|
358
464
|
describe "on destroy embedded" do
|
|
359
465
|
it "should be possible to re-create destroyed embedded" do
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
466
|
+
comment.destroy
|
|
467
|
+
comment.history_tracks.last.undo!(user)
|
|
468
|
+
post.reload
|
|
469
|
+
post.comments.first.title.should == "test"
|
|
364
470
|
end
|
|
365
471
|
|
|
366
472
|
it "should be possible to re-create destroyed embedded from parent" do
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
473
|
+
comment.destroy
|
|
474
|
+
post.history_tracks.last.undo!(user)
|
|
475
|
+
post.reload
|
|
476
|
+
post.comments.first.title.should == "test"
|
|
371
477
|
end
|
|
372
478
|
|
|
373
479
|
it "should be possible to destroy after re-create embedded from parent" do
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
480
|
+
comment.destroy
|
|
481
|
+
post.history_tracks.last.undo!(user)
|
|
482
|
+
post.history_tracks.last.undo!(user)
|
|
483
|
+
post.reload
|
|
484
|
+
post.comments.count.should == 0
|
|
379
485
|
end
|
|
380
486
|
|
|
381
487
|
it "should be possible to create with redo after undo create embedded from parent" do
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
488
|
+
comment # initialize
|
|
489
|
+
post.comments.create!(:title => "The second one")
|
|
490
|
+
track = post.history_tracks.last
|
|
491
|
+
track.undo!(user)
|
|
492
|
+
track.redo!(user)
|
|
493
|
+
post.reload
|
|
494
|
+
post.comments.count.should == 2
|
|
388
495
|
end
|
|
389
496
|
end
|
|
390
497
|
|
|
391
498
|
describe "embedded with cascading callbacks" do
|
|
499
|
+
|
|
500
|
+
let(:tag_foo){ post.tags.create(:title => "foo", :updated_by => user) }
|
|
501
|
+
let(:tag_bar){ post.tags.create(:title => "bar") }
|
|
502
|
+
|
|
392
503
|
before(:each) do
|
|
393
504
|
Mongoid.instantiate_observers
|
|
394
|
-
Thread.current[:mongoid_history_sweeper_controller] =
|
|
395
|
-
|
|
396
|
-
@tag_foo = @post.tags.create(:title => "foo", :updated_by => @user)
|
|
397
|
-
@tag_bar = @post.tags.create(:title => "bar")
|
|
505
|
+
Thread.current[:mongoid_history_sweeper_controller] = Mongoid::History::Sweeper.instance
|
|
506
|
+
Mongoid::History::Sweeper.instance.stub(:current_user){ user }
|
|
398
507
|
end
|
|
399
508
|
|
|
400
|
-
it "should have cascaded the creation callbacks and set timestamps" do
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
509
|
+
# it "should have cascaded the creation callbacks and set timestamps" do
|
|
510
|
+
# tag_foo; tag_bar # initialize
|
|
511
|
+
# tag_foo.created_at.should_not be_nil
|
|
512
|
+
# tag_foo.updated_at.should_not be_nil
|
|
513
|
+
# end
|
|
404
514
|
|
|
405
515
|
it "should allow an update through the parent model" do
|
|
406
|
-
update_hash = { "post" => { "tags_attributes" => { "1234" => { "id" =>
|
|
407
|
-
|
|
408
|
-
|
|
516
|
+
update_hash = { "post" => { "tags_attributes" => { "1234" => { "id" => tag_bar.id, "title" => "baz" } } } }
|
|
517
|
+
post.update_attributes(update_hash["post"])
|
|
518
|
+
post.tags.last.title.should == "baz"
|
|
409
519
|
end
|
|
410
520
|
|
|
411
521
|
it "should be possible to destroy through parent model using canoncial _destroy macro" do
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
522
|
+
tag_foo; tag_bar # initialize
|
|
523
|
+
post.tags.count.should == 2
|
|
524
|
+
update_hash = { "post" => { "tags_attributes" => { "1234" => { "id" => tag_bar.id, "title" => "baz", "_destroy" => "true"} } } }
|
|
525
|
+
post.update_attributes(update_hash["post"])
|
|
526
|
+
post.tags.count.should == 1
|
|
527
|
+
post.history_tracks.last.action.should == "destroy"
|
|
417
528
|
end
|
|
418
529
|
|
|
419
530
|
it "should write relationship name for association_chain hiearchy instead of class name when using _destroy macro" do
|
|
420
|
-
update_hash = {"tags_attributes" => { "1234" => { "id" =>
|
|
421
|
-
|
|
531
|
+
update_hash = {"tags_attributes" => { "1234" => { "id" => tag_foo.id, "_destroy" => "1"} } }
|
|
532
|
+
post.update_attributes(update_hash)
|
|
422
533
|
|
|
423
534
|
# historically this would have evaluated to 'Tags' and an error would be thrown
|
|
424
535
|
# on any call that walked up the association_chain, e.g. 'trackable'
|
|
425
|
-
|
|
426
|
-
lambda{
|
|
536
|
+
tag_foo.history_tracks.last.association_chain.last["name"].should == "tags"
|
|
537
|
+
lambda{ tag_foo.history_tracks.last.trackable }.should_not raise_error
|
|
427
538
|
end
|
|
428
539
|
|
|
429
540
|
it "should save modifier" do
|
|
430
|
-
|
|
431
|
-
|
|
541
|
+
Thread.current[:mongoid_history_sweeper_controller].current_user.should eq user
|
|
542
|
+
tag_foo.history_tracks.last.modifier.should eq user
|
|
543
|
+
tag_bar.history_tracks.last.modifier.should eq user
|
|
432
544
|
end
|
|
433
545
|
end
|
|
434
546
|
|
|
435
547
|
describe "non-embedded" do
|
|
436
548
|
it "should undo changes" do
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
549
|
+
post.update_attributes(:title => "Test2")
|
|
550
|
+
post.history_tracks.where(:version => 1).last.undo!(user)
|
|
551
|
+
post.reload
|
|
552
|
+
post.title.should == "Test"
|
|
441
553
|
end
|
|
442
554
|
|
|
443
555
|
it "should undo destruction" do
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
Post.find(
|
|
556
|
+
post.destroy
|
|
557
|
+
post.history_tracks.where(:version => 1).last.undo!(user)
|
|
558
|
+
Post.find(post.id).title.should == "Test"
|
|
447
559
|
end
|
|
448
560
|
|
|
449
561
|
it "should create a new history track after undo" do
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
562
|
+
comment # initialize
|
|
563
|
+
post.update_attributes(:title => "Test2")
|
|
564
|
+
post.history_tracks.last.undo!(user)
|
|
565
|
+
post.reload
|
|
566
|
+
post.history_tracks.count.should == 3
|
|
454
567
|
end
|
|
455
568
|
|
|
456
|
-
it "should assign
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
569
|
+
it "should assign user as the modifier of the newly created history track" do
|
|
570
|
+
post.update_attributes(:title => "Test2")
|
|
571
|
+
post.history_tracks.where(:version => 1).last.undo!(user)
|
|
572
|
+
post.reload
|
|
573
|
+
post.history_tracks.where(:version => 2).last.modifier.should == user
|
|
461
574
|
end
|
|
462
575
|
|
|
463
576
|
it "should stay the same after undo and redo" do
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
577
|
+
post.update_attributes(:title => "Test2")
|
|
578
|
+
track = post.history_tracks.last
|
|
579
|
+
track.undo!(user)
|
|
580
|
+
track.redo!(user)
|
|
581
|
+
post2 = Post.where(:_id => post.id).first
|
|
469
582
|
|
|
470
|
-
|
|
583
|
+
post.title.should == post2.title
|
|
471
584
|
end
|
|
472
585
|
|
|
473
586
|
it "should be destroyed after undo and redo" do
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
Post.where(:_id =>
|
|
587
|
+
post.destroy
|
|
588
|
+
track = post.history_tracks.where(:version => 1).last
|
|
589
|
+
track.undo!(user)
|
|
590
|
+
track.redo!(user)
|
|
591
|
+
Post.where(:_id => post.id).first.should == nil
|
|
479
592
|
end
|
|
480
593
|
end
|
|
481
594
|
|
|
482
595
|
describe "embedded" do
|
|
483
596
|
it "should undo changes" do
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
@post.reload
|
|
489
|
-
@comment = @post.comments.first
|
|
490
|
-
@comment.title.should == "test"
|
|
597
|
+
comment.update_attributes(:title => "Test2")
|
|
598
|
+
comment.history_tracks.where(:version => 2).first.undo!(user)
|
|
599
|
+
comment.reload
|
|
600
|
+
comment.title.should == "test"
|
|
491
601
|
end
|
|
492
602
|
|
|
493
603
|
it "should create a new history track after undo" do
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
@comment.history_tracks.count.should == 3
|
|
604
|
+
comment.update_attributes(:title => "Test2")
|
|
605
|
+
comment.history_tracks.where(:version => 2).first.undo!(user)
|
|
606
|
+
comment.reload
|
|
607
|
+
comment.history_tracks.count.should == 3
|
|
499
608
|
end
|
|
500
609
|
|
|
501
|
-
it "should assign
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
@comment.history_tracks.where(:version => 3).first.modifier.should == @user
|
|
610
|
+
it "should assign user as the modifier of the newly created history track" do
|
|
611
|
+
comment.update_attributes(:title => "Test2")
|
|
612
|
+
comment.history_tracks.where(:version => 2).first.undo!(user)
|
|
613
|
+
comment.reload
|
|
614
|
+
comment.history_tracks.where(:version => 3).first.modifier.should == user
|
|
507
615
|
end
|
|
508
616
|
|
|
509
617
|
it "should stay the same after undo and redo" do
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
@comment.title.should == @comment2.title
|
|
618
|
+
comment.update_attributes(:title => "Test2")
|
|
619
|
+
track = comment.history_tracks.where(:version => 2).first
|
|
620
|
+
track.undo!(user)
|
|
621
|
+
track.redo!(user)
|
|
622
|
+
comment.reload
|
|
623
|
+
comment.title.should == "Test2"
|
|
518
624
|
end
|
|
519
625
|
end
|
|
520
626
|
|
|
521
627
|
describe "trackables" do
|
|
522
628
|
before :each do
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
629
|
+
comment.update_attributes(:title => "Test2") # version == 2
|
|
630
|
+
comment.update_attributes(:title => "Test3") # version == 3
|
|
631
|
+
comment.update_attributes(:title => "Test4") # version == 4
|
|
526
632
|
end
|
|
527
633
|
|
|
528
634
|
describe "undo" do
|
|
529
635
|
it "should recognize :from, :to options" do
|
|
530
|
-
|
|
531
|
-
|
|
636
|
+
comment.undo! user, :from => 4, :to => 2
|
|
637
|
+
comment.title.should == "test"
|
|
532
638
|
end
|
|
533
639
|
|
|
534
640
|
it "should recognize parameter as version number" do
|
|
535
|
-
|
|
536
|
-
|
|
641
|
+
comment.undo! user, 3
|
|
642
|
+
comment.title.should == "Test2"
|
|
537
643
|
end
|
|
538
644
|
|
|
539
645
|
it "should undo last version when no parameter is specified" do
|
|
540
|
-
|
|
541
|
-
|
|
646
|
+
comment.undo! user
|
|
647
|
+
comment.title.should == "Test3"
|
|
542
648
|
end
|
|
543
649
|
|
|
544
650
|
it "should recognize :last options" do
|
|
545
|
-
|
|
546
|
-
|
|
651
|
+
comment.undo! user, :last => 2
|
|
652
|
+
comment.title.should == "Test2"
|
|
547
653
|
end
|
|
548
654
|
|
|
549
655
|
end
|
|
550
656
|
|
|
551
657
|
describe "redo" do
|
|
552
658
|
before :each do
|
|
553
|
-
|
|
659
|
+
comment.update_attributes(:title => "Test5")
|
|
554
660
|
end
|
|
555
661
|
|
|
556
662
|
it "should recognize :from, :to options" do
|
|
557
|
-
|
|
558
|
-
|
|
663
|
+
comment.redo! user, :from => 2, :to => 4
|
|
664
|
+
comment.title.should == "Test4"
|
|
559
665
|
end
|
|
560
666
|
|
|
561
667
|
it "should recognize parameter as version number" do
|
|
562
|
-
|
|
563
|
-
|
|
668
|
+
comment.redo! user, 2
|
|
669
|
+
comment.title.should == "Test2"
|
|
564
670
|
end
|
|
565
671
|
|
|
566
672
|
it "should redo last version when no parameter is specified" do
|
|
567
|
-
|
|
568
|
-
|
|
673
|
+
comment.redo! user
|
|
674
|
+
comment.title.should == "Test5"
|
|
569
675
|
end
|
|
570
676
|
|
|
571
677
|
it "should recognize :last options" do
|
|
572
|
-
|
|
573
|
-
|
|
678
|
+
comment.redo! user, :last => 1
|
|
679
|
+
comment.title.should == "Test5"
|
|
574
680
|
end
|
|
575
681
|
|
|
576
682
|
end
|
|
@@ -593,18 +699,56 @@ describe Mongoid::History do
|
|
|
593
699
|
|
|
594
700
|
track = sausage.history_tracks.last
|
|
595
701
|
|
|
596
|
-
track.undo!
|
|
702
|
+
track.undo! user
|
|
597
703
|
sausage.reload.flavour.should == "Apple"
|
|
598
704
|
|
|
599
|
-
track.redo!
|
|
705
|
+
track.redo! user
|
|
600
706
|
sausage.reload.flavour.should == "Guinness"
|
|
601
707
|
|
|
602
708
|
sausage.destroy
|
|
603
709
|
sausage.history_tracks.last.action.should == "destroy"
|
|
604
|
-
sausage.history_tracks.last.undo!
|
|
710
|
+
sausage.history_tracks.last.undo! user
|
|
605
711
|
sausage.reload.flavour.should == "Guinness"
|
|
606
712
|
end
|
|
607
713
|
end
|
|
608
714
|
end
|
|
715
|
+
|
|
716
|
+
describe "embedded with a polymorphic trackable" do
|
|
717
|
+
let(:foo){ Foo.new(:title => 'a title', :body => 'a body') }
|
|
718
|
+
before :each do
|
|
719
|
+
post.comments << foo
|
|
720
|
+
post.save
|
|
721
|
+
end
|
|
722
|
+
it "should assign interface name in association chain" do
|
|
723
|
+
foo.update_attribute(:body, 'a changed body')
|
|
724
|
+
expected_root = {"name" => "Post", "id" => post.id}
|
|
725
|
+
expected_node = {"name" => "coms", "id" => foo.id}
|
|
726
|
+
foo.history_tracks.first.association_chain.should == [expected_root, expected_node]
|
|
727
|
+
end
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
describe "#trackable_parent_class" do
|
|
731
|
+
context "a non-embedded model" do
|
|
732
|
+
it "should return the trackable parent class" do
|
|
733
|
+
tag.history_tracks.first.trackable_parent_class.should == Tag
|
|
734
|
+
end
|
|
735
|
+
it "should return the parent class even if the trackable is deleted" do
|
|
736
|
+
tracker = tag.history_tracks.first
|
|
737
|
+
tag.destroy
|
|
738
|
+
tracker.trackable_parent_class.should == Tag
|
|
739
|
+
end
|
|
740
|
+
end
|
|
741
|
+
context "an embedded model" do
|
|
742
|
+
it "should return the trackable parent class" do
|
|
743
|
+
comment.update_attributes(title: "Foo")
|
|
744
|
+
comment.history_tracks.first.trackable_parent_class.should == Post
|
|
745
|
+
end
|
|
746
|
+
it "should return the parent class even if the trackable is deleted" do
|
|
747
|
+
tracker = comment.history_tracks.first
|
|
748
|
+
comment.destroy
|
|
749
|
+
tracker.trackable_parent_class.should == Post
|
|
750
|
+
end
|
|
751
|
+
end
|
|
752
|
+
end
|
|
609
753
|
end
|
|
610
754
|
end
|