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,81 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ class HasOneRelated #:nodoc:
5
+ include Proxy
6
+
7
+ delegate :nil?, :to => :target
8
+
9
+ # Builds a new Document and sets it as the association.
10
+ #
11
+ # Returns the newly created object.
12
+ def build(attributes = {})
13
+ @target = @klass.instantiate(attributes)
14
+ name = @parent.class.to_s.underscore
15
+ @target.send("#{name}=", @parent)
16
+ @target
17
+ end
18
+
19
+ # Builds a new Document and sets it as the association, then saves the
20
+ # newly created document.
21
+ #
22
+ # Returns the newly created object.
23
+ def create(attributes)
24
+ build(attributes); @target.save; @target
25
+ end
26
+
27
+ # Initializing a related association only requires looking up the objects
28
+ # by their ids.
29
+ #
30
+ # Options:
31
+ #
32
+ # document: The +Document+ that contains the relationship.
33
+ # options: The association +Options+.
34
+ def initialize(document, options, target = nil)
35
+ @parent, @klass = document, options.klass
36
+ @foreign_key = document.class.to_s.foreign_key
37
+ @target = target || @klass.first(:conditions => { @foreign_key => @parent.id })
38
+ extends(options)
39
+ end
40
+
41
+ class << self
42
+ # Preferred method for creating the new +RelatesToMany+ association.
43
+ #
44
+ # Options:
45
+ #
46
+ # document: The +Document+ that contains the relationship.
47
+ # options: The association +Options+.
48
+ def instantiate(document, options, target = nil)
49
+ new(document, options, target)
50
+ end
51
+
52
+ # Returns the macro used to create the association.
53
+ def macro
54
+ :has_one_related
55
+ end
56
+
57
+ # Perform an update of the relationship of the parent and child. This
58
+ # will assimilate the child +Document+ into the parent's object graph.
59
+ #
60
+ # Options:
61
+ #
62
+ # related: The related object to update.
63
+ # document: The parent +Document+.
64
+ # options: The association +Options+
65
+ #
66
+ # Example:
67
+ #
68
+ # <tt>HasOneToRelated.update(game, person, options)</tt>
69
+ def update(target, document, options)
70
+ if target
71
+ name = document.class.to_s.underscore
72
+ target.send("#{name}=", document)
73
+ return instantiate(document, options, target)
74
+ end
75
+ target
76
+ end
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ class MetaData #:nodoc:
5
+
6
+ attr_reader :association, :options
7
+
8
+ delegate :macro, :to => :association
9
+
10
+ # Delegate all methods on +Options+ to the options instance.
11
+ Associations::Options.public_instance_methods(false).each do |name|
12
+ define_method(name) { |*args| @options.send(name) }
13
+ end
14
+
15
+ # Create the new associations MetaData object, which holds the type of
16
+ # the association and its options, with convenience methods for getting
17
+ # that information.
18
+ #
19
+ # Options:
20
+ #
21
+ # association: The association type as a class instance.
22
+ # options: The association options
23
+ def initialize(association, options)
24
+ @association, @options = association, options
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ class Options #:nodoc:
5
+
6
+ # Create the new +Options+ object, which provides convenience methods for
7
+ # accessing values out of an options +Hash+.
8
+ def initialize(attributes = {})
9
+ @attributes = attributes
10
+ end
11
+
12
+ # Returns the extension if it exists, nil if not.
13
+ def extension
14
+ @attributes[:extend]
15
+ end
16
+
17
+ # Returns true is the options have extensions.
18
+ def extension?
19
+ !extension.nil?
20
+ end
21
+
22
+ # Return the foreign key based off the association name.
23
+ def foreign_key
24
+ @attributes[:foreign_key] || klass.name.to_s.foreign_key
25
+ end
26
+
27
+ # Returns the name of the inverse_of association
28
+ def inverse_of
29
+ @attributes[:inverse_of]
30
+ end
31
+
32
+ # Return a +Class+ for the options. If a class_name was provided, then the
33
+ # constantized class_name will be returned. If not, a constant based on the
34
+ # association name will be returned.
35
+ def klass
36
+ class_name = @attributes[:class_name]
37
+ class_name ? class_name.constantize : name.to_s.classify.constantize
38
+ end
39
+
40
+ # Returns the association name of the options.
41
+ def name
42
+ @attributes[:name].to_s
43
+ end
44
+
45
+ # Returns whether or not this association is polymorphic.
46
+ def polymorphic
47
+ @attributes[:polymorphic] == true
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+ module Associations #:nodoc
4
+ module Proxy #:nodoc
5
+ def self.included(base)
6
+ base.class_eval do
7
+ instance_methods.each do |method|
8
+ undef_method(method) unless method =~ /(^__|^nil\?$|^send$|^object_id$|^extend$)/
9
+ end
10
+ include InstanceMethods
11
+ end
12
+ end
13
+ module InstanceMethods #:nodoc:
14
+ attr_reader \
15
+ :options,
16
+ :target
17
+
18
+ # Default behavior of method missing should be to delegate all calls
19
+ # to the target of the proxy. This can be overridden in special cases.
20
+ def method_missing(name, *args, &block)
21
+ @target.send(name, *args, &block)
22
+ end
23
+
24
+ # If anonymous extensions are added this will take care of them.
25
+ def extends(options)
26
+ extend Module.new(&options.extension) if options.extension?
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,185 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Attributes
4
+ extend ActiveSupport::Concern
5
+ module InstanceMethods
6
+ # Get the id associated with this object. This will pull the _id value out
7
+ # of the attributes +Hash+.
8
+ def id
9
+ @attributes["_id"]
10
+ end
11
+
12
+ # Set the id of the +Document+ to a new one.
13
+ def id=(new_id)
14
+ @attributes["_id"] = new_id
15
+ end
16
+
17
+ alias :_id :id
18
+ alias :_id= :id=
19
+
20
+ # Used for allowing accessor methods for dynamic attributes.
21
+ def method_missing(name, *args)
22
+ attr = name.to_s
23
+ return super unless @attributes.has_key?(attr.reader)
24
+ if attr.writer?
25
+ # "args.size > 1" allows to simulate 1.8 behavior of "*args"
26
+ @attributes[attr.reader] = (args.size > 1) ? args : args.first
27
+ else
28
+ @attributes[attr.reader]
29
+ end
30
+ end
31
+
32
+ # Process the provided attributes casting them to their proper values if a
33
+ # field exists for them on the +Document+. This will be limited to only the
34
+ # attributes provided in the suppied +Hash+ so that no extra nil values get
35
+ # put into the document's attributes.
36
+ def process(attrs = nil)
37
+ (attrs || {}).each_pair do |key, value|
38
+ if set_allowed?(key)
39
+ @attributes[key.to_s] = value
40
+ elsif write_allowed?(key)
41
+ send("#{key}=", value)
42
+ end
43
+ end
44
+ setup_modifications
45
+ end
46
+
47
+ # Read a value from the +Document+ attributes. If the value does not exist
48
+ # it will return nil.
49
+ #
50
+ # Options:
51
+ #
52
+ # name: The name of the attribute to get.
53
+ #
54
+ # Example:
55
+ #
56
+ # <tt>person.read_attribute(:title)</tt>
57
+ def read_attribute(name)
58
+ access = name.to_s
59
+ fields[access].get(@attributes[access])
60
+ end
61
+
62
+ # Remove a value from the +Document+ attributes. If the value does not exist
63
+ # it will fail gracefully.
64
+ #
65
+ # Options:
66
+ #
67
+ # name: The name of the attribute to remove.
68
+ #
69
+ # Example:
70
+ #
71
+ # <tt>person.remove_attribute(:title)</tt>
72
+ def remove_attribute(name)
73
+ @attributes.delete(name.to_s)
74
+ end
75
+
76
+ # Returns the object type. This corresponds to the name of the class that
77
+ # this +Document+ is, which is used in determining the class to
78
+ # instantiate in various cases.
79
+ def _type
80
+ @attributes["_type"]
81
+ end
82
+
83
+ # Set the type of the +Document+. This should be the name of the class.
84
+ def _type=(new_type)
85
+ @attributes["_type"] = new_type
86
+ end
87
+
88
+ # Write a single attribute to the +Document+ attribute +Hash+. This will
89
+ # also fire the before and after update callbacks, and perform any
90
+ # necessary typecasting.
91
+ #
92
+ # Options:
93
+ #
94
+ # name: The name of the attribute to update.
95
+ # value: The value to set for the attribute.
96
+ #
97
+ # Example:
98
+ #
99
+ # <tt>person.write_attribute(:title, "Mr.")</tt>
100
+ #
101
+ # This will also cause the observing +Document+ to notify it's parent if
102
+ # there is any.
103
+ def write_attribute(name, value)
104
+ access = name.to_s
105
+ modify(access, @attributes[access], fields[access].set(value))
106
+ notify unless id.blank?
107
+ end
108
+
109
+ # Writes the supplied attributes +Hash+ to the +Document+. This will only
110
+ # overwrite existing attributes if they are present in the new +Hash+, all
111
+ # others will be preserved.
112
+ #
113
+ # Options:
114
+ #
115
+ # attrs: The +Hash+ of new attributes to set on the +Document+
116
+ #
117
+ # Example:
118
+ #
119
+ # <tt>person.write_attributes(:title => "Mr.")</tt>
120
+ #
121
+ # This will also cause the observing +Document+ to notify it's parent if
122
+ # there is any.
123
+ def write_attributes(attrs = nil)
124
+ process(attrs || {})
125
+ identify if id.blank?
126
+ notify
127
+ end
128
+ alias :attributes= :write_attributes
129
+
130
+ protected
131
+ # Return true if dynamic field setting is enabled.
132
+ def set_allowed?(key)
133
+ Mongoid.allow_dynamic_fields && !respond_to?("#{key}=")
134
+ end
135
+
136
+ # Used when supplying a :reject_if block as an option to
137
+ # accepts_nested_attributes_for
138
+ def reject(attributes, options)
139
+ rejector = options[:reject_if]
140
+ if rejector
141
+ attributes.delete_if do |key, value|
142
+ rejector.call(value)
143
+ end
144
+ end
145
+ end
146
+
147
+ # Return true if writing to the given field is allowed
148
+ def write_allowed?(key)
149
+ return true unless fields[key.to_s]
150
+ fields[key.to_s].accessible?
151
+ end
152
+ end
153
+
154
+ module ClassMethods
155
+ # Defines attribute setters for the associations specified by the names.
156
+ # This will work for a has one or has many association.
157
+ #
158
+ # Example:
159
+ #
160
+ # class Person
161
+ # include Mongoid::Document
162
+ # embeds_one :name
163
+ # embeds_many :addresses
164
+ #
165
+ # accepts_nested_attributes_for :name, :addresses
166
+ # end
167
+ def accepts_nested_attributes_for(*args)
168
+ associations = args.flatten
169
+ options = associations.last.is_a?(Hash) ? associations.pop : {}
170
+ associations.each do |name|
171
+ define_method("#{name}_attributes=") do |attrs|
172
+ reject(attrs, options)
173
+ association = send(name)
174
+ if association
175
+ observe(association, true)
176
+ association.nested_build(attrs)
177
+ else
178
+ send("build_#{name}", attrs)
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Callbacks
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ extend ActiveModel::Callbacks
7
+
8
+ # Define all the callbacks that are accepted by the document.
9
+ define_model_callbacks \
10
+ :create,
11
+ :destroy,
12
+ :save,
13
+ :update,
14
+ :validate,
15
+ :terminator => false
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,119 @@
1
+ # encoding: utf-8
2
+ require "mongoid/collections/operations"
3
+ require "mongoid/collections/cyclic_iterator"
4
+ require "mongoid/collections/mimic"
5
+ require "mongoid/collections/master"
6
+ require "mongoid/collections/slaves"
7
+
8
+ module Mongoid #:nodoc
9
+ class Collection
10
+ include Collections::Mimic
11
+ attr_reader :counter, :name
12
+
13
+ # All write operations should delegate to the master connection. These
14
+ # operations mimic the methods on a Mongo:Collection.
15
+ #
16
+ # Example:
17
+ #
18
+ # <tt>collection.save({ :name => "Al" })</tt>
19
+ proxy(:master, Collections::Operations::PROXIED)
20
+
21
+ # Determines where to send the next read query. If the slaves are not
22
+ # defined then send to master. If the read counter is under the configured
23
+ # maximum then return the master. In any other case return the slaves.
24
+ #
25
+ # Example:
26
+ #
27
+ # <tt>collection.directed</tt>
28
+ #
29
+ # Return:
30
+ #
31
+ # Either a +Master+ or +Slaves+ collection.
32
+ def directed(options = {})
33
+ options.delete(:cache)
34
+ enslave = options.delete(:enslave) || @klass.enslaved?
35
+ enslave ? master_or_slaves : master
36
+ end
37
+
38
+ # Find documents from the database given a selector and options.
39
+ #
40
+ # Options:
41
+ #
42
+ # selector: A +Hash+ selector that is the query.
43
+ # options: The options to pass to the db.
44
+ #
45
+ # Example:
46
+ #
47
+ # <tt>collection.find({ :test => "value" })</tt>
48
+ def find(selector = {}, options = {})
49
+ cursor = Mongoid::Cursor.new(@klass, self, directed(options).find(selector, options))
50
+ if block_given?
51
+ yield cursor; cursor.close
52
+ else
53
+ cursor
54
+ end
55
+ end
56
+
57
+ # Find the first document from the database given a selector and options.
58
+ #
59
+ # Options:
60
+ #
61
+ # selector: A +Hash+ selector that is the query.
62
+ # options: The options to pass to the db.
63
+ #
64
+ # Example:
65
+ #
66
+ # <tt>collection.find_one({ :test => "value" })</tt>
67
+ def find_one(selector = {}, options = {})
68
+ directed(options).find_one(selector, options)
69
+ end
70
+
71
+ # Initialize a new Mongoid::Collection, setting up the master, slave, and
72
+ # name attributes. Masters will be used for writes, slaves for reads.
73
+ #
74
+ # Example:
75
+ #
76
+ # <tt>Mongoid::Collection.new(masters, slaves, "test")</tt>
77
+ def initialize(klass, name)
78
+ @klass, @name = klass, name
79
+ end
80
+
81
+ # Perform a map/reduce on the documents.
82
+ #
83
+ # Options:
84
+ #
85
+ # map: The map javascript funcdtion.
86
+ # reduce: The reduce javascript function.
87
+ def map_reduce(map, reduce, options = {})
88
+ directed(options).map_reduce(map, reduce, options)
89
+ end
90
+
91
+ alias :mapreduce :map_reduce
92
+
93
+ # Return the object responsible for writes to the database. This will
94
+ # always return a collection associated with the Master DB.
95
+ #
96
+ # Example:
97
+ #
98
+ # <tt>collection.writer</tt>
99
+ def master
100
+ @master ||= Collections::Master.new(Mongoid.master, @name)
101
+ end
102
+
103
+ # Return the object responsible for reading documents from the database.
104
+ # This is usually the slave databases, but in their absence the master will
105
+ # handle the task.
106
+ #
107
+ # Example:
108
+ #
109
+ # <tt>collection.reader</tt>
110
+ def slaves
111
+ @slaves ||= Collections::Slaves.new(Mongoid.slaves, @name)
112
+ end
113
+
114
+ protected
115
+ def master_or_slaves
116
+ slaves.empty? ? master : slaves
117
+ end
118
+ end
119
+ end