shingara-mongomapper 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/.gitignore +7 -0
  2. data/History +70 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +39 -0
  5. data/Rakefile +73 -0
  6. data/VERSION +1 -0
  7. data/bin/mmconsole +56 -0
  8. data/lib/mongomapper.rb +77 -0
  9. data/lib/mongomapper/associations.rb +84 -0
  10. data/lib/mongomapper/associations/base.rb +69 -0
  11. data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
  12. data/lib/mongomapper/associations/belongs_to_proxy.rb +22 -0
  13. data/lib/mongomapper/associations/many_documents_proxy.rb +103 -0
  14. data/lib/mongomapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
  15. data/lib/mongomapper/associations/many_embedded_proxy.rb +17 -0
  16. data/lib/mongomapper/associations/many_polymorphic_proxy.rb +11 -0
  17. data/lib/mongomapper/associations/many_proxy.rb +6 -0
  18. data/lib/mongomapper/associations/proxy.rb +63 -0
  19. data/lib/mongomapper/callbacks.rb +106 -0
  20. data/lib/mongomapper/document.rb +348 -0
  21. data/lib/mongomapper/dynamic_finder.rb +38 -0
  22. data/lib/mongomapper/embedded_document.rb +265 -0
  23. data/lib/mongomapper/finder_options.rb +85 -0
  24. data/lib/mongomapper/key.rb +76 -0
  25. data/lib/mongomapper/observing.rb +50 -0
  26. data/lib/mongomapper/pagination.rb +52 -0
  27. data/lib/mongomapper/rails_compatibility/document.rb +15 -0
  28. data/lib/mongomapper/rails_compatibility/embedded_document.rb +25 -0
  29. data/lib/mongomapper/save_with_validation.rb +19 -0
  30. data/lib/mongomapper/serialization.rb +55 -0
  31. data/lib/mongomapper/serializers/json_serializer.rb +92 -0
  32. data/lib/mongomapper/support.rb +30 -0
  33. data/lib/mongomapper/validations.rb +47 -0
  34. data/mongomapper.gemspec +142 -0
  35. data/test/NOTE_ON_TESTING +1 -0
  36. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +53 -0
  37. data/test/functional/associations/test_belongs_to_proxy.rb +45 -0
  38. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +131 -0
  39. data/test/functional/associations/test_many_embedded_proxy.rb +106 -0
  40. data/test/functional/associations/test_many_polymorphic_proxy.rb +261 -0
  41. data/test/functional/associations/test_many_proxy.rb +295 -0
  42. data/test/functional/test_associations.rb +47 -0
  43. data/test/functional/test_callbacks.rb +85 -0
  44. data/test/functional/test_document.rb +952 -0
  45. data/test/functional/test_pagination.rb +81 -0
  46. data/test/functional/test_rails_compatibility.rb +30 -0
  47. data/test/functional/test_validations.rb +172 -0
  48. data/test/models.rb +139 -0
  49. data/test/test_helper.rb +67 -0
  50. data/test/unit/serializers/test_json_serializer.rb +157 -0
  51. data/test/unit/test_association_base.rb +144 -0
  52. data/test/unit/test_document.rb +123 -0
  53. data/test/unit/test_embedded_document.rb +526 -0
  54. data/test/unit/test_finder_options.rb +183 -0
  55. data/test/unit/test_key.rb +247 -0
  56. data/test/unit/test_mongomapper.rb +28 -0
  57. data/test/unit/test_observing.rb +101 -0
  58. data/test/unit/test_pagination.rb +113 -0
  59. data/test/unit/test_rails_compatibility.rb +34 -0
  60. data/test/unit/test_serializations.rb +52 -0
  61. data/test/unit/test_validations.rb +259 -0
  62. metadata +189 -0
@@ -0,0 +1,34 @@
1
+ module MongoMapper
2
+ module Associations
3
+ class BelongsToPolymorphicProxy < Proxy
4
+ def replace(doc)
5
+ if doc
6
+ doc.save if doc.new?
7
+ id, type = doc.id, doc.class.name
8
+ end
9
+
10
+ @owner.send("#{@association.foreign_key}=", id)
11
+ @owner.send("#{@association.type_key_name}=", type)
12
+ reset
13
+ end
14
+
15
+ protected
16
+ def find_target
17
+ if proxy_id && proxy_class
18
+ proxy_class.find_by_id(proxy_id)
19
+ end
20
+ end
21
+
22
+ def proxy_id
23
+ @proxy_id ||= @owner.send(@association.foreign_key)
24
+ end
25
+
26
+ def proxy_class
27
+ @proxy_class ||= begin
28
+ klass = @owner.send(@association.type_key_name)
29
+ klass && klass.constantize
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,22 @@
1
+ module MongoMapper
2
+ module Associations
3
+ class BelongsToProxy < Proxy
4
+ def replace(doc)
5
+ if doc
6
+ doc.save if doc.new?
7
+ id = doc.id
8
+ end
9
+
10
+ @owner.send("#{@association.foreign_key}=", id)
11
+ reset
12
+ end
13
+
14
+ protected
15
+ def find_target
16
+ if association_id = @owner.send(@association.foreign_key)
17
+ @association.klass.find_by_id(association_id)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,103 @@
1
+ module MongoMapper
2
+ module Associations
3
+ class ManyDocumentsProxy < Proxy
4
+ delegate :klass, :to => :@association
5
+
6
+ def find(*args)
7
+ options = args.extract_options!
8
+ klass.find(*args << scoped_options(options))
9
+ end
10
+
11
+ def paginate(options)
12
+ klass.paginate(scoped_options(options))
13
+ end
14
+
15
+ def all(options={})
16
+ find(:all, scoped_options(options))
17
+ end
18
+
19
+ def first(options={})
20
+ find(:first, scoped_options(options))
21
+ end
22
+
23
+ def last(options={})
24
+ find(:last, scoped_options(options))
25
+ end
26
+
27
+ def count(conditions={})
28
+ klass.count(conditions.deep_merge(scoped_conditions))
29
+ end
30
+
31
+ def replace(docs)
32
+ @target.map(&:destroy) if load_target
33
+ docs.each { |doc| apply_scope(doc).save }
34
+ reset
35
+ end
36
+
37
+ def <<(*docs)
38
+ ensure_owner_saved
39
+ flatten_deeper(docs).each { |doc| apply_scope(doc).save }
40
+ reset
41
+ end
42
+ alias_method :push, :<<
43
+ alias_method :concat, :<<
44
+
45
+ def build(attrs={})
46
+ doc = klass.new(attrs)
47
+ apply_scope(doc)
48
+ doc
49
+ end
50
+
51
+ def create(attrs={})
52
+ doc = klass.new(attrs)
53
+ apply_scope(doc).save
54
+ doc
55
+ end
56
+
57
+ def destroy_all(conditions={})
58
+ all(:conditions => conditions).map(&:destroy)
59
+ reset
60
+ end
61
+
62
+ def delete_all(conditions={})
63
+ klass.delete_all(conditions.deep_merge(scoped_conditions))
64
+ reset
65
+ end
66
+
67
+ def nullify
68
+ criteria = FinderOptions.to_mongo_criteria(scoped_conditions)
69
+ all(criteria).each do |doc|
70
+ doc.update_attributes self.foreign_key => nil
71
+ end
72
+ reset
73
+ end
74
+
75
+ protected
76
+ def scoped_conditions
77
+ {self.foreign_key => @owner.id}
78
+ end
79
+
80
+ def scoped_options(options)
81
+ options.deep_merge({:conditions => scoped_conditions})
82
+ end
83
+
84
+ def find_target
85
+ find(:all)
86
+ end
87
+
88
+ def ensure_owner_saved
89
+ @owner.save if @owner.new?
90
+ end
91
+
92
+ def apply_scope(doc)
93
+ ensure_owner_saved
94
+ doc.send("#{self.foreign_key}=", @owner.id)
95
+ doc
96
+ end
97
+
98
+ def foreign_key
99
+ @association.options[:foreign_key] || @owner.class.name.underscore.gsub("/", "_") + "_id"
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,33 @@
1
+ module MongoMapper
2
+ module Associations
3
+ class ManyEmbeddedPolymorphicProxy < Proxy
4
+ def replace(v)
5
+ @_values = v.map do |doc_or_hash|
6
+ if doc_or_hash.kind_of?(EmbeddedDocument)
7
+ doc = doc_or_hash
8
+ {@association.type_key_name => doc.class.name}.merge(doc.attributes)
9
+ else
10
+ doc_or_hash
11
+ end
12
+ end
13
+
14
+ reset
15
+ end
16
+
17
+ protected
18
+ def find_target
19
+ (@_values || []).map do |hash|
20
+ polymorphic_class(hash).new(hash)
21
+ end
22
+ end
23
+
24
+ def polymorphic_class(doc)
25
+ if class_name = doc[@association.type_key_name]
26
+ class_name.constantize
27
+ else
28
+ @association.klass
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ module MongoMapper
2
+ module Associations
3
+ class ManyEmbeddedProxy < Proxy
4
+ def replace(v)
5
+ @_values = v.map { |e| e.kind_of?(EmbeddedDocument) ? e.attributes : e }
6
+ reset
7
+ end
8
+
9
+ protected
10
+ def find_target
11
+ (@_values || []).map do |e|
12
+ @association.klass.new(e)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module MongoMapper
2
+ module Associations
3
+ class ManyPolymorphicProxy < ManyDocumentsProxy
4
+ private
5
+ def apply_scope(doc)
6
+ doc.send("#{@association.type_key_name}=", doc.class.name)
7
+ super
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ module MongoMapper
2
+ module Associations
3
+ class ManyProxy < ManyDocumentsProxy
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,63 @@
1
+ module MongoMapper
2
+ module Associations
3
+ class Proxy < BasicObject
4
+ attr_reader :owner, :association
5
+
6
+ def initialize(owner, association)
7
+ @owner = owner
8
+ @association = association
9
+ reset
10
+ end
11
+
12
+ def respond_to?(*methods)
13
+ (load_target && @target.respond_to?(*methods))
14
+ end
15
+
16
+ def reset
17
+ @target = nil
18
+ end
19
+
20
+ def reload_target
21
+ reset
22
+ load_target
23
+ self
24
+ end
25
+
26
+ def send(method, *args)
27
+ load_target
28
+ @target.send(method, *args)
29
+ end
30
+
31
+ def replace(v)
32
+ raise NotImplementedError
33
+ end
34
+
35
+ protected
36
+ def method_missing(method, *args)
37
+ if load_target
38
+ if block_given?
39
+ @target.send(method, *args) { |*block_args| yield(*block_args) }
40
+ else
41
+ @target.send(method, *args)
42
+ end
43
+ end
44
+ end
45
+
46
+ def load_target
47
+ @target ||= find_target
48
+ end
49
+
50
+ def find_target
51
+ raise NotImplementedError
52
+ end
53
+
54
+ # Array#flatten has problems with recursive arrays. Going one level
55
+ # deeper solves the majority of the problems.
56
+ def flatten_deeper(array)
57
+ array.collect do |element|
58
+ (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element
59
+ end.flatten
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,106 @@
1
+ module MongoMapper
2
+ module Callbacks
3
+ def self.included(model) #:nodoc:
4
+ model.class_eval do
5
+ extend Observable
6
+ include ActiveSupport::Callbacks
7
+
8
+ define_callbacks *%w(
9
+ before_save after_save before_create after_create before_update after_update before_validation
10
+ after_validation before_validation_on_create after_validation_on_create before_validation_on_update
11
+ after_validation_on_update before_destroy after_destroy
12
+ )
13
+
14
+ [:create_or_update, :valid?, :create, :update, :destroy].each do |method|
15
+ alias_method_chain method, :callbacks
16
+ end
17
+ end
18
+ end
19
+
20
+ def before_save() end
21
+
22
+ def after_save() end
23
+ def create_or_update_with_callbacks #:nodoc:
24
+ return false if callback(:before_save) == false
25
+ if result = create_or_update_without_callbacks
26
+ callback(:after_save)
27
+ end
28
+ result
29
+ end
30
+ private :create_or_update_with_callbacks
31
+
32
+ def before_create() end
33
+
34
+ def after_create() end
35
+ def create_with_callbacks #:nodoc:
36
+ return false if callback(:before_create) == false
37
+ result = create_without_callbacks
38
+ callback(:after_create)
39
+ result
40
+ end
41
+ private :create_with_callbacks
42
+
43
+ def before_update() end
44
+
45
+ def after_update() end
46
+
47
+ def update_with_callbacks(*args) #:nodoc:
48
+ return false if callback(:before_update) == false
49
+ result = update_without_callbacks(*args)
50
+ callback(:after_update)
51
+ result
52
+ end
53
+ private :update_with_callbacks
54
+
55
+ def before_validation() end
56
+
57
+ def after_validation() end
58
+
59
+ def before_validation_on_create() end
60
+
61
+ def after_validation_on_create() end
62
+
63
+ def before_validation_on_update() end
64
+
65
+ def after_validation_on_update() end
66
+
67
+ def valid_with_callbacks? #:nodoc:
68
+ return false if callback(:before_validation) == false
69
+ result = new? ? callback(:before_validation_on_create) : callback(:before_validation_on_update)
70
+ return false if false == result
71
+
72
+ result = valid_without_callbacks?
73
+ callback(:after_validation)
74
+
75
+ new? ? callback(:after_validation_on_create) : callback(:after_validation_on_update)
76
+ return result
77
+ end
78
+
79
+ def before_destroy() end
80
+
81
+ def after_destroy() end
82
+ def destroy_with_callbacks #:nodoc:
83
+ return false if callback(:before_destroy) == false
84
+ result = destroy_without_callbacks
85
+ callback(:after_destroy)
86
+ result
87
+ end
88
+
89
+ private
90
+ def callback(method)
91
+ result = run_callbacks(method) { |result, object| false == result }
92
+
93
+ if result != false && respond_to?(method)
94
+ result = send(method)
95
+ end
96
+
97
+ notify(method)
98
+ return result
99
+ end
100
+
101
+ def notify(method) #:nodoc:
102
+ self.class.changed
103
+ self.class.notify_observers(method, self)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,348 @@
1
+ require 'set'
2
+
3
+ module MongoMapper
4
+ module Document
5
+ def self.included(model)
6
+ model.class_eval do
7
+ include EmbeddedDocument
8
+ include InstanceMethods
9
+ include Observing
10
+ include Callbacks
11
+ include SaveWithValidation
12
+ include RailsCompatibility::Document
13
+ extend ClassMethods
14
+ end
15
+
16
+ descendants << model
17
+ end
18
+
19
+ def self.descendants
20
+ @descendants ||= Set.new
21
+ end
22
+
23
+ module ClassMethods
24
+ def find(*args)
25
+ options = args.extract_options!
26
+
27
+ case args.first
28
+ when :first then find_first(options)
29
+ when :last then find_last(options)
30
+ when :all then find_every(options)
31
+ else find_from_ids(args, options)
32
+ end
33
+ end
34
+
35
+ def paginate(options)
36
+ per_page = options.delete(:per_page)
37
+ page = options.delete(:page)
38
+ total_entries = count(options[:conditions] || {})
39
+ collection = Pagination::PaginationProxy.new(total_entries, page, per_page)
40
+
41
+ options[:limit] = collection.limit
42
+ options[:offset] = collection.offset
43
+
44
+ collection.subject = find_every(options)
45
+ collection
46
+ end
47
+
48
+ def first(options={})
49
+ find_first(options)
50
+ end
51
+
52
+ def last(options={})
53
+ find_last(options)
54
+ end
55
+
56
+ def all(options={})
57
+ find_every(options)
58
+ end
59
+
60
+ def find_by_id(id)
61
+ criteria = FinderOptions.to_mongo_criteria(:_id => id)
62
+ if doc = collection.find_first(criteria)
63
+ new(doc)
64
+ end
65
+ end
66
+
67
+ def count(conditions={})
68
+ collection.count(FinderOptions.to_mongo_criteria(conditions))
69
+ end
70
+
71
+ def create(*docs)
72
+ instances = []
73
+ docs = [{}] if docs.blank?
74
+ docs.flatten.each do |attrs|
75
+ doc = new(attrs); doc.save
76
+ instances << doc
77
+ end
78
+ instances.size == 1 ? instances[0] : instances
79
+ end
80
+
81
+ # For updating single document
82
+ # Person.update(1, {:foo => 'bar'})
83
+ #
84
+ # For updating multiple documents at once:
85
+ # Person.update({'1' => {:foo => 'bar'}, '2' => {:baz => 'wick'}})
86
+ def update(*args)
87
+ updating_multiple = args.length == 1
88
+ if updating_multiple
89
+ update_multiple(args[0])
90
+ else
91
+ id, attributes = args
92
+ update_single(id, attributes)
93
+ end
94
+ end
95
+
96
+ def delete(*ids)
97
+ criteria = FinderOptions.to_mongo_criteria(:_id => ids.flatten)
98
+ collection.remove(criteria)
99
+ end
100
+
101
+ def delete_all(conditions={})
102
+ criteria = FinderOptions.to_mongo_criteria(conditions)
103
+ collection.remove(criteria)
104
+ end
105
+
106
+ def destroy(*ids)
107
+ find_some(ids.flatten).each(&:destroy)
108
+ end
109
+
110
+ def destroy_all(conditions={})
111
+ find(:all, :conditions => conditions).each(&:destroy)
112
+ end
113
+
114
+ def connection(mongo_connection=nil)
115
+ if mongo_connection.nil?
116
+ @connection ||= MongoMapper.connection
117
+ else
118
+ @connection = mongo_connection
119
+ end
120
+ @connection
121
+ end
122
+
123
+ def database(name=nil)
124
+ if name.nil?
125
+ @database ||= MongoMapper.database
126
+ else
127
+ @database = MongoMapper.database(name)
128
+ end
129
+ @database
130
+ end
131
+
132
+ def collection(name=nil)
133
+ if name.nil?
134
+ @collection ||= database.collection(self.to_s.demodulize.tableize)
135
+ else
136
+ @collection = database.collection(name)
137
+ end
138
+ @collection
139
+ end
140
+
141
+ def timestamps!
142
+ key :created_at, Time
143
+ key :updated_at, Time
144
+
145
+ class_eval { before_save :update_timestamps }
146
+ end
147
+
148
+ def validates_uniqueness_of(*args)
149
+ add_validations(args, MongoMapper::Validations::ValidatesUniquenessOf)
150
+ end
151
+
152
+ def validates_exclusion_of(*args)
153
+ add_validations(args, MongoMapper::Validations::ValidatesExclusionOf)
154
+ end
155
+
156
+ def validates_inclusion_of(*args)
157
+ add_validations(args, MongoMapper::Validations::ValidatesInclusionOf)
158
+ end
159
+
160
+ protected
161
+ def method_missing(method, *args)
162
+ finder = DynamicFinder.new(self, method)
163
+
164
+ if finder.valid?
165
+ meta_def(finder.options[:method]) do |*args|
166
+ find_with_args(args, finder.options)
167
+ end
168
+
169
+ send(finder.options[:method], *args)
170
+ else
171
+ super
172
+ end
173
+ end
174
+
175
+ private
176
+ def find_every(options)
177
+ criteria, options = FinderOptions.new(options).to_a
178
+ collection.find(criteria, options).to_a.map { |doc| new(doc) }
179
+ end
180
+
181
+ def find_first(options)
182
+ options.merge!(:limit => 1)
183
+ find_every({:order => '$natural asc'}.merge(options))[0]
184
+ end
185
+
186
+ def find_last(options)
187
+ options.merge!(:limit => 1)
188
+ options[:order] = invert_order_clause(options)
189
+ find_every(options)[0]
190
+ #find_every({:order => '$natural desc'}.merge(invert_order_clause(options)))[0]
191
+ end
192
+
193
+ def invert_order_clause(options)
194
+ return '$natural desc' unless options[:order]
195
+ options[:order].split(',').map do |order_segment|
196
+ if order_segment =~ /\sasc/i
197
+ order_segment.sub /\sasc/i, ' desc'
198
+ elsif order_segment =~ /\sdesc/i
199
+ order_segment.sub /\sdesc/i, ' asc'
200
+ else
201
+ "#{order_segment.strip} desc"
202
+ end
203
+ end.join(',')
204
+ end
205
+
206
+ def find_some(ids, options={})
207
+ documents = find_every(options.deep_merge(:conditions => {'_id' => ids}))
208
+ if ids.size == documents.size
209
+ documents
210
+ else
211
+ raise DocumentNotFound, "Couldn't find all of the ids (#{ids.to_sentence}). Found #{documents.size}, but was expecting #{ids.size}"
212
+ end
213
+ end
214
+
215
+ def find_one(id, options={})
216
+ if doc = find_every(options.deep_merge(:conditions => {:_id => id})).first
217
+ doc
218
+ else
219
+ raise DocumentNotFound, "Document with id of #{id} does not exist in collection named #{collection.name}"
220
+ end
221
+ end
222
+
223
+ def find_from_ids(ids, options={})
224
+ ids = ids.flatten.compact.uniq
225
+
226
+ case ids.size
227
+ when 0
228
+ raise(DocumentNotFound, "Couldn't find without an ID")
229
+ when 1
230
+ find_one(ids[0], options)
231
+ else
232
+ find_some(ids, options)
233
+ end
234
+ end
235
+
236
+ def find_with_args(args, options)
237
+ attributes, = {}
238
+ find_options = args.extract_options!.deep_merge(:conditions => attributes)
239
+
240
+ options[:attribute_names].each_with_index do |attr, index|
241
+ attributes[attr] = args[index]
242
+ end
243
+
244
+ result = find(options[:finder], find_options)
245
+
246
+ if result.nil?
247
+ if options[:bang]
248
+ raise DocumentNotFound, "Couldn't find Document with #{attributes.inspect} in collection named #{collection.name}"
249
+ end
250
+
251
+ if options[:instantiator]
252
+ self.send(options[:instantiator], attributes)
253
+ end
254
+ else
255
+ result
256
+ end
257
+ end
258
+
259
+ def update_single(id, attrs)
260
+ if id.blank? || attrs.blank? || !attrs.is_a?(Hash)
261
+ raise ArgumentError, "Updating a single document requires an id and a hash of attributes"
262
+ end
263
+
264
+ doc = find(id)
265
+ doc.update_attributes(attrs)
266
+ doc
267
+ end
268
+
269
+ def update_multiple(docs)
270
+ unless docs.is_a?(Hash)
271
+ raise ArgumentError, "Updating multiple documents takes 1 argument and it must be hash"
272
+ end
273
+
274
+ instances = []
275
+ docs.each_pair { |id, attrs| instances << update(id, attrs) }
276
+ instances
277
+ end
278
+ end
279
+
280
+ module InstanceMethods
281
+ def collection
282
+ self.class.collection
283
+ end
284
+
285
+ def new?
286
+ read_attribute('_id').blank? || using_custom_id?
287
+ end
288
+
289
+ def save
290
+ create_or_update
291
+ end
292
+
293
+ def save!
294
+ create_or_update || raise(DocumentNotValid.new(self))
295
+ end
296
+
297
+ def update_attributes(attrs={})
298
+ self.attributes = attrs
299
+ save
300
+ end
301
+
302
+ def destroy
303
+ return false if frozen?
304
+
305
+ criteria = FinderOptions.to_mongo_criteria(:_id => id)
306
+ collection.remove(criteria) unless new?
307
+ freeze
308
+ end
309
+
310
+ private
311
+ def create_or_update
312
+ result = new? ? create : update
313
+ result != false
314
+ end
315
+
316
+ def create
317
+ assign_id
318
+ save_to_collection
319
+ end
320
+
321
+ def assign_id
322
+ if read_attribute(:_id).blank?
323
+ write_attribute(:_id, XGen::Mongo::Driver::ObjectID.new.to_s)
324
+ end
325
+ end
326
+
327
+ def update
328
+ save_to_collection
329
+ end
330
+
331
+ # collection.save returns mongoid
332
+ def save_to_collection
333
+ clear_custom_id_flag
334
+ collection.save(attributes)
335
+ end
336
+
337
+ def update_timestamps
338
+ now = Time.now.utc
339
+ write_attribute('created_at', now) if new?
340
+ write_attribute('updated_at', now)
341
+ end
342
+
343
+ def clear_custom_id_flag
344
+ @using_custom_id = nil
345
+ end
346
+ end
347
+ end # Document
348
+ end # MongoMapper