mongoid-audit 0.3.2 → 1.0.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
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