mongoid-audit 0.3.2 → 1.0.0.alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.coveralls.yml DELETED
@@ -1 +0,0 @@
1
- service_name: travis-ci
data/.rspec DELETED
@@ -1 +0,0 @@
1
- --color
data/.travis.yml DELETED
@@ -1,15 +0,0 @@
1
- services: mongodb
2
-
3
- notifications:
4
- email: false
5
-
6
- language: ruby
7
- rvm:
8
- - 1.9.3
9
- - 2.0.0
10
- - jruby-19mode
11
-
12
- gemfile:
13
- - Gemfile
14
- - gemfiles/mongoid-4.0.gemfile
15
-
@@ -1,172 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid
3
-
4
- # Observer classes respond to life cycle callbacks to implement trigger-like
5
- # behavior outside the original class. This is a great way to reduce the
6
- # clutter that normally comes when the model class is burdened with
7
- # functionality that doesn't pertain to the core responsibility of the
8
- # class. Mongoid's observers work similar to ActiveRecord's. Example:
9
- #
10
- # class CommentObserver < Mongoid::Observer
11
- # def after_save(comment)
12
- # Notifications.comment(
13
- # "admin@do.com", "New comment was posted", comment
14
- # ).deliver
15
- # end
16
- # end
17
- #
18
- # This Observer sends an email when a Comment#save is finished.
19
- #
20
- # class ContactObserver < Mongoid::Observer
21
- # def after_create(contact)
22
- # contact.logger.info('New contact added!')
23
- # end
24
- #
25
- # def after_destroy(contact)
26
- # contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
27
- # end
28
- # end
29
- #
30
- # This Observer uses logger to log when specific callbacks are triggered.
31
- #
32
- # == Observing a class that can't be inferred
33
- #
34
- # Observers will by default be mapped to the class with which they share a
35
- # name. So CommentObserver will be tied to observing Comment,
36
- # ProductManagerObserver to ProductManager, and so on. If you want to
37
- # name your observer differently than the class you're interested in
38
- # observing, you can use the Observer.observe class method which takes
39
- # either the concrete class (Product) or a symbol for that class (:product):
40
- #
41
- # class AuditObserver < Mongoid::Observer
42
- # observe :account
43
- #
44
- # def after_update(account)
45
- # AuditTrail.new(account, "UPDATED")
46
- # end
47
- # end
48
- #
49
- # If the audit observer needs to watch more than one kind of object,
50
- # this can be specified with multiple arguments:
51
- #
52
- # class AuditObserver < Mongoid::Observer
53
- # observe :account, :balance
54
- #
55
- # def after_update(record)
56
- # AuditTrail.new(record, "UPDATED")
57
- # end
58
- # end
59
- #
60
- # The AuditObserver will now act on both updates to Account and Balance
61
- # by treating them both as records.
62
- #
63
- # == Available callback methods
64
- #
65
- # * after_initialize
66
- # * before_validation
67
- # * after_validation
68
- # * before_create
69
- # * around_create
70
- # * after_create
71
- # * before_update
72
- # * around_update
73
- # * after_update
74
- # * before_upsert
75
- # * around_upsert
76
- # * after_upsert
77
- # * before_save
78
- # * around_save
79
- # * after_save
80
- # * before_destroy
81
- # * around_destroy
82
- # * after_destroy
83
- #
84
- # == Storing Observers in Rails
85
- #
86
- # If you're using Mongoid within Rails, observer classes are usually stored
87
- # in +app/models+ with the naming convention of +app/models/audit_observer.rb+.
88
- #
89
- # == Configuration
90
- #
91
- # In order to activate an observer, list it in the +config.mongoid.observers+
92
- # configuration setting in your +config/application.rb+ file.
93
- #
94
- # config.mongoid.observers = :comment_observer, :signup_observer
95
- #
96
- # Observers will not be invoked unless you define them in your
97
- # application configuration.
98
- #
99
- # == Loading
100
- #
101
- # Observers register themselves with the model class that they observe,
102
- # since it is the class that notifies them of events when they occur.
103
- # As a side-effect, when an observer is loaded, its corresponding model
104
- # class is loaded.
105
- #
106
- # Observers are loaded after the application initializers, so that
107
- # observed models can make use of extensions. If by any chance you are
108
- # using observed models in the initialization, you can
109
- # still load their observers by calling +ModelObserver.instance+ before.
110
- # Observers are singletons and that call instantiates and registers them.
111
- class Observer < ActiveModel::Observer
112
-
113
- private
114
-
115
- # Adds the specified observer to the class.
116
- #
117
- # @example Add the observer.
118
- # observer.add_observer!(Document)
119
- #
120
- # @param [ Class ] klass The child observer to add.
121
- #
122
- # @since 2.0.0.rc.8
123
- def add_observer!(klass)
124
- super
125
- define_callbacks(klass)
126
- end
127
-
128
- # Defines all the callbacks for each observer of the model.
129
- #
130
- # @example Define all the callbacks.
131
- # observer.define_callbacks(Document)
132
- #
133
- # @param [ Class ] klass The model to define them on.
134
- #
135
- # @since 2.0.0.rc.8
136
- def define_callbacks(klass)
137
- observer = self
138
- observer_name = observer.class.name.underscore.gsub('/', '__')
139
- Mongoid::Callbacks.observables.each do |callback|
140
- next unless respond_to?(callback)
141
- callback_meth = :"_notify_#{observer_name}_for_#{callback}"
142
- unless klass.respond_to?(callback_meth)
143
- klass.send(:define_method, callback_meth) do |&block|
144
- if value = observer.update(callback, self, &block)
145
- value
146
- else
147
- block.call if block
148
- end
149
- end
150
- klass.send(callback, callback_meth)
151
- end
152
- end
153
- self
154
- end
155
-
156
- # Are the observers disabled for the object?
157
- #
158
- # @api private
159
- #
160
- # @example If the observer disabled?
161
- # Observer.disabled_for(band)
162
- #
163
- # @param [ Document ] object The model instance.
164
- #
165
- # @return [ true, false ] If the observer is disabled.
166
- def disabled_for?(object)
167
- klass = object.class
168
- return false unless klass.respond_to?(:observers)
169
- klass.observers.disabled_for?(self) || Mongoid.observers.disabled_for?(self)
170
- end
171
- end
172
- end
@@ -1,46 +0,0 @@
1
- module Mongoid::Audit
2
- class Sweeper < ActiveModel::Observer
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
10
-
11
- def self.observed_classes
12
- [ Mongoid::Audit.tracker_class ]
13
- end
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.
19
- def before(controller)
20
- self.controller = controller
21
- true
22
- end
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
28
- def after(controller)
29
- self.controller = nil
30
- end
31
-
32
- def before_create(track)
33
- track.modifier = audit_current_user if track.modifier.nil?
34
- end
35
-
36
- def audit_current_user
37
- if controller.nil?
38
- nil
39
- elsif controller.respond_to?(Mongoid::Audit.current_user_method, true)
40
- controller.send Mongoid::Audit.current_user_method
41
- else
42
- nil
43
- end
44
- end
45
- end
46
- end
@@ -1,170 +0,0 @@
1
- module Mongoid::Audit
2
- module Tracker
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- include Mongoid::Document
7
- include Mongoid::Timestamps
8
- include ActiveModel::Observing
9
-
10
- attr_writer :trackable
11
-
12
- field :association_chain, :type => Array, :default => []
13
- field :modified, :type => Hash
14
- field :original, :type => Hash
15
- field :version, :type => Integer
16
- field :action, :type => String
17
- field :scope, :type => String
18
- belongs_to :modifier, :class_name => Mongoid::Audit.modifier_class_name
19
-
20
- Mongoid::Audit.tracker_class_name = self.name.tableize.singularize.to_sym
21
-
22
- index({'association_chain.name' => 1, 'association_chain.id' => 1})
23
-
24
- Mongoid::Interceptable::CALLBACKS.each do |callback|
25
- callback_method = :"_notify_#{Mongoid::Audit.tracker_class_name}_#{callback}"
26
- module_eval <<-RUBY, __FILE__, __LINE__+1
27
- #{callback} #{callback_method.inspect}
28
- def #{callback_method}(&block)
29
- if "#{callback}".start_with?( 'around_' )
30
- notify_observers(#{callback.inspect}, &block)
31
- yield
32
- else
33
- notify_observers(#{callback.inspect}, &block)
34
- true
35
- end
36
- end
37
- private #{callback_method.inspect}
38
- RUBY
39
- end
40
-
41
- end
42
-
43
- def undo!(modifier)
44
- if action.to_sym == :destroy
45
- re_create
46
- elsif action.to_sym == :create
47
- re_destroy
48
- else
49
- trackable.update_attributes!(undo_attr(modifier))
50
- end
51
- end
52
-
53
- def redo!(modifier)
54
- if action.to_sym == :destroy
55
- re_destroy
56
- elsif action.to_sym == :create
57
- re_create
58
- else
59
- trackable.update_attributes!(redo_attr(modifier))
60
- end
61
- end
62
-
63
- def undo_attr(modifier)
64
- undo_hash = affected.easy_unmerge(modified)
65
- undo_hash.easy_merge!(original)
66
- modifier_field = trackable.history_trackable_options[:modifier_field]
67
- undo_hash[modifier_field] = modifier
68
- undo_hash
69
- end
70
-
71
- def redo_attr(modifier)
72
- redo_hash = affected.easy_unmerge(original)
73
- redo_hash.easy_merge!(modified)
74
- modifier_field = trackable.history_trackable_options[:modifier_field]
75
- redo_hash[modifier_field] = modifier
76
- redo_hash
77
- end
78
-
79
- def trackable_root
80
- @trackable_root ||= trackable_parents_and_trackable.first
81
- end
82
-
83
- def trackable
84
- @trackable ||= trackable_parents_and_trackable.last
85
- end
86
-
87
- def trackable_parents
88
- @trackable_parents ||= trackable_parents_and_trackable[0, -1]
89
- end
90
-
91
- def trackable_parent
92
- @trackable_parent ||= trackable_parents_and_trackable[-2]
93
- end
94
-
95
- def affected
96
- @affected ||= (modified.keys | original.keys).inject({}){ |h,k| h[k] =
97
- trackable ? trackable.attributes[k] : modified[k]; h}
98
- end
99
-
100
- private
101
-
102
- def re_create
103
- association_chain.length > 1 ? create_on_parent : create_standalone
104
- end
105
-
106
- def re_destroy
107
- trackable.destroy
108
- end
109
-
110
- def create_standalone
111
- class_name = association_chain.first["name"]
112
- restored = class_name.constantize.new(modified)
113
- restored.id = modified["_id"]
114
- restored.save!
115
- end
116
-
117
- def create_on_parent
118
- name = association_chain.last["name"]
119
- if embeds_one?(trackable_parent, name)
120
- trackable_parent.send("create_#{name}!", modified)
121
- elsif embeds_many?(trackable_parent, name)
122
- trackable_parent.send(name).create!(modified)
123
- else
124
- raise "This should never happen. Please report bug!"
125
- end
126
- end
127
-
128
- def trackable_parents_and_trackable
129
- @trackable_parents_and_trackable ||= traverse_association_chain
130
- end
131
-
132
- def relation_of(doc, name)
133
- meta = doc.reflect_on_association(name)
134
- meta ? meta.relation : nil
135
- end
136
-
137
- def embeds_one?(doc, name)
138
- relation_of(doc, name) == Mongoid::Relations::Embedded::One
139
- end
140
-
141
- def embeds_many?(doc, name)
142
- relation_of(doc, name) == Mongoid::Relations::Embedded::Many
143
- end
144
-
145
- def traverse_association_chain
146
- chain = association_chain.dup
147
- doc = nil
148
- documents = []
149
-
150
- begin
151
- node = chain.shift
152
- name = node['name']
153
-
154
- doc = if doc.nil?
155
- # root association. First element of the association chain
156
- klass = name.classify.constantize
157
- klass.where(:_id => node['id']).first
158
- elsif embeds_one?(doc, name)
159
- doc.send(name)
160
- elsif embeds_many?(doc, name)
161
- doc.send(name).where(:_id => node['id']).first
162
- else
163
- raise "This should never happen. Please report bug."
164
- end
165
- documents << doc
166
- end while( !chain.empty? )
167
- documents
168
- end
169
- end
170
- end
@@ -1,666 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
-
3
- describe Mongoid::Audit do
4
- before :all do
5
- class Post
6
- include Mongoid::Document
7
- include Mongoid::Timestamps
8
- include Mongoid::Audit::Trackable
9
-
10
- field :title
11
- field :body
12
- field :rating
13
- field :views
14
-
15
- embeds_many :comments
16
- embeds_one :section
17
- embeds_many :tags
18
-
19
- accepts_nested_attributes_for :tags, :allow_destroy => true
20
-
21
- track_history :on => [:title, :body], :track_destroy => true
22
- end
23
-
24
- class Comment
25
- include Mongoid::Document
26
- include Mongoid::Timestamps
27
- include Mongoid::Audit::Trackable
28
-
29
- field :title
30
- field :body
31
- embedded_in :post
32
- track_history :on => [:title, :body], :scope => :post, :track_create => true, :track_destroy => true
33
- end
34
-
35
- class Section
36
- include Mongoid::Document
37
- include Mongoid::Timestamps
38
- include Mongoid::Audit::Trackable
39
-
40
- field :title
41
- embedded_in :post
42
- track_history :on => [:title], :scope => :post, :track_create => true, :track_destroy => true
43
- end
44
-
45
- class User
46
- include Mongoid::Document
47
- include Mongoid::Timestamps
48
- include Mongoid::Audit::Trackable
49
-
50
- has_and_belongs_to_many :own_restaurants, class_name: 'Restaurant', inverse_of: :owners
51
-
52
- field :email
53
- field :name
54
- track_history :except => [:email]
55
- end
56
-
57
- class Tag
58
- include Mongoid::Document
59
- include Mongoid::Timestamps
60
- include Mongoid::Audit::Trackable
61
-
62
- belongs_to :updated_by, :class_name => "User"
63
-
64
- field :title
65
- track_history :on => [:title], :scope => :post, :track_create => true, :track_destroy => true, :modifier_field => :updated_by
66
- end
67
-
68
- class Restaurant
69
- include Mongoid::Document
70
- include Mongoid::Audit::Trackable
71
-
72
- has_and_belongs_to_many :owners, class_name: 'User', inverse_of: nil
73
-
74
- field :title
75
-
76
- track_history :on => [:title], :track_create => true, :track_destroy => true
77
- end
78
- end
79
-
80
- before :each do
81
- @user = User.create(:name => "Aaron", :email => "aaron@randomemail.com")
82
- @another_user = User.create(:name => "Another Guy", :email => "anotherguy@randomemail.com")
83
- @post = Post.create(:title => "Test", :body => "Post", :modifier => @user, :views => 100)
84
- @comment = @post.comments.create(:title => "test", :body => "comment", :modifier => @user)
85
- end
86
-
87
- describe "track" do
88
- describe "on creation" do
89
- it "should have one history track in comment" do
90
- @comment.history_tracks.count.should == 1
91
- end
92
-
93
- it "should assign title and body on modified" do
94
- @comment.history_tracks.first.modified.should == {'title' => "test", 'body' => "comment"}
95
- end
96
-
97
- it "should not assign title and body on original" do
98
- @comment.history_tracks.first.original.should == {}
99
- end
100
-
101
- it "should assign modifier" do
102
- @comment.history_tracks.first.modifier.should == @user
103
- end
104
-
105
- it "should assign version" do
106
- @comment.history_tracks.first.version.should == 1
107
- end
108
-
109
- it "should assign scope" do
110
- @comment.history_tracks.first.scope.should == "post"
111
- end
112
-
113
- it "should assign method" do
114
- @comment.history_tracks.first.action.should == "create"
115
- end
116
-
117
- it "should assign association_chain" do
118
- expected = [
119
- {'id' => @post.id, 'name' => "Post"},
120
- {'id' => @comment.id, 'name' => "comments"}
121
- ]
122
- @comment.history_tracks.first.association_chain.should == expected
123
- end
124
- end
125
-
126
- describe "on destruction" do
127
- it "should have two history track records in post" do
128
- lambda {
129
- @post.destroy
130
- }.should change(HistoryTracker, :count).by(1)
131
- end
132
-
133
- it "should assign destroy on track record" do
134
- @post.destroy
135
- @post.history_tracks.last.action.should == "destroy"
136
- end
137
-
138
- it "should return affected attributes from track record" do
139
- @post.destroy
140
- @post.history_tracks.last.affected["title"].should == "Test"
141
- end
142
- end
143
-
144
- describe "on update non-embedded" do
145
- it "should create a history track if changed attributes match tracked attributes" do
146
- lambda {
147
- @post.update_attributes(:title => "Another Test")
148
- }.should change(HistoryTracker, :count).by(1)
149
- end
150
-
151
- it "should not create a history track if changed attributes do not match tracked attributes" do
152
- lambda {
153
- @post.update_attributes(:rating => "untracked")
154
- }.should change(HistoryTracker, :count).by(0)
155
- end
156
-
157
- it "should assign modified fields" do
158
- @post.update_attributes(:title => "Another Test")
159
- @post.history_tracks.last.modified.should == {
160
- "title" => "Another Test"
161
- }
162
- end
163
-
164
- it "should assign method field" do
165
- @post.update_attributes(:title => "Another Test")
166
- @post.history_tracks.last.action.should == "update"
167
- end
168
-
169
- it "should assign original fields" do
170
- @post.update_attributes(:title => "Another Test")
171
- @post.history_tracks.last.original.should == {
172
- "title" => "Test"
173
- }
174
- end
175
-
176
- it "should assign modifier" do
177
- @post.update_attributes(:title => "Another Test")
178
- @post.history_tracks.first.modifier.should == @user
179
- end
180
-
181
- it "should assign version on history tracks" do
182
- @post.update_attributes(:title => "Another Test")
183
- @post.history_tracks.first.version.should == 1
184
- end
185
-
186
- it "should assign version on post" do
187
- @post.update_attributes(:title => "Another Test")
188
- @post.version.should == 1
189
- end
190
-
191
- it "should assign scope" do
192
- @post.update_attributes(:title => "Another Test")
193
- @post.history_tracks.first.scope.should == "post"
194
- end
195
-
196
- it "should assign association_chain" do
197
- @post.update_attributes(:title => "Another Test")
198
- @post.history_tracks.last.association_chain.should == [{'id' => @post.id, 'name' => "Post"}]
199
- end
200
-
201
- it "should exclude defined options" do
202
- @user.update_attributes(:name => "Aaron2", :email => "aaronsnewemail@randomemail.com")
203
- @user.history_tracks.first.modified.keys.should include "name"
204
- @user.history_tracks.first.modified.keys.should_not include "email"
205
- end
206
- end
207
-
208
- describe "on update non-embedded twice" do
209
- it "should assign version on post" do
210
- @post.update_attributes(:title => "Test2")
211
- @post.update_attributes(:title => "Test3")
212
- @post.version.should == 2
213
- end
214
-
215
- it "should create a history track if changed attributes match tracked attributes" do
216
- lambda {
217
- @post.update_attributes(:title => "Test2")
218
- @post.update_attributes(:title => "Test3")
219
- }.should change(HistoryTracker, :count).by(2)
220
- end
221
-
222
- it "should create a history track of version 2" do
223
- @post.update_attributes(:title => "Test2")
224
- @post.update_attributes(:title => "Test3")
225
- @post.history_tracks.where(:version => 2).first.should_not be_nil
226
- end
227
-
228
- it "should assign modified fields" do
229
- @post.update_attributes(:title => "Test2")
230
- @post.update_attributes(:title => "Test3")
231
- @post.history_tracks.where(:version => 2).first.modified.should == {
232
- "title" => "Test3"
233
- }
234
- end
235
-
236
- it "should assign original fields" do
237
- @post.update_attributes(:title => "Test2")
238
- @post.update_attributes(:title => "Test3")
239
- @post.history_tracks.where(:version => 2).first.original.should == {
240
- "title" => "Test2"
241
- }
242
- end
243
-
244
-
245
- it "should assign modifier" do
246
- @post.update_attributes(:title => "Another Test", :modifier => @another_user)
247
- @post.history_tracks.last.modifier.should == @another_user
248
- end
249
- end
250
-
251
- describe "on update embedded 1..N (embeds_many)" do
252
- it "should assign version on comment" do
253
- @comment.update_attributes(:title => "Test2")
254
- @comment.version.should == 2 # first track generated on creation
255
- end
256
-
257
- it "should create a history track of version 2" do
258
- @comment.update_attributes(:title => "Test2")
259
- @comment.history_tracks.where(:version => 2).first.should_not be_nil
260
- end
261
-
262
- it "should assign modified fields" do
263
- @comment.update_attributes(:title => "Test2")
264
- @comment.history_tracks.where(:version => 2).first.modified.should == {
265
- "title" => "Test2"
266
- }
267
- end
268
-
269
- it "should assign original fields" do
270
- @comment.update_attributes(:title => "Test2")
271
- @comment.history_tracks.where(:version => 2).first.original.should == {
272
- "title" => "test"
273
- }
274
- end
275
-
276
- it "should be possible to undo from parent" do
277
- @comment.update_attributes(:title => "Test 2")
278
- @post.history_tracks.last.undo!(@user)
279
- @comment.reload
280
- @comment.title.should == "test"
281
- end
282
-
283
- it "should assign modifier" do
284
- @post.update_attributes(:title => "Another Test", :modifier => @another_user)
285
- @post.history_tracks.last.modifier.should == @another_user
286
- end
287
- end
288
-
289
- describe "on update embedded 1..1 (embeds_one)" do
290
- before(:each) do
291
- @section = Section.new(:title => 'Technology')
292
- @post.section = @section
293
- @post.save!
294
- @post.reload
295
- @section = @post.section
296
- end
297
-
298
- it "should assign version on create section" do
299
- @section.version.should == 1
300
- end
301
-
302
- it "should assign version on section" do
303
- @section.update_attributes(:title => 'Technology 2')
304
- @section.version.should == 2 # first track generated on creation
305
- end
306
-
307
- it "should create a history track of version 2" do
308
- @section.update_attributes(:title => 'Technology 2')
309
- @section.history_tracks.where(:version => 2).first.should_not be_nil
310
- end
311
-
312
- it "should assign modified fields" do
313
- @section.update_attributes(:title => 'Technology 2')
314
- @section.history_tracks.where(:version => 2).first.modified.should == {
315
- "title" => "Technology 2"
316
- }
317
- end
318
-
319
- it "should assign original fields" do
320
- @section.update_attributes(:title => 'Technology 2')
321
- @section.history_tracks.where(:version => 2).first.original.should == {
322
- "title" => "Technology"
323
- }
324
- end
325
-
326
- it "should be possible to undo from parent" do
327
- @section.update_attributes(:title => 'Technology 2')
328
- @post.history_tracks.last.undo!(@user)
329
- @section.reload
330
- @section.title.should == "Technology"
331
- end
332
-
333
- it "should assign modifier" do
334
- @section.update_attributes(:title => "Business", :modifier => @another_user)
335
- @post.reload
336
- # just workaround strange mongoid #last bug for now
337
- @post.history_tracks.to_a[-1].modifier.should == @another_user
338
- end
339
-
340
- it "should update modifier" do
341
- @section.update_attributes(:title => 'Technology 2', :modifier => @user)
342
- @section.update_attributes(:title => "Business", :modifier => @another_user)
343
- @post.reload
344
- @post.history_tracks.last.modifier.should == @another_user
345
- end
346
- end
347
-
348
- describe "on destroy embedded" do
349
- it "should be possible to re-create destroyed embedded" do
350
- @comment.destroy
351
- @comment.history_tracks.last.undo!(@user)
352
- @post.reload
353
- @post.comments.first.title.should == "test"
354
- end
355
-
356
- it "should be possible to re-create destroyed embedded from parent" do
357
- @comment.destroy
358
- @post.history_tracks.last.undo!(@user)
359
- @post.reload
360
- @post.comments.first.title.should == "test"
361
- end
362
-
363
- it "should be possible to destroy after re-create embedded from parent" do
364
- @comment.destroy
365
- @post.history_tracks.last.undo!(@user)
366
- @post.history_tracks.last.undo!(@user)
367
- @post.reload
368
- @post.comments.count.should == 0
369
- end
370
-
371
- it "should be possible to create with redo after undo create embedded from parent" do
372
- @post.comments.create!(:title => "The second one")
373
- @track = @post.history_tracks.last
374
- @track.undo!(@user)
375
- @track.redo!(@user)
376
- @post.reload
377
- @post.comments.count.should == 2
378
- end
379
- end
380
-
381
- describe "embedded with cascading callbacks" do
382
- before(:each) do
383
- Thread.current[:mongoid_history_sweeper_controller] = self
384
- # self.stub!(:current_user).and_return @user
385
- allow(self).to receive(:current_user).and_return(@user)
386
- @tag_foo = @post.tags.create(:title => "foo", :updated_by => @user)
387
- @tag_bar = @post.tags.create(:title => "bar")
388
- end
389
-
390
- after(:each) do
391
- Thread.current[:mongoid_history_sweeper_controller] = nil
392
- end
393
-
394
- it "should have cascaded the creation callbacks and set timestamps" do
395
- @tag_foo.created_at.should_not be_nil
396
- @tag_foo.updated_at.should_not be_nil
397
- end
398
-
399
- it "should allow an update through the parent model" do
400
- update_hash = { "post" => { "tags_attributes" => { "1234" => { "id" => @tag_bar.id, "title" => "baz" } } } }
401
- @post.update_attributes(update_hash["post"])
402
- @post.tags.last.title.should == "baz"
403
- end
404
-
405
- it "should be possible to destroy through parent model using canoncial _destroy macro" do
406
- @post.tags.count.should == 2
407
- update_hash = { "post" => { "tags_attributes" => { "1234" => { "id" => @tag_bar.id, "title" => "baz", "_destroy" => "true"} } } }
408
- @post.update_attributes(update_hash["post"])
409
- @post.tags.count.should == 1
410
- @post.history_tracks.last.action.should == "destroy"
411
- end
412
-
413
- it "should write relationship name for association_chain hiearchy instead of class name when using _destroy macro" do
414
- update_hash = {"tags_attributes" => { "1234" => { "id" => @tag_foo.id, "_destroy" => "1"} } }
415
- @post.update_attributes(update_hash)
416
-
417
- # historically this would have evaluated to 'Tags' and an error would be thrown
418
- # on any call that walked up the association_chain, e.g. 'trackable'
419
- @tag_foo.history_tracks.last.association_chain.last["name"].should == "tags"
420
- lambda{ @tag_foo.history_tracks.last.trackable }.should_not raise_error
421
- end
422
-
423
- it "should save modifier" do
424
- @tag_foo.history_tracks.last.modifier.should eq @user
425
- @tag_bar.history_tracks.last.modifier.should eq @user
426
- end
427
- end
428
-
429
- describe "non-embedded" do
430
- it "should undo changes" do
431
- @post.update_attributes(:title => "Test2")
432
- @post.history_tracks.where(:version => 1).last.undo!(@user)
433
- @post.reload
434
- @post.title.should == "Test"
435
- end
436
-
437
- it "should undo destruction" do
438
- @post.destroy
439
- @post.history_tracks.where(:version => 1).last.undo!(@user)
440
- Post.find(@post.id).title.should == "Test"
441
- end
442
-
443
- it "should create a new history track after undo" do
444
- @post.update_attributes(:title => "Test2")
445
- @post.history_tracks.last.undo!(@user)
446
- @post.reload
447
- @post.history_tracks.count.should == 3
448
- end
449
-
450
- it "should assign @user as the modifier of the newly created history track" do
451
- @post.update_attributes(:title => "Test2")
452
- @post.history_tracks.where(:version => 1).last.undo!(@user)
453
- @post.reload
454
- @post.history_tracks.where(:version => 2).last.modifier.should == @user
455
- end
456
-
457
- it "should stay the same after undo and redo" do
458
- @post.update_attributes(:title => "Test2")
459
- @track = @post.history_tracks.last
460
- @track.undo!(@user)
461
- @track.redo!(@user)
462
- @post2 = Post.where(:_id => @post.id).first
463
-
464
- @post.title.should == @post2.title
465
- end
466
-
467
- it "should be destroyed after undo and redo" do
468
- @post.destroy
469
- @track = @post.history_tracks.where(:version => 1).last
470
- @track.undo!(@user)
471
- @track.redo!(@user)
472
- Post.where(:_id => @post.id).first.should == nil
473
- end
474
- end
475
-
476
- describe "embedded" do
477
- it "should undo changes" do
478
- @comment.update_attributes(:title => "Test2")
479
- @comment.history_tracks.where(:version => 2).first.undo!(@user)
480
- # reloading an embedded document === KAMIKAZE
481
- # at least for the current release of mongoid...
482
- @post.reload
483
- @comment = @post.comments.first
484
- @comment.title.should == "test"
485
- end
486
-
487
- it "should create a new history track after undo" do
488
- @comment.update_attributes(:title => "Test2")
489
- @comment.history_tracks.where(:version => 2).first.undo!(@user)
490
- @post.reload
491
- @comment = @post.comments.first
492
- @comment.history_tracks.count.should == 3
493
- end
494
-
495
- it "should assign @user as the modifier of the newly created history track" do
496
- @comment.update_attributes(:title => "Test2")
497
- @comment.history_tracks.where(:version => 2).first.undo!(@user)
498
- @post.reload
499
- @comment = @post.comments.first
500
- @comment.history_tracks.where(:version => 3).first.modifier.should == @user
501
- end
502
-
503
- it "should stay the same after undo and redo" do
504
- @comment.update_attributes(:title => "Test2")
505
- @track = @comment.history_tracks.where(:version => 2).first
506
- @track.undo!(@user)
507
- @track.redo!(@user)
508
- @post2 = Post.where(:_id => @post.id).first
509
- @comment2 = @post2.comments.first
510
-
511
- @comment.title.should == @comment2.title
512
- end
513
- end
514
-
515
- describe "trackables" do
516
- before :each do
517
- @comment.update_attributes(:title => "Test2") # version == 2
518
- @comment.update_attributes(:title => "Test3") # version == 3
519
- @comment.update_attributes(:title => "Test4") # version == 4
520
- end
521
-
522
- describe "undo" do
523
- it "should recognize :from, :to options" do
524
- @comment.undo! @user, :from => 4, :to => 2
525
- @comment.title.should == "test"
526
- end
527
-
528
- it "should recognize parameter as version number" do
529
- @comment.undo! @user, 3
530
- @comment.title.should == "Test2"
531
- end
532
-
533
- it "should undo last version when no parameter is specified" do
534
- @comment.undo! @user
535
- @comment.title.should == "Test3"
536
- end
537
-
538
- it "should recognize :last options" do
539
- @comment.undo! @user, :last => 2
540
- @comment.title.should == "Test2"
541
- end
542
- end
543
-
544
- describe "redo" do
545
- before :each do
546
- @comment.update_attributes(:title => "Test5")
547
- end
548
-
549
- it "should recognize :from, :to options" do
550
- @comment.redo! @user, :from => 2, :to => 4
551
- @comment.title.should == "Test4"
552
- end
553
-
554
- it "should recognize parameter as version number" do
555
- @comment.redo! @user, 2
556
- @comment.title.should == "Test2"
557
- end
558
-
559
- it "should redo last version when no parameter is specified" do
560
- @comment.redo! @user
561
- @comment.title.should == "Test5"
562
- end
563
-
564
- it "should recognize :last options" do
565
- @comment.redo! @user, :last => 1
566
- @comment.title.should == "Test5"
567
- end
568
- end
569
- end
570
-
571
- describe "duplicate relations" do
572
- it "should save correct relation" do
573
- lambda{ Restaurant.create!( title: 'test', modifier_id: @user.id ) }.should_not raise_error
574
- end
575
- end
576
-
577
-
578
- describe "rails admin history" do
579
- before :each do
580
- @restaurant = Restaurant.create!( title: 'test', modifier_id: @user.id )
581
- @adapter = ::RailsAdmin::Extensions::MongoidAudit::AuditingAdapter.new( nil )
582
- @model = Struct.new( :model_name ).new( 'Restaurant' )
583
- end
584
-
585
- it "should list all records from history table" do
586
- query = ''
587
-
588
- items = @adapter.listing_for_model(@model, query, false, 'false', true, 1, 10)
589
-
590
- expect( items ).not_to be_empty
591
-
592
- item = items.first
593
-
594
- expect( item.message ).to eq 'create Restaurant [title = test]'
595
- expect( item.table ).to eq 'Restaurant'
596
- expect( item.username ).to eq @user.email
597
- expect( item.item ).to eq @restaurant.id
598
-
599
- query = nil
600
-
601
- items = @adapter.listing_for_model(@model, query, false, 'false', true, 1, 10)
602
-
603
- expect( items ).not_to be_empty
604
-
605
- item = items.first
606
-
607
- expect( item.message ).to eq 'create Restaurant [title = test]'
608
- end
609
-
610
- it "should list records from history table specified by query" do
611
- query = 'create'
612
-
613
- items = @adapter.listing_for_model(@model, query, false, 'false', true, 1, 10)
614
-
615
- expect( items ).not_to be_empty
616
-
617
- item = items.first
618
-
619
- expect( item.message ).to eq 'create Restaurant [title = test]'
620
- expect( item.table ).to eq 'Restaurant'
621
- expect( item.username ).to eq @user.email
622
- expect( item.item ).to eq @restaurant.id
623
- end
624
-
625
- it "should list records from history table specified by item" do
626
- query = ''
627
-
628
- items = @adapter.listing_for_object(@model, @restaurant, query, false, 'false', true, 1, 10)
629
-
630
- expect( items ).not_to be_empty
631
-
632
- item = items.first
633
-
634
- expect( item.message ).to eq 'create Restaurant [title = test]'
635
- expect( item.table ).to eq 'Restaurant'
636
- expect( item.username ).to eq @user.email
637
- expect( item.item ).to eq @restaurant.id
638
-
639
- query = nil
640
-
641
- items = @adapter.listing_for_object(@model, @restaurant, query, false, 'false', true, 1, 10)
642
-
643
- expect( items ).not_to be_empty
644
-
645
- item = items.first
646
-
647
- expect( item.message ).to eq 'create Restaurant [title = test]'
648
- end
649
-
650
- it "should list records from history table specified by item and query" do
651
- query = 'create'
652
-
653
- items = @adapter.listing_for_object(@model, @restaurant, query, false, 'false', true, 1, 10)
654
-
655
- expect( items ).not_to be_empty
656
-
657
- item = items.first
658
-
659
- expect( item.message ).to eq 'create Restaurant [title = test]'
660
- expect( item.table ).to eq 'Restaurant'
661
- expect( item.username ).to eq @user.email
662
- expect( item.item ).to eq @restaurant.id
663
- end
664
- end
665
- end
666
- end