mongoid-pre 2.0.0.beta1

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 (246) hide show
  1. data/.gitignore +6 -0
  2. data/.watchr +24 -0
  3. data/MIT_LICENSE +20 -0
  4. data/README.rdoc +49 -0
  5. data/Rakefile +52 -0
  6. data/VERSION +1 -0
  7. data/caliper.yml +4 -0
  8. data/lib/mongoid.rb +135 -0
  9. data/lib/mongoid/associations.rb +263 -0
  10. data/lib/mongoid/associations/belongs_to_related.rb +59 -0
  11. data/lib/mongoid/associations/embedded_in.rb +64 -0
  12. data/lib/mongoid/associations/embeds_many.rb +193 -0
  13. data/lib/mongoid/associations/embeds_one.rb +95 -0
  14. data/lib/mongoid/associations/has_many_related.rb +133 -0
  15. data/lib/mongoid/associations/has_one_related.rb +81 -0
  16. data/lib/mongoid/associations/meta_data.rb +28 -0
  17. data/lib/mongoid/associations/options.rb +52 -0
  18. data/lib/mongoid/associations/proxy.rb +31 -0
  19. data/lib/mongoid/attributes.rb +185 -0
  20. data/lib/mongoid/callbacks.rb +18 -0
  21. data/lib/mongoid/collection.rb +119 -0
  22. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  23. data/lib/mongoid/collections/master.rb +28 -0
  24. data/lib/mongoid/collections/mimic.rb +46 -0
  25. data/lib/mongoid/collections/operations.rb +41 -0
  26. data/lib/mongoid/collections/slaves.rb +44 -0
  27. data/lib/mongoid/commands.rb +161 -0
  28. data/lib/mongoid/commands/create.rb +19 -0
  29. data/lib/mongoid/commands/delete.rb +16 -0
  30. data/lib/mongoid/commands/delete_all.rb +25 -0
  31. data/lib/mongoid/commands/deletion.rb +18 -0
  32. data/lib/mongoid/commands/destroy.rb +17 -0
  33. data/lib/mongoid/commands/destroy_all.rb +25 -0
  34. data/lib/mongoid/commands/save.rb +30 -0
  35. data/lib/mongoid/components.rb +31 -0
  36. data/lib/mongoid/config.rb +86 -0
  37. data/lib/mongoid/contexts.rb +25 -0
  38. data/lib/mongoid/contexts/enumerable.rb +151 -0
  39. data/lib/mongoid/contexts/ids.rb +25 -0
  40. data/lib/mongoid/contexts/mongo.rb +285 -0
  41. data/lib/mongoid/contexts/paging.rb +42 -0
  42. data/lib/mongoid/criteria.rb +239 -0
  43. data/lib/mongoid/criterion/complex.rb +21 -0
  44. data/lib/mongoid/criterion/exclusion.rb +65 -0
  45. data/lib/mongoid/criterion/inclusion.rb +93 -0
  46. data/lib/mongoid/criterion/optional.rb +136 -0
  47. data/lib/mongoid/cursor.rb +82 -0
  48. data/lib/mongoid/deprecation.rb +22 -0
  49. data/lib/mongoid/dirty.rb +203 -0
  50. data/lib/mongoid/document.rb +306 -0
  51. data/lib/mongoid/errors.rb +77 -0
  52. data/lib/mongoid/extensions.rb +99 -0
  53. data/lib/mongoid/extensions/array/accessors.rb +17 -0
  54. data/lib/mongoid/extensions/array/aliasing.rb +4 -0
  55. data/lib/mongoid/extensions/array/assimilation.rb +26 -0
  56. data/lib/mongoid/extensions/array/conversions.rb +27 -0
  57. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  58. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  59. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  60. data/lib/mongoid/extensions/boolean/conversions.rb +16 -0
  61. data/lib/mongoid/extensions/date/conversions.rb +15 -0
  62. data/lib/mongoid/extensions/datetime/conversions.rb +17 -0
  63. data/lib/mongoid/extensions/float/conversions.rb +16 -0
  64. data/lib/mongoid/extensions/hash/accessors.rb +38 -0
  65. data/lib/mongoid/extensions/hash/assimilation.rb +30 -0
  66. data/lib/mongoid/extensions/hash/conversions.rb +15 -0
  67. data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
  68. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  69. data/lib/mongoid/extensions/integer/conversions.rb +16 -0
  70. data/lib/mongoid/extensions/nil/assimilation.rb +13 -0
  71. data/lib/mongoid/extensions/object/conversions.rb +27 -0
  72. data/lib/mongoid/extensions/objectid/conversions.rb +15 -0
  73. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  74. data/lib/mongoid/extensions/string/conversions.rb +15 -0
  75. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  76. data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
  77. data/lib/mongoid/extensions/time/conversions.rb +18 -0
  78. data/lib/mongoid/extras.rb +61 -0
  79. data/lib/mongoid/factory.rb +19 -0
  80. data/lib/mongoid/field.rb +52 -0
  81. data/lib/mongoid/fields.rb +62 -0
  82. data/lib/mongoid/finders.rb +136 -0
  83. data/lib/mongoid/identity.rb +39 -0
  84. data/lib/mongoid/indexes.rb +27 -0
  85. data/lib/mongoid/javascript.rb +21 -0
  86. data/lib/mongoid/javascript/functions.yml +37 -0
  87. data/lib/mongoid/matchers.rb +36 -0
  88. data/lib/mongoid/matchers/all.rb +11 -0
  89. data/lib/mongoid/matchers/default.rb +26 -0
  90. data/lib/mongoid/matchers/exists.rb +13 -0
  91. data/lib/mongoid/matchers/gt.rb +11 -0
  92. data/lib/mongoid/matchers/gte.rb +11 -0
  93. data/lib/mongoid/matchers/in.rb +11 -0
  94. data/lib/mongoid/matchers/lt.rb +11 -0
  95. data/lib/mongoid/matchers/lte.rb +11 -0
  96. data/lib/mongoid/matchers/ne.rb +11 -0
  97. data/lib/mongoid/matchers/nin.rb +11 -0
  98. data/lib/mongoid/matchers/size.rb +11 -0
  99. data/lib/mongoid/memoization.rb +27 -0
  100. data/lib/mongoid/named_scope.rb +42 -0
  101. data/lib/mongoid/observable.rb +30 -0
  102. data/lib/mongoid/paths.rb +54 -0
  103. data/lib/mongoid/persistence.rb +27 -0
  104. data/lib/mongoid/persistence/command.rb +20 -0
  105. data/lib/mongoid/persistence/insert.rb +71 -0
  106. data/lib/mongoid/persistence/update.rb +78 -0
  107. data/lib/mongoid/scope.rb +75 -0
  108. data/lib/mongoid/state.rb +32 -0
  109. data/lib/mongoid/timestamps.rb +27 -0
  110. data/lib/mongoid/validations.rb +51 -0
  111. data/lib/mongoid/validations/associated.rb +32 -0
  112. data/lib/mongoid/validations/locale/en.yml +4 -0
  113. data/lib/mongoid/validations/uniqueness.rb +22 -0
  114. data/lib/mongoid/versioning.rb +26 -0
  115. data/mongoid.gemspec +413 -0
  116. data/perf/benchmark.rb +77 -0
  117. data/spec/integration/mongoid/associations_spec.rb +340 -0
  118. data/spec/integration/mongoid/attributes_spec.rb +22 -0
  119. data/spec/integration/mongoid/commands_spec.rb +230 -0
  120. data/spec/integration/mongoid/contexts/enumerable_spec.rb +33 -0
  121. data/spec/integration/mongoid/criteria_spec.rb +272 -0
  122. data/spec/integration/mongoid/dirty_spec.rb +70 -0
  123. data/spec/integration/mongoid/document_spec.rb +650 -0
  124. data/spec/integration/mongoid/extensions_spec.rb +22 -0
  125. data/spec/integration/mongoid/finders_spec.rb +119 -0
  126. data/spec/integration/mongoid/inheritance_spec.rb +137 -0
  127. data/spec/integration/mongoid/named_scope_spec.rb +46 -0
  128. data/spec/integration/mongoid/persistence/update_spec.rb +46 -0
  129. data/spec/models/address.rb +39 -0
  130. data/spec/models/animal.rb +6 -0
  131. data/spec/models/callbacks.rb +18 -0
  132. data/spec/models/comment.rb +8 -0
  133. data/spec/models/country_code.rb +6 -0
  134. data/spec/models/employer.rb +5 -0
  135. data/spec/models/game.rb +7 -0
  136. data/spec/models/inheritance.rb +56 -0
  137. data/spec/models/location.rb +5 -0
  138. data/spec/models/mixed_drink.rb +4 -0
  139. data/spec/models/name.rb +13 -0
  140. data/spec/models/namespacing.rb +11 -0
  141. data/spec/models/patient.rb +6 -0
  142. data/spec/models/person.rb +99 -0
  143. data/spec/models/pet.rb +7 -0
  144. data/spec/models/pet_owner.rb +6 -0
  145. data/spec/models/phone.rb +7 -0
  146. data/spec/models/post.rb +15 -0
  147. data/spec/models/translation.rb +5 -0
  148. data/spec/models/vet_visit.rb +5 -0
  149. data/spec/spec.opts +3 -0
  150. data/spec/spec_helper.rb +31 -0
  151. data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +145 -0
  152. data/spec/unit/mongoid/associations/embedded_in_spec.rb +193 -0
  153. data/spec/unit/mongoid/associations/embeds_many_spec.rb +516 -0
  154. data/spec/unit/mongoid/associations/embeds_one_spec.rb +282 -0
  155. data/spec/unit/mongoid/associations/has_many_related_spec.rb +418 -0
  156. data/spec/unit/mongoid/associations/has_one_related_spec.rb +179 -0
  157. data/spec/unit/mongoid/associations/meta_data_spec.rb +88 -0
  158. data/spec/unit/mongoid/associations/options_spec.rb +192 -0
  159. data/spec/unit/mongoid/associations_spec.rb +595 -0
  160. data/spec/unit/mongoid/attributes_spec.rb +507 -0
  161. data/spec/unit/mongoid/callbacks_spec.rb +55 -0
  162. data/spec/unit/mongoid/collection_spec.rb +187 -0
  163. data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
  164. data/spec/unit/mongoid/collections/master_spec.rb +41 -0
  165. data/spec/unit/mongoid/collections/mimic_spec.rb +43 -0
  166. data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
  167. data/spec/unit/mongoid/commands/create_spec.rb +31 -0
  168. data/spec/unit/mongoid/commands/delete_all_spec.rb +59 -0
  169. data/spec/unit/mongoid/commands/delete_spec.rb +38 -0
  170. data/spec/unit/mongoid/commands/destroy_all_spec.rb +21 -0
  171. data/spec/unit/mongoid/commands/destroy_spec.rb +51 -0
  172. data/spec/unit/mongoid/commands/save_spec.rb +107 -0
  173. data/spec/unit/mongoid/commands_spec.rb +270 -0
  174. data/spec/unit/mongoid/config_spec.rb +176 -0
  175. data/spec/unit/mongoid/contexts/enumerable_spec.rb +421 -0
  176. data/spec/unit/mongoid/contexts/mongo_spec.rb +682 -0
  177. data/spec/unit/mongoid/contexts_spec.rb +25 -0
  178. data/spec/unit/mongoid/criteria_spec.rb +824 -0
  179. data/spec/unit/mongoid/criterion/complex_spec.rb +19 -0
  180. data/spec/unit/mongoid/criterion/exclusion_spec.rb +91 -0
  181. data/spec/unit/mongoid/criterion/inclusion_spec.rb +219 -0
  182. data/spec/unit/mongoid/criterion/optional_spec.rb +319 -0
  183. data/spec/unit/mongoid/cursor_spec.rb +74 -0
  184. data/spec/unit/mongoid/deprecation_spec.rb +24 -0
  185. data/spec/unit/mongoid/dirty_spec.rb +286 -0
  186. data/spec/unit/mongoid/document_spec.rb +818 -0
  187. data/spec/unit/mongoid/errors_spec.rb +103 -0
  188. data/spec/unit/mongoid/extensions/array/accessors_spec.rb +50 -0
  189. data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +24 -0
  190. data/spec/unit/mongoid/extensions/array/conversions_spec.rb +35 -0
  191. data/spec/unit/mongoid/extensions/array/parentization_spec.rb +20 -0
  192. data/spec/unit/mongoid/extensions/big_decimal/conversions_spec.rb +22 -0
  193. data/spec/unit/mongoid/extensions/binary/conversions_spec.rb +22 -0
  194. data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +49 -0
  195. data/spec/unit/mongoid/extensions/date/conversions_spec.rb +102 -0
  196. data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +67 -0
  197. data/spec/unit/mongoid/extensions/float/conversions_spec.rb +61 -0
  198. data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +184 -0
  199. data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +46 -0
  200. data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +21 -0
  201. data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +17 -0
  202. data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +14 -0
  203. data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +61 -0
  204. data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +24 -0
  205. data/spec/unit/mongoid/extensions/object/conversions_spec.rb +57 -0
  206. data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +34 -0
  207. data/spec/unit/mongoid/extensions/string/conversions_spec.rb +17 -0
  208. data/spec/unit/mongoid/extensions/string/inflections_spec.rb +208 -0
  209. data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +91 -0
  210. data/spec/unit/mongoid/extensions/time/conversions_spec.rb +70 -0
  211. data/spec/unit/mongoid/extras_spec.rb +102 -0
  212. data/spec/unit/mongoid/factory_spec.rb +31 -0
  213. data/spec/unit/mongoid/field_spec.rb +143 -0
  214. data/spec/unit/mongoid/fields_spec.rb +181 -0
  215. data/spec/unit/mongoid/finders_spec.rb +404 -0
  216. data/spec/unit/mongoid/identity_spec.rb +109 -0
  217. data/spec/unit/mongoid/indexes_spec.rb +93 -0
  218. data/spec/unit/mongoid/javascript_spec.rb +48 -0
  219. data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
  220. data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
  221. data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
  222. data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
  223. data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
  224. data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
  225. data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
  226. data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
  227. data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
  228. data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
  229. data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
  230. data/spec/unit/mongoid/matchers_spec.rb +329 -0
  231. data/spec/unit/mongoid/memoization_spec.rb +75 -0
  232. data/spec/unit/mongoid/named_scope_spec.rb +123 -0
  233. data/spec/unit/mongoid/observable_spec.rb +46 -0
  234. data/spec/unit/mongoid/paths_spec.rb +124 -0
  235. data/spec/unit/mongoid/persistence/insert_spec.rb +175 -0
  236. data/spec/unit/mongoid/persistence/update_spec.rb +148 -0
  237. data/spec/unit/mongoid/persistence_spec.rb +40 -0
  238. data/spec/unit/mongoid/scope_spec.rb +240 -0
  239. data/spec/unit/mongoid/state_spec.rb +83 -0
  240. data/spec/unit/mongoid/timestamps_spec.rb +25 -0
  241. data/spec/unit/mongoid/validations/associated_spec.rb +103 -0
  242. data/spec/unit/mongoid/validations/uniqueness_spec.rb +47 -0
  243. data/spec/unit/mongoid/validations_spec.rb +190 -0
  244. data/spec/unit/mongoid/versioning_spec.rb +41 -0
  245. data/spec/unit/mongoid_spec.rb +46 -0
  246. metadata +476 -0
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Matchers #:nodoc:
4
+ class In < Default
5
+ # Return true if the attribute is in the values.
6
+ def matches?(value)
7
+ value.values.first.include?(@attribute)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Matchers #:nodoc:
4
+ class Lt < Default
5
+ # Return true if the attribute is less than the value.
6
+ def matches?(value)
7
+ determine(value, :<)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Matchers #:nodoc:
4
+ class Lte < Default
5
+ # Return true if the attribute is less than or equal to the value.
6
+ def matches?(value)
7
+ determine(value, :<=)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Matchers #:nodoc:
4
+ class Ne < Default
5
+ # Return true if the attribute and first value are not equal.
6
+ def matches?(value)
7
+ @attribute != value.values.first
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Matchers #:nodoc:
4
+ class Nin < Default
5
+ # Return true if the attribute is not in the value list.
6
+ def matches?(value)
7
+ !value.values.first.include?(@attribute)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Matchers #:nodoc:
4
+ class Size < Default
5
+ # Return true if the attribute size is equal to the first value.
6
+ def matches?(value)
7
+ @attribute.size == value.values.first
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,27 @@
1
+ module Mongoid #:nodoc
2
+ module Memoization
3
+
4
+ # Handles cases when accessing an association that should be memoized in
5
+ # the Mongoid specific manner.
6
+ def memoized(name, &block)
7
+ var = "@#{name}"
8
+ if instance_variable_defined?(var)
9
+ return instance_variable_get(var)
10
+ end
11
+ value = yield
12
+ instance_variable_set(var, value)
13
+ end
14
+
15
+ # Mongoid specific behavior is to remove the memoized object when setting
16
+ # the association, or if it wasn't previously memoized it will get set.
17
+ def reset(name, &block)
18
+ var = "@#{name}"
19
+ value = yield
20
+ if instance_variable_defined?(var)
21
+ remove_instance_variable(var)
22
+ else
23
+ instance_variable_set(var, value)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module NamedScope
4
+ # Creates a named_scope for the +Document+, similar to ActiveRecord's
5
+ # named_scopes. +NamedScopes+ are proxied +Criteria+ objects that can be
6
+ # chained.
7
+ #
8
+ # Example:
9
+ #
10
+ # class Person
11
+ # include Mongoid::Document
12
+ # field :active, :type => Boolean
13
+ # field :count, :type => Integer
14
+ #
15
+ # named_scope :active, :where => { :active => true }
16
+ # named_scope :count_gt_one, :where => { :count.gt => 1 }
17
+ # named_scope :at_least_count, lambda { |count| { :where => { :count.gt => count } } }
18
+ # end
19
+ def named_scope(name, options = {}, &block)
20
+ name = name.to_sym
21
+ scopes[name] = lambda do |parent, *args|
22
+ Scope.new(parent, options.scoped(*args), &block)
23
+ end
24
+ (class << self; self; end).class_eval <<-EOT
25
+ def #{name}(*args)
26
+ scopes[:#{name}].call(self, *args)
27
+ end
28
+ EOT
29
+ end
30
+
31
+ alias :scope :named_scope
32
+
33
+ alias :scope :named_scope
34
+
35
+ # Return the scopes or default to an empty +Hash+.
36
+ def scopes
37
+ read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
38
+ end
39
+
40
+ end
41
+ end
42
+
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Observable #:nodoc:
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ attr_reader :observers
7
+ end
8
+
9
+ # Add an observer to this object. This mimics the standard Ruby observable
10
+ # library.
11
+ #
12
+ # Example:
13
+ #
14
+ # <tt>address.add_observer(person)</tt>
15
+ def add_observer(object)
16
+ @observers ||= []
17
+ @observers.push(object)
18
+ end
19
+
20
+ # Notify all the objects observing this object of an update. All observers
21
+ # need to respond to the update method in order to handle this.
22
+ #
23
+ # Example:
24
+ #
25
+ # <tt>document.notify_observers(self)</tt>
26
+ def notify_observers(*args)
27
+ @observers.dup.each { |observer| observer.observe(*args) } if @observers
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Paths #:nodoc:
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ cattr_accessor :_path, :_position
7
+ delegate :_path, :_position, :to => "self.class"
8
+ end
9
+ module InstanceMethods
10
+ # Return the path to this +Document+ in JSON notation, used for atomic
11
+ # updates via $set in MongoDB.
12
+ #
13
+ # Example:
14
+ #
15
+ # <tt>address.path # returns "addresses"</tt>
16
+ def path
17
+ self._path ||= climb("") do |document, value|
18
+ value = "#{document.association_name}#{"." + value unless value.blank?}"
19
+ end
20
+ end
21
+
22
+ # Returns the positional operator of this document for modification.
23
+ #
24
+ # Example:
25
+ #
26
+ # <tt>address.position</tt>
27
+ def position
28
+ self._position ||= (path.blank? ? "" : "#{path}.$")
29
+ end
30
+
31
+ # Return the selector for this document to be matched exactly for use
32
+ # with MongoDB's $ operator.
33
+ #
34
+ # Example:
35
+ #
36
+ # <tt>address.selector</tt>
37
+ def selector
38
+ @selector ||= climb({ "_id" => _root.id }) do |document, value|
39
+ value["#{document.path}._id"] = document.id; value
40
+ end
41
+ end
42
+
43
+ protected
44
+ def climb(value, &block)
45
+ document = self;
46
+ while (document._parent) do
47
+ value = yield document, value
48
+ document = document._parent
49
+ end
50
+ value
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ require "mongoid/persistence/command"
3
+ require "mongoid/persistence/insert"
4
+ require "mongoid/persistence/update"
5
+
6
+ module Mongoid #:nodoc:
7
+ module Persistence #:nodoc:
8
+ # Insert a new +Document+ into the database. Will return the document
9
+ # itself whether or not the save was successful.
10
+ #
11
+ # Example:
12
+ #
13
+ # <tt>document.insert</tt>
14
+ def insert
15
+ Insert.new(self).persist
16
+ end
17
+
18
+ # Update the +Document+ in the datbase.
19
+ #
20
+ # Example:
21
+ #
22
+ # <tt>document.update</tt>
23
+ def update
24
+ Update.new(self).persist
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Persistence #:nodoc:
4
+ # Mixin for common functionality for all persistence commands.
5
+ module Command
6
+ attr_reader :collection, :document, :options, :validate
7
+ # Initialize the persistence +Command+.
8
+ #
9
+ # Options:
10
+ #
11
+ # document: The +Document+ to be persisted.
12
+ # validate: Is the document to be validated.
13
+ def init(document, validate)
14
+ @collection = document.embedded ? document._root.collection : document.collection
15
+ @document = document
16
+ @validate = validate
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,71 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Persistence #:nodoc:
4
+ # Insert is a persistence command responsible for taking a document that
5
+ # has not been saved to the database and saving it.
6
+ #
7
+ # The underlying query resembles the following MongoDB query:
8
+ #
9
+ # collection.insert(
10
+ # { "_id" : 1, "field" : "value" },
11
+ # false
12
+ # );
13
+ class Insert
14
+ include Persistence::Command
15
+
16
+ # Create the new insert persister.
17
+ #
18
+ # Options:
19
+ #
20
+ # document: The +Document+ to persist.
21
+ # validate: +Boolean+ to validate or not.
22
+ #
23
+ # Example:
24
+ #
25
+ # <tt>Insert.new(document)</tt>
26
+ def initialize(document, validate = true)
27
+ init(document, validate)
28
+ @options = { :safe => Mongoid.persist_in_safe_mode }
29
+ end
30
+
31
+ # Insert the new document in the database. This delegates to the standard
32
+ # MongoDB collection's insert command.
33
+ #
34
+ # Example:
35
+ #
36
+ # <tt>Insert.persist</tt>
37
+ #
38
+ # Returns:
39
+ #
40
+ # The +Document+, whether the insert succeeded or not.
41
+ def persist
42
+ return @document if validate && !@document.valid?
43
+ @document.run_callbacks(:create, :save) do
44
+ @document.new_record = false if insert
45
+ @document
46
+ end
47
+ end
48
+
49
+ protected
50
+ # Insert the document into the database.
51
+ def insert
52
+ if @document.embedded
53
+ insert_embedded
54
+ else
55
+ collection.insert(@document.raw_attributes, options)
56
+ end
57
+ end
58
+
59
+ # Logic for determining how to insert the embedded doc.
60
+ def insert_embedded
61
+ parent = @document._parent
62
+ if parent.new_record?
63
+ @document.notify_observers(document, true); parent.insert
64
+ else
65
+ update = { @document.path => { "$push" => @document.raw_attributes } }
66
+ collection.update(parent.selector, update, @options)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Persistence #:nodoc:
4
+ # Update is a persistence command responsible for taking a document that
5
+ # has already been saved to the database and saving it, depending on
6
+ # whether or not the document has been modified.
7
+ #
8
+ # Before persisting the command will check via dirty attributes if the
9
+ # document has changed, if not, it will simply return true. If it has it
10
+ # will go through the validation steps, run callbacks, and set the changed
11
+ # fields atomically on the document. The underlying query resembles the
12
+ # following MongoDB query:
13
+ #
14
+ # collection.update(
15
+ # { "_id" : 1,
16
+ # { "$set" : { "field" : "value" },
17
+ # false,
18
+ # false
19
+ # );
20
+ #
21
+ # For embedded documents it will use the positional locator:
22
+ #
23
+ # collection.update(
24
+ # { "_id" : 1, "addresses._id" : 2 },
25
+ # { "$set" : { "addresses.$.field" : "value" },
26
+ # false,
27
+ # false
28
+ # );
29
+ #
30
+ class Update
31
+ include Persistence::Command
32
+
33
+ # Create the new update persister.
34
+ #
35
+ # Options:
36
+ #
37
+ # document: The +Document+ to persist.
38
+ # validate: +Boolean+ to validate or not.
39
+ #
40
+ # Example:
41
+ #
42
+ # <tt>Update.new(document)</tt>
43
+ def initialize(document, validate = true)
44
+ init(document, validate)
45
+ @options = { :multi => false, :safe => Mongoid.persist_in_safe_mode }
46
+ end
47
+
48
+ # Persist the document that is to be updated to the database. This will
49
+ # only write changed fields via MongoDB's $set modifier operation.
50
+ #
51
+ # Example:
52
+ #
53
+ # <tt>Update.persist</tt>
54
+ #
55
+ # Returns:
56
+ #
57
+ # +true+ or +false+, depending on validation.
58
+ def persist
59
+ if @document.changed?
60
+ return false if validate && !@document.valid?
61
+ @document.run_callbacks(:save, :update) do
62
+ if update
63
+ @document.move_changes
64
+ else
65
+ return false
66
+ end
67
+ end
68
+ end; true
69
+ end
70
+
71
+ protected
72
+ # Update the document in the database atomically.
73
+ def update
74
+ collection.update(@document.selector, { "$set" => @document.setters }, @options)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,75 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ class Scope #:nodoc:
4
+
5
+ delegate :scopes, :to => :parent
6
+
7
+ attr_reader :parent, :conditions
8
+
9
+ # If the other is a scope then compare the parent and conditions, otherwise
10
+ # if its enumerable collect and compare.
11
+ def ==(other)
12
+ case other
13
+ when Scope
14
+ @parent == other.parent && @conditions == other.conditions
15
+ when Enumerable
16
+ @collection ||= entries
17
+ return (@collection == other)
18
+ else
19
+ return false
20
+ end
21
+ end
22
+
23
+ # Create the new +Scope+. If a block is passed in, this Scope will extend
24
+ # the block.
25
+ #
26
+ # Options:
27
+ #
28
+ # parent: The class the scope belongs to, or a parent +Scope+.
29
+ # conditions: A +Hash+ of conditions.
30
+ #
31
+ # Example:
32
+ #
33
+ # Mongoid::Scope.new(Person, { :title => "Sir" }) do
34
+ # def knighted?
35
+ # title == "Sir"
36
+ # end
37
+ # end
38
+ def initialize(parent, conditions, &block)
39
+ @parent, @conditions = parent, conditions
40
+ extend Module.new(&block) if block_given?
41
+ end
42
+
43
+ # Return the class for the +Scope+. This will be the parent if the parent
44
+ # is a class, otherwise will be nil.
45
+ def klass
46
+ @klass ||= @parent unless @parent.is_a?(Scope)
47
+ end
48
+
49
+ # Chaining is supported through method_missing. If a scope is already
50
+ # defined with the method name the call will be passed there, otherwise it
51
+ # will be passed to the target or parent.
52
+ def method_missing(name, *args, &block)
53
+ if scopes.include?(name)
54
+ scopes[name].call(self, *args)
55
+ elsif klass
56
+ target.send(name, *args, &block)
57
+ else
58
+ @parent.fuse(@conditions); @parent.send(name, *args, &block)
59
+ end
60
+ end
61
+
62
+ # The +Scope+ must respond like a +Criteria+ object. If this is a parent
63
+ # criteria delegate to the target, otherwise bubble up to the parent.
64
+ def respond_to?(name)
65
+ super || (klass ? target.respond_to?(name) : @parent.respond_to?(name))
66
+ end
67
+
68
+ # Returns the target criteria if it has already been set or creates a new
69
+ # criteria from the parent class.
70
+ def target
71
+ @target ||= klass.criteria.fuse(@conditions)
72
+ end
73
+ end
74
+ end
75
+