mongoid-rails2 1.9.3

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.
Files changed (104) hide show
  1. data/MIT_LICENSE +20 -0
  2. data/README.rdoc +49 -0
  3. data/lib/mongoid/associations/belongs_to_related.rb +58 -0
  4. data/lib/mongoid/associations/embedded_in.rb +72 -0
  5. data/lib/mongoid/associations/embeds_many.rb +254 -0
  6. data/lib/mongoid/associations/embeds_one.rb +96 -0
  7. data/lib/mongoid/associations/has_many_related.rb +181 -0
  8. data/lib/mongoid/associations/has_one_related.rb +85 -0
  9. data/lib/mongoid/associations/meta_data.rb +29 -0
  10. data/lib/mongoid/associations/options.rb +57 -0
  11. data/lib/mongoid/associations/proxy.rb +24 -0
  12. data/lib/mongoid/associations.rb +300 -0
  13. data/lib/mongoid/attributes.rb +204 -0
  14. data/lib/mongoid/callbacks.rb +23 -0
  15. data/lib/mongoid/collection.rb +120 -0
  16. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  17. data/lib/mongoid/collections/master.rb +29 -0
  18. data/lib/mongoid/collections/operations.rb +41 -0
  19. data/lib/mongoid/collections/slaves.rb +45 -0
  20. data/lib/mongoid/collections.rb +41 -0
  21. data/lib/mongoid/components.rb +27 -0
  22. data/lib/mongoid/concern.rb +31 -0
  23. data/lib/mongoid/config.rb +191 -0
  24. data/lib/mongoid/contexts/enumerable.rb +151 -0
  25. data/lib/mongoid/contexts/ids.rb +25 -0
  26. data/lib/mongoid/contexts/mongo.rb +285 -0
  27. data/lib/mongoid/contexts/paging.rb +50 -0
  28. data/lib/mongoid/contexts.rb +25 -0
  29. data/lib/mongoid/criteria.rb +249 -0
  30. data/lib/mongoid/criterion/complex.rb +21 -0
  31. data/lib/mongoid/criterion/exclusion.rb +65 -0
  32. data/lib/mongoid/criterion/inclusion.rb +110 -0
  33. data/lib/mongoid/criterion/optional.rb +136 -0
  34. data/lib/mongoid/cursor.rb +81 -0
  35. data/lib/mongoid/deprecation.rb +22 -0
  36. data/lib/mongoid/dirty.rb +253 -0
  37. data/lib/mongoid/document.rb +311 -0
  38. data/lib/mongoid/errors.rb +108 -0
  39. data/lib/mongoid/extensions/array/accessors.rb +17 -0
  40. data/lib/mongoid/extensions/array/aliasing.rb +4 -0
  41. data/lib/mongoid/extensions/array/assimilation.rb +26 -0
  42. data/lib/mongoid/extensions/array/conversions.rb +29 -0
  43. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  44. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  45. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  46. data/lib/mongoid/extensions/boolean/conversions.rb +22 -0
  47. data/lib/mongoid/extensions/date/conversions.rb +24 -0
  48. data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
  49. data/lib/mongoid/extensions/float/conversions.rb +20 -0
  50. data/lib/mongoid/extensions/hash/accessors.rb +38 -0
  51. data/lib/mongoid/extensions/hash/assimilation.rb +39 -0
  52. data/lib/mongoid/extensions/hash/conversions.rb +45 -0
  53. data/lib/mongoid/extensions/hash/criteria_helpers.rb +21 -0
  54. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  55. data/lib/mongoid/extensions/integer/conversions.rb +20 -0
  56. data/lib/mongoid/extensions/nil/assimilation.rb +17 -0
  57. data/lib/mongoid/extensions/object/conversions.rb +33 -0
  58. data/lib/mongoid/extensions/objectid/conversions.rb +15 -0
  59. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  60. data/lib/mongoid/extensions/string/conversions.rb +15 -0
  61. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  62. data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
  63. data/lib/mongoid/extensions/time_conversions.rb +35 -0
  64. data/lib/mongoid/extensions.rb +101 -0
  65. data/lib/mongoid/extras.rb +61 -0
  66. data/lib/mongoid/factory.rb +20 -0
  67. data/lib/mongoid/field.rb +59 -0
  68. data/lib/mongoid/fields.rb +65 -0
  69. data/lib/mongoid/finders.rb +144 -0
  70. data/lib/mongoid/identity.rb +39 -0
  71. data/lib/mongoid/indexes.rb +30 -0
  72. data/lib/mongoid/javascript/functions.yml +37 -0
  73. data/lib/mongoid/javascript.rb +21 -0
  74. data/lib/mongoid/matchers/all.rb +11 -0
  75. data/lib/mongoid/matchers/default.rb +26 -0
  76. data/lib/mongoid/matchers/exists.rb +13 -0
  77. data/lib/mongoid/matchers/gt.rb +11 -0
  78. data/lib/mongoid/matchers/gte.rb +11 -0
  79. data/lib/mongoid/matchers/in.rb +11 -0
  80. data/lib/mongoid/matchers/lt.rb +11 -0
  81. data/lib/mongoid/matchers/lte.rb +11 -0
  82. data/lib/mongoid/matchers/ne.rb +11 -0
  83. data/lib/mongoid/matchers/nin.rb +11 -0
  84. data/lib/mongoid/matchers/size.rb +11 -0
  85. data/lib/mongoid/matchers.rb +36 -0
  86. data/lib/mongoid/memoization.rb +33 -0
  87. data/lib/mongoid/named_scope.rb +37 -0
  88. data/lib/mongoid/observable.rb +30 -0
  89. data/lib/mongoid/paths.rb +62 -0
  90. data/lib/mongoid/persistence/command.rb +39 -0
  91. data/lib/mongoid/persistence/insert.rb +50 -0
  92. data/lib/mongoid/persistence/insert_embedded.rb +38 -0
  93. data/lib/mongoid/persistence/remove.rb +39 -0
  94. data/lib/mongoid/persistence/remove_all.rb +37 -0
  95. data/lib/mongoid/persistence/remove_embedded.rb +50 -0
  96. data/lib/mongoid/persistence/update.rb +63 -0
  97. data/lib/mongoid/persistence.rb +222 -0
  98. data/lib/mongoid/scope.rb +75 -0
  99. data/lib/mongoid/state.rb +39 -0
  100. data/lib/mongoid/timestamps.rb +27 -0
  101. data/lib/mongoid/version.rb +4 -0
  102. data/lib/mongoid/versioning.rb +27 -0
  103. data/lib/mongoid.rb +122 -0
  104. metadata +298 -0
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ class Deprecation #:nodoc
4
+ include Singleton
5
+
6
+ # Alert of a deprecation. This will delegate to the logger and call warn on
7
+ # it.
8
+ #
9
+ # Example:
10
+ #
11
+ # <tt>deprecation.alert("Method no longer used")</tt>
12
+ def alert(message)
13
+ @logger.warn("Deprecation: #{message}")
14
+ end
15
+
16
+ protected
17
+ # Instantiate a new logger to stdout or a rails logger if available.
18
+ def initialize
19
+ @logger = defined?(Rails) ? Rails.logger : Logger.new($stdout)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,253 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Dirty #:nodoc:
4
+ extend ActiveSupport::Concern
5
+ module InstanceMethods #:nodoc:
6
+ # Gets the changes for a specific field.
7
+ #
8
+ # Example:
9
+ #
10
+ # person = Person.new(:title => "Sir")
11
+ # person.title = "Madam"
12
+ # person.attribute_change("title") # [ "Sir", "Madam" ]
13
+ #
14
+ # Returns:
15
+ #
16
+ # An +Array+ containing the old and new values.
17
+ def attribute_change(name)
18
+ modifications[name]
19
+ end
20
+
21
+ # Determines if a specific field has chaged.
22
+ #
23
+ # Example:
24
+ #
25
+ # person = Person.new(:title => "Sir")
26
+ # person.title = "Madam"
27
+ # person.attribute_changed?("title") # true
28
+ #
29
+ # Returns:
30
+ #
31
+ # +true+ if changed, +false+ if not.
32
+ def attribute_changed?(name)
33
+ modifications.include?(name)
34
+ end
35
+
36
+ # Gets the old value for a specific field.
37
+ #
38
+ # Example:
39
+ #
40
+ # person = Person.new(:title => "Sir")
41
+ # person.title = "Madam"
42
+ # person.attribute_was("title") # "Sir"
43
+ #
44
+ # Returns:
45
+ #
46
+ # The old field value.
47
+ def attribute_was(name)
48
+ change = modifications[name]
49
+ change ? change[0] : nil
50
+ end
51
+
52
+ # Gets the names of all the fields that have changed in the document.
53
+ #
54
+ # Example:
55
+ #
56
+ # person = Person.new(:title => "Sir")
57
+ # person.title = "Madam"
58
+ # person.changed # returns [ "title" ]
59
+ #
60
+ # Returns:
61
+ #
62
+ # An +Array+ of changed field names.
63
+ def changed
64
+ modifications.keys
65
+ end
66
+
67
+ # Alerts to whether the document has been modified or not.
68
+ #
69
+ # Example:
70
+ #
71
+ # person = Person.new(:title => "Sir")
72
+ # person.title = "Madam"
73
+ # person.changed? # returns true
74
+ #
75
+ # Returns:
76
+ #
77
+ # +true+ if changed, +false+ if not.
78
+ def changed?
79
+ !modifications.empty?
80
+ end
81
+
82
+ # Gets all the modifications that have happened to the object as a +Hash+
83
+ # with the keys being the names of the fields, and the values being an
84
+ # +Array+ with the old value and new value.
85
+ #
86
+ # Example:
87
+ #
88
+ # person = Person.new(:title => "Sir")
89
+ # person.title = "Madam"
90
+ # person.changes # returns { "title" => [ "Sir", "Madam" ] }
91
+ #
92
+ # Returns:
93
+ #
94
+ # A +Hash+ of changes.
95
+ def changes
96
+ modifications
97
+ end
98
+
99
+ # Call this method after save, so the changes can be properly switched.
100
+ #
101
+ # Example:
102
+ #
103
+ # <tt>person.move_changes</tt>
104
+ def move_changes
105
+ @previous_modifications = modifications.dup
106
+ @modifications = {}
107
+ end
108
+
109
+ # Gets all the new values for each of the changed fields, to be passed to
110
+ # a MongoDB $set modifier.
111
+ #
112
+ # Example:
113
+ #
114
+ # person = Person.new(:title => "Sir")
115
+ # person.title = "Madam"
116
+ # person.setters # returns { "title" => "Madam" }
117
+ #
118
+ # Returns:
119
+ #
120
+ # A +Hash+ of new values.
121
+ def setters
122
+ modifications.inject({}) do |sets, (field, changes)|
123
+ key = embedded? ? "#{_position}.#{field}" : field
124
+ sets[key] = changes[1]; sets
125
+ end
126
+ end
127
+
128
+ # Gets all the modifications that have happened to the object before the
129
+ # object was saved.
130
+ #
131
+ # Example:
132
+ #
133
+ # person = Person.new(:title => "Sir")
134
+ # person.title = "Madam"
135
+ # person.save!
136
+ # person.previous_changes # returns { "title" => [ "Sir", "Madam" ] }
137
+ #
138
+ # Returns:
139
+ #
140
+ # A +Hash+ of changes before save.
141
+ def previous_changes
142
+ @previous_modifications
143
+ end
144
+
145
+ # Resets a changed field back to its old value.
146
+ #
147
+ # Example:
148
+ #
149
+ # person = Person.new(:title => "Sir")
150
+ # person.title = "Madam"
151
+ # person.reset_attribute!("title")
152
+ # person.title # "Sir"
153
+ #
154
+ # Returns:
155
+ #
156
+ # The old field value.
157
+ def reset_attribute!(name)
158
+ value = attribute_was(name)
159
+ if value
160
+ @attributes[name] = value
161
+ modifications.delete(name)
162
+ end
163
+ end
164
+
165
+ # Sets up the modifications hash. This occurs just after the document is
166
+ # instantiated.
167
+ #
168
+ # Example:
169
+ #
170
+ # <tt>document.setup_notifications</tt>
171
+ def setup_modifications
172
+ @accessed ||= {}
173
+ @modifications ||= {}
174
+ @previous_modifications ||= {}
175
+ end
176
+
177
+ # Reset all modifications for the document. This will wipe all the marked
178
+ # changes, but not reset the values.
179
+ #
180
+ # Example:
181
+ #
182
+ # <tt>document.reset_modifications</tt>
183
+ def reset_modifications
184
+ @accessed = {}
185
+ @modifications = {}
186
+ end
187
+
188
+ protected
189
+
190
+ # Audit the original value for a field that can be modified in place.
191
+ #
192
+ # Example:
193
+ #
194
+ # <tt>person.accessed("aliases", [ "007" ])</tt>
195
+ def accessed(name, value)
196
+ @accessed ||= {}
197
+ @accessed[name] = value.dup if (value.is_a?(Array) || value.is_a?(Hash)) && !@accessed.has_key?(name)
198
+ value
199
+ end
200
+
201
+ # Get all normal modifications plus in place potential changes.
202
+ #
203
+ # Example:
204
+ #
205
+ # <tt>person.modifications</tt>
206
+ #
207
+ # Returns:
208
+ #
209
+ # All changes to the document.
210
+ def modifications
211
+ @accessed.each_pair do |field, value|
212
+ current = @attributes[field]
213
+ @modifications[field] = [ value, current ] if current != value
214
+ end
215
+ @accessed.clear
216
+ @modifications
217
+ end
218
+
219
+ # Audit the change of a field's value.
220
+ #
221
+ # Example:
222
+ #
223
+ # <tt>person.modify("name", "Jack", "John")</tt>
224
+ def modify(name, old_value, new_value)
225
+ @attributes[name] = new_value
226
+ if @modifications && (old_value != new_value)
227
+ original = @modifications[name].first if @modifications[name]
228
+ @modifications[name] = [ (original || old_value), new_value ]
229
+ end
230
+ end
231
+ end
232
+
233
+ module ClassMethods #:nodoc:
234
+ # Add the dynamic dirty methods. These are custom methods defined on a
235
+ # field by field basis that wrap the dirty attribute methods.
236
+ #
237
+ # Example:
238
+ #
239
+ # person = Person.new(:title => "Sir")
240
+ # person.title = "Madam"
241
+ # person.title_change # [ "Sir", "Madam" ]
242
+ # person.title_changed? # true
243
+ # person.title_was # "Sir"
244
+ # person.reset_title!
245
+ def add_dirty_methods(name)
246
+ define_method("#{name}_change") { attribute_change(name) }
247
+ define_method("#{name}_changed?") { attribute_changed?(name) }
248
+ define_method("#{name}_was") { attribute_was(name) }
249
+ define_method("reset_#{name}!") { reset_attribute!(name) }
250
+ end
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,311 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Document
4
+ extend ActiveSupport::Concern
5
+ include Mongoid::Components
6
+ included do
7
+ include Mongoid::Components
8
+
9
+ cattr_accessor :primary_key, :hereditary
10
+ self.hereditary = false
11
+
12
+ attr_accessor :association_name, :_parent
13
+
14
+ delegate :db, :primary_key, :to => "self.class"
15
+ end
16
+
17
+ module ClassMethods
18
+ # Return the database associated with this class.
19
+ def db
20
+ collection.db
21
+ end
22
+
23
+ # Perform default behavior but mark the hierarchy as being hereditary.
24
+ def inherited(subclass)
25
+ super(subclass)
26
+ self.hereditary = true
27
+ end
28
+
29
+ # Returns a human readable version of the class.
30
+ #
31
+ # Example:
32
+ #
33
+ # <tt>MixedDrink.human_name # returns "Mixed Drink"</tt>
34
+ def human_name
35
+ name.labelize
36
+ end
37
+
38
+ # Instantiate a new object, only when loaded from the database or when
39
+ # the attributes have already been typecast.
40
+ #
41
+ # Example:
42
+ #
43
+ # <tt>Person.instantiate(:title => "Sir", :age => 30)</tt>
44
+ def instantiate(attrs = nil, allocating = false)
45
+ attributes = attrs || {}
46
+ if attributes["_id"] || allocating
47
+ document = allocate
48
+ document.instance_variable_set(:@attributes, attributes)
49
+ document.setup_modifications
50
+ return document
51
+ else
52
+ return new(attrs)
53
+ end
54
+ end
55
+
56
+ # Defines the field that will be used for the id of this +Document+. This
57
+ # set the id of this +Document+ before save to a parameterized version of
58
+ # the field that was supplied. This is good for use for readable URLS in
59
+ # web applications.
60
+ #
61
+ # Example:
62
+ #
63
+ # class Person
64
+ # include Mongoid::Document
65
+ # key :first_name, :last_name
66
+ # end
67
+ def key(*fields)
68
+ self.primary_key = fields
69
+ before_save :identify
70
+ end
71
+
72
+ # Returns all types to query for when using this class as the base.
73
+ def _types
74
+ @_type ||= (self.subclasses + [ self.name ])
75
+ end
76
+
77
+ # return the list of subclassses for an object
78
+ def subclasses_of(*superclasses) #:nodoc:
79
+ subclasses = []
80
+ superclasses.each do |sup|
81
+ ObjectSpace.each_object(class << sup; self; end) do |k|
82
+ if k != sup && (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
83
+ subclasses << k
84
+ end
85
+ end
86
+ end
87
+ subclasses
88
+ end
89
+ end
90
+
91
+ module InstanceMethods
92
+ # Performs equality checking on the document ids. For more robust
93
+ # equality checking please override this method.
94
+ def ==(other)
95
+ return false unless other.is_a?(Document)
96
+ id == other.id
97
+ end
98
+
99
+ # Delegates to ==
100
+ def eql?(comparison_object)
101
+ self == (comparison_object)
102
+ end
103
+
104
+ # Delegates to id in order to allow two records of the same type and id to work with something like:
105
+ # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
106
+ def hash
107
+ id.hash
108
+ end
109
+
110
+ # Is inheritance in play here?
111
+ #
112
+ # Returns:
113
+ #
114
+ # <tt>true</tt> if inheritance used, <tt>false</tt> if not.
115
+ def hereditary?
116
+ !!self.hereditary
117
+ end
118
+
119
+ # Introduces a child object into the +Document+ object graph. This will
120
+ # set up the relationships between the parent and child and update the
121
+ # attributes of the parent +Document+.
122
+ #
123
+ # Options:
124
+ #
125
+ # parent: The +Document+ to assimilate with.
126
+ # options: The association +Options+ for the child.
127
+ def assimilate(parent, options)
128
+ parentize(parent, options.name); notify; self
129
+ end
130
+
131
+ # Return the attributes hash with indifferent access.
132
+ def attributes
133
+ @attributes.with_indifferent_access
134
+ end
135
+
136
+ # Clone the current +Document+. This will return all attributes with the
137
+ # exception of the document's id and versions.
138
+ def clone
139
+ self.class.instantiate(@attributes.except("_id").except("versions").dup, true)
140
+ end
141
+
142
+ # Generate an id for this +Document+.
143
+ def identify
144
+ Identity.create(self)
145
+ end
146
+
147
+ # Instantiate a new +Document+, setting the Document's attributes if
148
+ # given. If no attributes are provided, they will be initialized with
149
+ # an empty +Hash+.
150
+ #
151
+ # If a primary key is defined, the document's id will be set to that key,
152
+ # otherwise it will be set to a fresh +BSON::ObjectId+ string.
153
+ #
154
+ # Options:
155
+ #
156
+ # attrs: The attributes +Hash+ to set up the document with.
157
+ def initialize(attrs = nil)
158
+ @attributes = default_attributes
159
+ process(attrs)
160
+ @new_record = true
161
+ document = yield self if block_given?
162
+ identify
163
+ end
164
+
165
+ # Returns the class name plus its attributes.
166
+ def inspect
167
+ attrs = fields.map { |name, field| "#{name}: #{@attributes[name].inspect}" }
168
+ if Mongoid.allow_dynamic_fields
169
+ dynamic_keys = @attributes.keys - fields.keys - associations.keys - ["_id", "_type"]
170
+ attrs += dynamic_keys.map { |name| "#{name}: #{@attributes[name].inspect}" }
171
+ end
172
+ "#<#{self.class.name} _id: #{id}, #{attrs * ', '}>"
173
+ end
174
+
175
+ # Notify observers of an update.
176
+ #
177
+ # Example:
178
+ #
179
+ # <tt>person.notify</tt>
180
+ def notify
181
+ notify_observers(self)
182
+ end
183
+
184
+ # Sets up a child/parent association. This is used for newly created
185
+ # objects so they can be properly added to the graph and have the parent
186
+ # observers set up properly.
187
+ #
188
+ # Options:
189
+ #
190
+ # abject: The parent object that needs to be set for the child.
191
+ # association_name: The name of the association for the child.
192
+ #
193
+ # Example:
194
+ #
195
+ # <tt>address.parentize(person, :addresses)</tt>
196
+ def parentize(object, association_name)
197
+ self._parent = object
198
+ self.association_name = association_name.to_s
199
+ add_observer(object)
200
+ end
201
+
202
+ # Return the attributes hash.
203
+ def raw_attributes
204
+ @attributes
205
+ end
206
+
207
+ # Reloads the +Document+ attributes from the database.
208
+ def reload
209
+ reloaded = collection.find_one(:_id => id)
210
+ if Mongoid.raise_not_found_error
211
+ raise Errors::DocumentNotFound.new(self.class, id) if reloaded.nil?
212
+ end
213
+ @attributes = {}.merge(reloaded || {})
214
+ self.associations.keys.each { |association_name| unmemoize(association_name) }; self
215
+ end
216
+
217
+ # Remove a child document from this parent +Document+. Will reset the
218
+ # memoized association and notify the parent of the change.
219
+ def remove(child)
220
+ name = child.association_name
221
+ reset(name) { @attributes.remove(name, child.raw_attributes) }
222
+ notify
223
+ end
224
+
225
+ # Return the root +Document+ in the object graph. If the current +Document+
226
+ # is the root object in the graph it will return self.
227
+ def _root
228
+ object = self
229
+ while (object._parent) do object = object._parent; end
230
+ object || self
231
+ end
232
+
233
+ # Return an array with this +Document+ only in it.
234
+ def to_a
235
+ [ self ]
236
+ end
237
+
238
+ # Return this document as a JSON string. Nothing special is required here
239
+ # since Mongoid bubbles up all the child associations to the parent
240
+ # attribute +Hash+ using observers throughout the +Document+ lifecycle.
241
+ #
242
+ # Example:
243
+ #
244
+ # <tt>person.to_json</tt>
245
+ def to_json(options = nil)
246
+ attributes.to_json(options)
247
+ end
248
+
249
+ # Return an object to be encoded into a JSON string.
250
+ # Used by Rails 3's object->JSON chain to create JSON
251
+ # in a backend-agnostic way
252
+ #
253
+ # Example:
254
+ #
255
+ # <tt>person.as_json</tt>
256
+ def as_json(options = nil)
257
+ attributes
258
+ end
259
+
260
+ # Return this document as an object to be encoded as JSON,
261
+ # with any particular items modified on a per-encoder basis.
262
+ # Nothing special is required here since Mongoid bubbles up
263
+ # all the child associations to the parent attribute +Hash+
264
+ # using observers throughout the +Document+ lifecycle.
265
+ #
266
+ # Example:
267
+ #
268
+ # <tt>person.encode_json(encoder)</tt>
269
+ def encode_json(encoder)
270
+ attributes
271
+ end
272
+
273
+ # Returns the id of the Document, used in Rails compatibility.
274
+ def to_param
275
+ id
276
+ end
277
+
278
+ # Observe a notify call from a child +Document+. This will either update
279
+ # existing attributes on the +Document+ or clear them out for the child if
280
+ # the clear boolean is provided.
281
+ #
282
+ # Options:
283
+ #
284
+ # child: The child +Document+ that sent the notification.
285
+ # clear: Will clear out the child's attributes if set to true.
286
+ #
287
+ # This will also cause the observing +Document+ to notify it's parent if
288
+ # there is any.
289
+ def observe(child, clear = false)
290
+ name = child.association_name
291
+ attrs = child.instance_variable_get(:@attributes)
292
+ if clear
293
+ @attributes.delete(name)
294
+ else
295
+ @attributes.insert(name, attrs) unless @attributes[name] && @attributes[name].include?(attrs)
296
+ end
297
+ notify
298
+ end
299
+
300
+ protected
301
+ # apply default values to attributes - calling procs as required
302
+ def attributes_with_defaults(attributes = {})
303
+ default_values = defaults
304
+ default_values.each_pair do |key, val|
305
+ default_values[key] = val.call if val.respond_to?(:call)
306
+ end
307
+ default_values.merge(attributes)
308
+ end
309
+ end
310
+ end
311
+ end
@@ -0,0 +1,108 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+ module Errors #:nodoc
4
+
5
+ # Raised when querying the database for a document by a specific id which
6
+ # does not exist. If multiple ids were passed then it will display all of
7
+ # those.
8
+ #
9
+ # Example:
10
+ #
11
+ # <tt>DocumentNotFound.new(Person, ["1", "2"])</tt>
12
+ class DocumentNotFound < RuntimeError
13
+ def initialize(klass, ids)
14
+ @klass, @identifier = klass, ids.is_a?(Array) ? ids.join(", ") : ids
15
+ end
16
+ def message
17
+ "Document not found for class #{@klass} and id(s) #{@identifier}"
18
+ end
19
+ end
20
+
21
+ # Raised when invalid options are passed into a constructor or method.
22
+ #
23
+ # Example:
24
+ #
25
+ # <tt>InvalidOptions.new</tt>
26
+ class InvalidOptions < RuntimeError; end
27
+
28
+ # Raised when the database connection has not been set up properly, either
29
+ # by attempting to set an object on the db that is not a +Mongo::DB+, or
30
+ # not setting anything at all.
31
+ #
32
+ # Example:
33
+ #
34
+ # <tt>InvalidDatabase.new("Not a DB")</tt>
35
+ class InvalidDatabase < RuntimeError
36
+ def initialize(database)
37
+ @database = database
38
+ end
39
+ def message
40
+ "Database should be a Mongo::DB, not #{@database.class.name}"
41
+ end
42
+ end
43
+
44
+ # Raised when the database version is not supported by Mongoid.
45
+ #
46
+ # Example:
47
+ #
48
+ # <tt>UnsupportedVersion.new(Mongo::ServerVersion.new("1.3.1"))</tt>
49
+ class UnsupportedVersion < RuntimeError
50
+ def initialize(version)
51
+ @version = version
52
+ end
53
+ def message
54
+ "MongoDB #{@version} not supported, please upgrade to #{Mongoid::MONGODB_VERSION}"
55
+ end
56
+ end
57
+
58
+ # Raised when a persisence method ending in ! fails validation. The message
59
+ # will contain the full error messages from the +Document+ in question.
60
+ #
61
+ # Example:
62
+ #
63
+ # <tt>Validations.new(person.errors)</tt>
64
+ class Validations < RuntimeError
65
+ def initialize(errors)
66
+ @errors = errors
67
+ end
68
+ def message
69
+ "Validation Failed: #{@errors.full_messages.join(", ")}"
70
+ end
71
+ end
72
+
73
+ # This error is raised when trying to access a Mongo::Collection from an
74
+ # embedded document.
75
+ #
76
+ # Example:
77
+ #
78
+ # <tt>InvalidCollection.new(Address)</tt>
79
+ class InvalidCollection < RuntimeError
80
+ def initialize(klass)
81
+ @klass = klass
82
+ end
83
+ def message
84
+ "Access to the collection for #{@klass.name} is not allowed " +
85
+ "since it is an embedded document, please access a collection from " +
86
+ "the root document"
87
+ end
88
+ end
89
+
90
+ # This error is raised when trying to create a field that conflicts with
91
+ # a Mongoid internal attribute or method.
92
+ #
93
+ # Example:
94
+ #
95
+ # <tt>InvalidField.new('collection')</tt>
96
+ class InvalidField < RuntimeError
97
+ def initialize(name)
98
+ @name = name
99
+ end
100
+ def message
101
+ "Defining a field named '#{@name}' is not allowed. " +
102
+ "Do not define fields that conflict with Mongoid internal attributes " +
103
+ "or method names. Use Document#instance_methods to see what " +
104
+ "names this includes."
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Array #:nodoc:
5
+ module Accessors #:nodoc:
6
+ # If the attributes already exists in the array then they will be
7
+ # updated, otherwise they will be appended.
8
+ def update(attributes)
9
+ delete_if { |e| attributes["_id"] && (e["_id"] == attributes["_id"]) }
10
+ self.<< attributes
11
+ end
12
+
13
+ alias :merge! :update
14
+ end
15
+ end
16
+ end
17
+ end