chhean-mongoid 2.0.1.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 (117) hide show
  1. data/MIT_LICENSE +20 -0
  2. data/README.rdoc +49 -0
  3. data/lib/mongoid.rb +139 -0
  4. data/lib/mongoid/associations.rb +327 -0
  5. data/lib/mongoid/associations/embedded_in.rb +72 -0
  6. data/lib/mongoid/associations/embeds_many.rb +262 -0
  7. data/lib/mongoid/associations/embeds_one.rb +95 -0
  8. data/lib/mongoid/associations/foreign_key.rb +35 -0
  9. data/lib/mongoid/associations/meta_data.rb +29 -0
  10. data/lib/mongoid/associations/options.rb +73 -0
  11. data/lib/mongoid/associations/proxy.rb +33 -0
  12. data/lib/mongoid/associations/referenced_in.rb +71 -0
  13. data/lib/mongoid/associations/references_many.rb +243 -0
  14. data/lib/mongoid/associations/references_many_as_array.rb +78 -0
  15. data/lib/mongoid/associations/references_one.rb +116 -0
  16. data/lib/mongoid/attributes.rb +226 -0
  17. data/lib/mongoid/callbacks.rb +17 -0
  18. data/lib/mongoid/collection.rb +120 -0
  19. data/lib/mongoid/collections.rb +41 -0
  20. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  21. data/lib/mongoid/collections/master.rb +29 -0
  22. data/lib/mongoid/collections/operations.rb +41 -0
  23. data/lib/mongoid/collections/slaves.rb +45 -0
  24. data/lib/mongoid/components.rb +32 -0
  25. data/lib/mongoid/config.rb +237 -0
  26. data/lib/mongoid/contexts.rb +24 -0
  27. data/lib/mongoid/contexts/enumerable.rb +151 -0
  28. data/lib/mongoid/contexts/ids.rb +25 -0
  29. data/lib/mongoid/contexts/mongo.rb +285 -0
  30. data/lib/mongoid/contexts/paging.rb +50 -0
  31. data/lib/mongoid/criteria.rb +230 -0
  32. data/lib/mongoid/criterion/complex.rb +21 -0
  33. data/lib/mongoid/criterion/exclusion.rb +65 -0
  34. data/lib/mongoid/criterion/inclusion.rb +110 -0
  35. data/lib/mongoid/criterion/optional.rb +136 -0
  36. data/lib/mongoid/cursor.rb +82 -0
  37. data/lib/mongoid/deprecation.rb +22 -0
  38. data/lib/mongoid/dirty.rb +254 -0
  39. data/lib/mongoid/document.rb +264 -0
  40. data/lib/mongoid/errors.rb +124 -0
  41. data/lib/mongoid/extensions.rb +106 -0
  42. data/lib/mongoid/extensions/array/accessors.rb +17 -0
  43. data/lib/mongoid/extensions/array/aliasing.rb +4 -0
  44. data/lib/mongoid/extensions/array/assimilation.rb +26 -0
  45. data/lib/mongoid/extensions/array/conversions.rb +27 -0
  46. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  47. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  48. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  49. data/lib/mongoid/extensions/boolean/conversions.rb +22 -0
  50. data/lib/mongoid/extensions/date/conversions.rb +24 -0
  51. data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
  52. data/lib/mongoid/extensions/float/conversions.rb +20 -0
  53. data/lib/mongoid/extensions/hash/accessors.rb +38 -0
  54. data/lib/mongoid/extensions/hash/assimilation.rb +39 -0
  55. data/lib/mongoid/extensions/hash/conversions.rb +45 -0
  56. data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
  57. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  58. data/lib/mongoid/extensions/integer/conversions.rb +20 -0
  59. data/lib/mongoid/extensions/nil/assimilation.rb +17 -0
  60. data/lib/mongoid/extensions/object/conversions.rb +27 -0
  61. data/lib/mongoid/extensions/objectid/conversions.rb +15 -0
  62. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  63. data/lib/mongoid/extensions/set/conversions.rb +20 -0
  64. data/lib/mongoid/extensions/string/conversions.rb +15 -0
  65. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  66. data/lib/mongoid/extensions/symbol/inflections.rb +39 -0
  67. data/lib/mongoid/extensions/time_conversions.rb +35 -0
  68. data/lib/mongoid/extras.rb +61 -0
  69. data/lib/mongoid/factory.rb +20 -0
  70. data/lib/mongoid/field.rb +80 -0
  71. data/lib/mongoid/fields.rb +61 -0
  72. data/lib/mongoid/finders.rb +144 -0
  73. data/lib/mongoid/identity.rb +39 -0
  74. data/lib/mongoid/indexes.rb +27 -0
  75. data/lib/mongoid/javascript.rb +21 -0
  76. data/lib/mongoid/javascript/functions.yml +37 -0
  77. data/lib/mongoid/matchers.rb +35 -0
  78. data/lib/mongoid/matchers/all.rb +11 -0
  79. data/lib/mongoid/matchers/default.rb +26 -0
  80. data/lib/mongoid/matchers/exists.rb +13 -0
  81. data/lib/mongoid/matchers/gt.rb +11 -0
  82. data/lib/mongoid/matchers/gte.rb +11 -0
  83. data/lib/mongoid/matchers/in.rb +11 -0
  84. data/lib/mongoid/matchers/lt.rb +11 -0
  85. data/lib/mongoid/matchers/lte.rb +11 -0
  86. data/lib/mongoid/matchers/ne.rb +11 -0
  87. data/lib/mongoid/matchers/nin.rb +11 -0
  88. data/lib/mongoid/matchers/size.rb +11 -0
  89. data/lib/mongoid/memoization.rb +33 -0
  90. data/lib/mongoid/named_scope.rb +37 -0
  91. data/lib/mongoid/observable.rb +30 -0
  92. data/lib/mongoid/paths.rb +62 -0
  93. data/lib/mongoid/persistence.rb +218 -0
  94. data/lib/mongoid/persistence/command.rb +39 -0
  95. data/lib/mongoid/persistence/insert.rb +47 -0
  96. data/lib/mongoid/persistence/insert_embedded.rb +38 -0
  97. data/lib/mongoid/persistence/remove.rb +39 -0
  98. data/lib/mongoid/persistence/remove_all.rb +37 -0
  99. data/lib/mongoid/persistence/remove_embedded.rb +50 -0
  100. data/lib/mongoid/persistence/update.rb +63 -0
  101. data/lib/mongoid/railtie.rb +54 -0
  102. data/lib/mongoid/railties/database.rake +37 -0
  103. data/lib/mongoid/scope.rb +75 -0
  104. data/lib/mongoid/state.rb +32 -0
  105. data/lib/mongoid/timestamps.rb +27 -0
  106. data/lib/mongoid/validations.rb +51 -0
  107. data/lib/mongoid/validations/associated.rb +32 -0
  108. data/lib/mongoid/validations/locale/en.yml +4 -0
  109. data/lib/mongoid/validations/uniqueness.rb +50 -0
  110. data/lib/mongoid/version.rb +4 -0
  111. data/lib/mongoid/versioning.rb +27 -0
  112. data/lib/rails/generators/mongoid/config/config_generator.rb +41 -0
  113. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +24 -0
  114. data/lib/rails/generators/mongoid/model/model_generator.rb +24 -0
  115. data/lib/rails/generators/mongoid/model/templates/model.rb +15 -0
  116. data/lib/rails/generators/mongoid_generator.rb +61 -0
  117. metadata +284 -0
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ # Represents an relational one-to-many association with an object in a
5
+ # separate collection or database, stored as an array of ids on the parent
6
+ # document.
7
+ class ReferencesManyAsArray < ReferencesMany
8
+
9
+ # Append a document to this association. This will also set the appended
10
+ # document's id on the inverse association as well.
11
+ #
12
+ # Example:
13
+ #
14
+ # <tt>person.preferences << Preference.new(:name => "VGA")</tt>
15
+ def <<(*objects)
16
+ @target = @target.entries
17
+ objects.flatten.each do |object|
18
+ # First set the documents id on the parent array of ids.
19
+ @parent.send(@foreign_key) << object.id
20
+ # Then we need to set the parent's id on the documents array of ids
21
+ # to get the inverse side of the association as well. Note, need a
22
+ # clean way to handle this with new documents - we want to set the
23
+ # actual objects as well, but dont want to get in an infinite loop
24
+ # while doing so.
25
+ object.send(reverse_key(object)) << @parent.id
26
+ @target << object
27
+ end
28
+ end
29
+
30
+ alias :concat :<<
31
+ alias :push :<<
32
+
33
+ # Builds a new Document and adds it to the association collection. The
34
+ # document created will be of the same class as the others in the
35
+ # association, and the attributes will be passed into the constructor.
36
+ #
37
+ # Returns the newly created object.
38
+ def build(attributes = nil)
39
+ load_target
40
+ document = @klass.instantiate(attributes || {})
41
+ push(document); document
42
+ end
43
+
44
+ protected
45
+ # Find the inverse key for the supplied document.
46
+ def reverse_key(document)
47
+ document.send(@options.inverse_of).options.foreign_key
48
+ end
49
+
50
+ # The default query used for retrieving the documents from the database.
51
+ def query
52
+ @query ||= lambda { @klass.any_in(:_id => @parent.send(@foreign_key)) }
53
+ end
54
+
55
+ class << self
56
+ # Perform an update of the relationship of the parent and child. This
57
+ # will assimilate the child +Document+ into the parent's object graph.
58
+ #
59
+ # Options:
60
+ #
61
+ # related: The related object
62
+ # parent: The parent +Document+ to update.
63
+ # options: The association +Options+
64
+ #
65
+ # Example:
66
+ #
67
+ # <tt>RelatesToManyAsArray.update(preferences, person, options)</tt>
68
+ def update(target, document, options)
69
+ target.each do |child|
70
+ name = child.associations[options.inverse_of.to_s].options.name
71
+ child.send(name) << document
72
+ end
73
+ instantiate(document, options, target)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,116 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ # Represents an relational one-to-one association with an object in a
5
+ # separate collection or database.
6
+ class ReferencesOne < Proxy
7
+
8
+ delegate :nil?, :to => :target
9
+
10
+ # Builds a new Document and sets it as the association.
11
+ #
12
+ # Returns the newly created object.
13
+ def build(attributes = {})
14
+ @target = @klass.instantiate(attributes)
15
+
16
+ if @foreign_type
17
+ inverse = @target.associations.values.detect do |metadata|
18
+ metadata.options.name == @options.as.to_s
19
+ end
20
+ else
21
+ inverse = @target.associations.values.detect do |metadata|
22
+ metadata.options.klass == @parent.class
23
+ end
24
+ end
25
+ name = inverse.name
26
+ @target.send("#{name}=", @parent)
27
+ @target
28
+ end
29
+
30
+ # Builds a new Document and sets it as the association, then saves the
31
+ # newly created document.
32
+ #
33
+ # Returns the newly created object.
34
+ def create(attributes)
35
+ build(attributes).tap(&:save)
36
+ end
37
+
38
+ # Initializing a related association only requires looking up the objects
39
+ # by their ids.
40
+ #
41
+ # Options:
42
+ #
43
+ # document: The +Document+ that contains the relationship.
44
+ # options: The association +Options+.
45
+ def initialize(document, options, target = nil)
46
+ @parent, @klass, @options = document, options.klass, options
47
+
48
+ @foreign_key = options.foreign_key
49
+
50
+ pre_conditions = { @foreign_key => @parent.id }
51
+
52
+ if options.as
53
+ @foreign_type = options.as.to_s+"_type"
54
+
55
+ pre_conditions[@foreign_type] = document.class.to_s
56
+ end
57
+
58
+ @target = target || @klass.first(:conditions => pre_conditions)
59
+ extends(options)
60
+ end
61
+
62
+ # Used for setting the association via a nested attributes setter on the
63
+ # parent +Document+. Called when using accepts_nested_attributes_for.
64
+ #
65
+ # Options:
66
+ #
67
+ # attributes: The attributes for the new association
68
+ #
69
+ # Returns:
70
+ #
71
+ # A new target document.
72
+ def nested_build(attributes, options = nil)
73
+ build(attributes) unless @target.blank? && options[:update_only]
74
+ end
75
+
76
+ class << self
77
+ # Preferred method for creating the new +RelatesToMany+ association.
78
+ #
79
+ # Options:
80
+ #
81
+ # document: The +Document+ that contains the relationship.
82
+ # options: The association +Options+.
83
+ def instantiate(document, options, target = nil)
84
+ new(document, options, target)
85
+ end
86
+
87
+ # Returns the macro used to create the association.
88
+ def macro
89
+ :references_one
90
+ end
91
+
92
+ # Perform an update of the relationship of the parent and child. This
93
+ # will assimilate the child +Document+ into the parent's object graph.
94
+ #
95
+ # Options:
96
+ #
97
+ # related: The related object to update.
98
+ # document: The parent +Document+.
99
+ # options: The association +Options+
100
+ #
101
+ # Example:
102
+ #
103
+ # <tt>HasOneToRelated.update(game, person, options)</tt>
104
+ def update(target, document, options)
105
+ if target
106
+ name = document.class.to_s.underscore
107
+ target.send("#{name}=", document)
108
+ return instantiate(document, options, target)
109
+ end
110
+ target
111
+ end
112
+ end
113
+
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,226 @@
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
+ write_attribute(attr.reader, (args.size > 1) ? args : args.first)
27
+ else
28
+ read_attribute(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
+ write_attribute(key, 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
+ value = @attributes[access]
60
+ typed_value = fields.has_key?(access) ? fields[access].get(value) : value
61
+ accessed(access, typed_value)
62
+ end
63
+
64
+ # Remove a value from the +Document+ attributes. If the value does not exist
65
+ # it will fail gracefully.
66
+ #
67
+ # Options:
68
+ #
69
+ # name: The name of the attribute to remove.
70
+ #
71
+ # Example:
72
+ #
73
+ # <tt>person.remove_attribute(:title)</tt>
74
+ def remove_attribute(name)
75
+ access = name.to_s
76
+ modify(access, @attributes.delete(name.to_s), nil)
77
+ end
78
+
79
+ # Returns the object type. This corresponds to the name of the class that
80
+ # this +Document+ is, which is used in determining the class to
81
+ # instantiate in various cases.
82
+ def _type
83
+ @attributes["_type"]
84
+ end
85
+
86
+ # Set the type of the +Document+. This should be the name of the class.
87
+ def _type=(new_type)
88
+ @attributes["_type"] = new_type
89
+ end
90
+
91
+ # Write a single attribute to the +Document+ attribute +Hash+. This will
92
+ # also fire the before and after update callbacks, and perform any
93
+ # necessary typecasting.
94
+ #
95
+ # Options:
96
+ #
97
+ # name: The name of the attribute to update.
98
+ # value: The value to set for the attribute.
99
+ #
100
+ # Example:
101
+ #
102
+ # <tt>person.write_attribute(:title, "Mr.")</tt>
103
+ #
104
+ # This will also cause the observing +Document+ to notify it's parent if
105
+ # there is any.
106
+ def write_attribute(name, value)
107
+ access = name.to_s
108
+ typed_value = fields.has_key?(access) ? fields[access].set(value) : value
109
+ modify(access, @attributes[access], typed_value)
110
+ notify if !id.blank? && new_record?
111
+ end
112
+
113
+ # Writes the supplied attributes +Hash+ to the +Document+. This will only
114
+ # overwrite existing attributes if they are present in the new +Hash+, all
115
+ # others will be preserved.
116
+ #
117
+ # Options:
118
+ #
119
+ # attrs: The +Hash+ of new attributes to set on the +Document+
120
+ #
121
+ # Example:
122
+ #
123
+ # <tt>person.write_attributes(:title => "Mr.")</tt>
124
+ #
125
+ # This will also cause the observing +Document+ to notify it's parent if
126
+ # there is any.
127
+ def write_attributes(attrs = nil)
128
+ process(attrs || {})
129
+ identified = !id.blank?
130
+ if new_record? && !identified
131
+ identify; notify
132
+ end
133
+ end
134
+ alias :attributes= :write_attributes
135
+
136
+ protected
137
+ # apply default values to attributes - calling procs as required
138
+ def default_attributes
139
+ default_values = defaults
140
+ default_values.each_pair do |key, val|
141
+ default_values[key] = val.call if val.respond_to?(:call)
142
+ end
143
+ default_values || {}
144
+ end
145
+
146
+ # Return true if dynamic field setting is enabled.
147
+ def set_allowed?(key)
148
+ Mongoid.allow_dynamic_fields && !respond_to?("#{key}=") && is_accesible?(key)
149
+ end
150
+
151
+ # Returns true if accessible
152
+ def is_accesible?(key)
153
+ return true unless self.class.attr_accessible_list.any? || self.class.attr_protected_list.any?
154
+
155
+ return false if self.class.attr_protected_list.include?(key.to_sym)
156
+
157
+ if self.class.attr_accessible_list.any?
158
+ return self.class.attr_accessible_list.include?(key.to_sym)
159
+ else
160
+ return true
161
+ end
162
+ end
163
+
164
+ # Used when supplying a :reject_if block as an option to
165
+ # accepts_nested_attributes_for
166
+ def reject(attributes, options)
167
+ rejector = options[:reject_if]
168
+ if rejector
169
+ attributes.delete_if do |key, value|
170
+ rejector.call(value)
171
+ end
172
+ end
173
+ end
174
+
175
+ # Used when supplying a :limit as an option to accepts_nested_attributes_for
176
+ def limit(attributes, name, options)
177
+ raise Mongoid::Errors::TooManyNestedAttributeRecords.new(name, options[:limit]) if options[:limit] && attributes.size > options[:limit]
178
+ end
179
+
180
+ # Return true if writing to the given field is allowed
181
+ def write_allowed?(key)
182
+ name = key.to_s
183
+ existing = fields[name]
184
+ return is_accesible?(key) unless existing
185
+
186
+ if existing.set_accessible?
187
+ existing.accessible?
188
+ else
189
+ is_accesible?(key)
190
+ end
191
+ end
192
+ end
193
+
194
+ module ClassMethods
195
+ # Defines attribute setters for the associations specified by the names.
196
+ # This will work for a has one or has many association.
197
+ #
198
+ # Example:
199
+ #
200
+ # class Person
201
+ # include Mongoid::Document
202
+ # embeds_one :name
203
+ # embeds_many :addresses
204
+ #
205
+ # accepts_nested_attributes_for :name, :addresses
206
+ # end
207
+ def accepts_nested_attributes_for(*args)
208
+ associations = args.flatten
209
+ options = associations.last.is_a?(Hash) ? associations.pop : {}
210
+ associations.each do |name|
211
+ define_method("#{name}_attributes=") do |attrs|
212
+ reject(attrs, options)
213
+ limit(attrs, name, options)
214
+ association = send(name)
215
+ if association
216
+ # observe(association, true)
217
+ association.nested_build(attrs, options)
218
+ else
219
+ send("build_#{name}", attrs, options)
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,17 @@
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
+ end
16
+ end
17
+ end
@@ -0,0 +1,120 @@
1
+ # encoding: utf-8
2
+ require "mongoid/collections/operations"
3
+ require "mongoid/collections/cyclic_iterator"
4
+ require "mongoid/collections/master"
5
+ require "mongoid/collections/slaves"
6
+
7
+ module Mongoid #:nodoc
8
+ # The Mongoid wrapper to the Mongo Ruby driver's collection object.
9
+ class Collection
10
+ attr_reader :counter, :name
11
+
12
+ # All write operations should delegate to the master connection. These
13
+ # operations mimic the methods on a Mongo:Collection.
14
+ #
15
+ # Example:
16
+ #
17
+ # <tt>collection.save({ :name => "Al" })</tt>
18
+ Collections::Operations::PROXIED.each do |name|
19
+ define_method(name) { |*args| master.send(name, *args) }
20
+ end
21
+
22
+ # Determines where to send the next read query. If the slaves are not
23
+ # defined then send to master. If the read counter is under the configured
24
+ # maximum then return the master. In any other case return the slaves.
25
+ #
26
+ # Example:
27
+ #
28
+ # <tt>collection.directed</tt>
29
+ #
30
+ # Return:
31
+ #
32
+ # Either a +Master+ or +Slaves+ collection.
33
+ def directed(options = {})
34
+ options.delete(:cache)
35
+ enslave = options.delete(:enslave) || @klass.enslaved?
36
+ enslave ? master_or_slaves : master
37
+ end
38
+
39
+ # Find documents from the database given a selector and options.
40
+ #
41
+ # Options:
42
+ #
43
+ # selector: A +Hash+ selector that is the query.
44
+ # options: The options to pass to the db.
45
+ #
46
+ # Example:
47
+ #
48
+ # <tt>collection.find({ :test => "value" })</tt>
49
+ def find(selector = {}, options = {})
50
+ cursor = Mongoid::Cursor.new(@klass, self, directed(options).find(selector, options))
51
+ if block_given?
52
+ yield cursor; cursor.close
53
+ else
54
+ cursor
55
+ end
56
+ end
57
+
58
+ # Find the first document from the database given a selector and options.
59
+ #
60
+ # Options:
61
+ #
62
+ # selector: A +Hash+ selector that is the query.
63
+ # options: The options to pass to the db.
64
+ #
65
+ # Example:
66
+ #
67
+ # <tt>collection.find_one({ :test => "value" })</tt>
68
+ def find_one(selector = {}, options = {})
69
+ directed(options).find_one(selector, options)
70
+ end
71
+
72
+ # Initialize a new Mongoid::Collection, setting up the master, slave, and
73
+ # name attributes. Masters will be used for writes, slaves for reads.
74
+ #
75
+ # Example:
76
+ #
77
+ # <tt>Mongoid::Collection.new(masters, slaves, "test")</tt>
78
+ def initialize(klass, name)
79
+ @klass, @name = klass, name
80
+ end
81
+
82
+ # Perform a map/reduce on the documents.
83
+ #
84
+ # Options:
85
+ #
86
+ # map: The map javascript funcdtion.
87
+ # reduce: The reduce javascript function.
88
+ def map_reduce(map, reduce, options = {})
89
+ directed(options).map_reduce(map, reduce, options)
90
+ end
91
+
92
+ alias :mapreduce :map_reduce
93
+
94
+ # Return the object responsible for writes to the database. This will
95
+ # always return a collection associated with the Master DB.
96
+ #
97
+ # Example:
98
+ #
99
+ # <tt>collection.writer</tt>
100
+ def master
101
+ @master ||= Collections::Master.new(Mongoid.master, @name)
102
+ end
103
+
104
+ # Return the object responsible for reading documents from the database.
105
+ # This is usually the slave databases, but in their absence the master will
106
+ # handle the task.
107
+ #
108
+ # Example:
109
+ #
110
+ # <tt>collection.reader</tt>
111
+ def slaves
112
+ @slaves ||= Collections::Slaves.new(Mongoid.slaves, @name)
113
+ end
114
+
115
+ protected
116
+ def master_or_slaves
117
+ slaves.empty? ? master : slaves
118
+ end
119
+ end
120
+ end