stonegao-mongoid 2.0.0.rc.6

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 (199) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +50 -0
  3. data/Rakefile +51 -0
  4. data/lib/config/locales/bg.yml +44 -0
  5. data/lib/config/locales/de.yml +44 -0
  6. data/lib/config/locales/en.yml +45 -0
  7. data/lib/config/locales/es.yml +44 -0
  8. data/lib/config/locales/fr.yml +45 -0
  9. data/lib/config/locales/hu.yml +47 -0
  10. data/lib/config/locales/it.yml +42 -0
  11. data/lib/config/locales/kr.yml +68 -0
  12. data/lib/config/locales/nl.yml +42 -0
  13. data/lib/config/locales/pl.yml +42 -0
  14. data/lib/config/locales/pt-br.yml +43 -0
  15. data/lib/config/locales/pt.yml +43 -0
  16. data/lib/config/locales/ro.yml +49 -0
  17. data/lib/config/locales/sv.yml +43 -0
  18. data/lib/config/locales/zh-CN.yml +34 -0
  19. data/lib/mongoid/atomicity.rb +111 -0
  20. data/lib/mongoid/attributes.rb +251 -0
  21. data/lib/mongoid/callbacks.rb +13 -0
  22. data/lib/mongoid/collection.rb +137 -0
  23. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  24. data/lib/mongoid/collections/master.rb +29 -0
  25. data/lib/mongoid/collections/operations.rb +42 -0
  26. data/lib/mongoid/collections/slaves.rb +45 -0
  27. data/lib/mongoid/collections.rb +70 -0
  28. data/lib/mongoid/components.rb +45 -0
  29. data/lib/mongoid/config/database.rb +167 -0
  30. data/lib/mongoid/config/replset_database.rb +48 -0
  31. data/lib/mongoid/config.rb +343 -0
  32. data/lib/mongoid/contexts/enumerable/sort.rb +43 -0
  33. data/lib/mongoid/contexts/enumerable.rb +226 -0
  34. data/lib/mongoid/contexts/ids.rb +25 -0
  35. data/lib/mongoid/contexts/mongo.rb +345 -0
  36. data/lib/mongoid/contexts/paging.rb +50 -0
  37. data/lib/mongoid/contexts.rb +21 -0
  38. data/lib/mongoid/copyable.rb +44 -0
  39. data/lib/mongoid/criteria.rb +325 -0
  40. data/lib/mongoid/criterion/complex.rb +34 -0
  41. data/lib/mongoid/criterion/creational.rb +34 -0
  42. data/lib/mongoid/criterion/exclusion.rb +67 -0
  43. data/lib/mongoid/criterion/inclusion.rb +134 -0
  44. data/lib/mongoid/criterion/inspection.rb +20 -0
  45. data/lib/mongoid/criterion/optional.rb +213 -0
  46. data/lib/mongoid/criterion/selector.rb +74 -0
  47. data/lib/mongoid/cursor.rb +81 -0
  48. data/lib/mongoid/default_scope.rb +28 -0
  49. data/lib/mongoid/dirty.rb +251 -0
  50. data/lib/mongoid/document.rb +256 -0
  51. data/lib/mongoid/errors/document_not_found.rb +29 -0
  52. data/lib/mongoid/errors/invalid_collection.rb +19 -0
  53. data/lib/mongoid/errors/invalid_database.rb +20 -0
  54. data/lib/mongoid/errors/invalid_field.rb +19 -0
  55. data/lib/mongoid/errors/invalid_options.rb +16 -0
  56. data/lib/mongoid/errors/invalid_type.rb +26 -0
  57. data/lib/mongoid/errors/mongoid_error.rb +27 -0
  58. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +21 -0
  59. data/lib/mongoid/errors/unsaved_document.rb +23 -0
  60. data/lib/mongoid/errors/unsupported_version.rb +21 -0
  61. data/lib/mongoid/errors/validations.rb +24 -0
  62. data/lib/mongoid/errors.rb +12 -0
  63. data/lib/mongoid/extensions/array/conversions.rb +23 -0
  64. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  65. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  66. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  67. data/lib/mongoid/extensions/boolean/conversions.rb +27 -0
  68. data/lib/mongoid/extensions/date/conversions.rb +25 -0
  69. data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
  70. data/lib/mongoid/extensions/false_class/equality.rb +13 -0
  71. data/lib/mongoid/extensions/float/conversions.rb +20 -0
  72. data/lib/mongoid/extensions/hash/conversions.rb +19 -0
  73. data/lib/mongoid/extensions/hash/criteria_helpers.rb +22 -0
  74. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  75. data/lib/mongoid/extensions/integer/conversions.rb +20 -0
  76. data/lib/mongoid/extensions/nil/collectionization.rb +12 -0
  77. data/lib/mongoid/extensions/object/conversions.rb +25 -0
  78. data/lib/mongoid/extensions/object/reflections.rb +17 -0
  79. data/lib/mongoid/extensions/object/yoda.rb +27 -0
  80. data/lib/mongoid/extensions/object_id/conversions.rb +57 -0
  81. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  82. data/lib/mongoid/extensions/set/conversions.rb +20 -0
  83. data/lib/mongoid/extensions/string/conversions.rb +34 -0
  84. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  85. data/lib/mongoid/extensions/symbol/conversions.rb +21 -0
  86. data/lib/mongoid/extensions/symbol/inflections.rb +40 -0
  87. data/lib/mongoid/extensions/time_conversions.rb +38 -0
  88. data/lib/mongoid/extensions/true_class/equality.rb +13 -0
  89. data/lib/mongoid/extensions.rb +116 -0
  90. data/lib/mongoid/extras.rb +61 -0
  91. data/lib/mongoid/factory.rb +20 -0
  92. data/lib/mongoid/field.rb +95 -0
  93. data/lib/mongoid/fields.rb +138 -0
  94. data/lib/mongoid/finders.rb +173 -0
  95. data/lib/mongoid/hierarchy.rb +85 -0
  96. data/lib/mongoid/identity.rb +89 -0
  97. data/lib/mongoid/indexes.rb +38 -0
  98. data/lib/mongoid/inspection.rb +58 -0
  99. data/lib/mongoid/javascript/functions.yml +37 -0
  100. data/lib/mongoid/javascript.rb +21 -0
  101. data/lib/mongoid/json.rb +16 -0
  102. data/lib/mongoid/keys.rb +77 -0
  103. data/lib/mongoid/logger.rb +18 -0
  104. data/lib/mongoid/matchers/all.rb +11 -0
  105. data/lib/mongoid/matchers/default.rb +27 -0
  106. data/lib/mongoid/matchers/exists.rb +13 -0
  107. data/lib/mongoid/matchers/gt.rb +11 -0
  108. data/lib/mongoid/matchers/gte.rb +11 -0
  109. data/lib/mongoid/matchers/in.rb +11 -0
  110. data/lib/mongoid/matchers/lt.rb +11 -0
  111. data/lib/mongoid/matchers/lte.rb +11 -0
  112. data/lib/mongoid/matchers/ne.rb +11 -0
  113. data/lib/mongoid/matchers/nin.rb +11 -0
  114. data/lib/mongoid/matchers/size.rb +11 -0
  115. data/lib/mongoid/matchers.rb +55 -0
  116. data/lib/mongoid/modifiers/command.rb +18 -0
  117. data/lib/mongoid/modifiers/inc.rb +24 -0
  118. data/lib/mongoid/modifiers.rb +24 -0
  119. data/lib/mongoid/multi_database.rb +11 -0
  120. data/lib/mongoid/multi_parameter_attributes.rb +80 -0
  121. data/lib/mongoid/named_scope.rb +36 -0
  122. data/lib/mongoid/nested_attributes.rb +43 -0
  123. data/lib/mongoid/paranoia.rb +103 -0
  124. data/lib/mongoid/paths.rb +61 -0
  125. data/lib/mongoid/persistence/command.rb +59 -0
  126. data/lib/mongoid/persistence/insert.rb +53 -0
  127. data/lib/mongoid/persistence/insert_embedded.rb +42 -0
  128. data/lib/mongoid/persistence/remove.rb +44 -0
  129. data/lib/mongoid/persistence/remove_all.rb +40 -0
  130. data/lib/mongoid/persistence/remove_embedded.rb +48 -0
  131. data/lib/mongoid/persistence/update.rb +76 -0
  132. data/lib/mongoid/persistence.rb +237 -0
  133. data/lib/mongoid/railtie.rb +129 -0
  134. data/lib/mongoid/railties/database.rake +171 -0
  135. data/lib/mongoid/railties/document.rb +12 -0
  136. data/lib/mongoid/relations/accessors.rb +157 -0
  137. data/lib/mongoid/relations/auto_save.rb +34 -0
  138. data/lib/mongoid/relations/binding.rb +26 -0
  139. data/lib/mongoid/relations/bindings/embedded/in.rb +82 -0
  140. data/lib/mongoid/relations/bindings/embedded/many.rb +98 -0
  141. data/lib/mongoid/relations/bindings/embedded/one.rb +66 -0
  142. data/lib/mongoid/relations/bindings/referenced/in.rb +74 -0
  143. data/lib/mongoid/relations/bindings/referenced/many.rb +96 -0
  144. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +99 -0
  145. data/lib/mongoid/relations/bindings/referenced/one.rb +66 -0
  146. data/lib/mongoid/relations/bindings.rb +9 -0
  147. data/lib/mongoid/relations/builder.rb +42 -0
  148. data/lib/mongoid/relations/builders/embedded/in.rb +25 -0
  149. data/lib/mongoid/relations/builders/embedded/many.rb +32 -0
  150. data/lib/mongoid/relations/builders/embedded/one.rb +26 -0
  151. data/lib/mongoid/relations/builders/nested_attributes/many.rb +116 -0
  152. data/lib/mongoid/relations/builders/nested_attributes/one.rb +135 -0
  153. data/lib/mongoid/relations/builders/referenced/in.rb +32 -0
  154. data/lib/mongoid/relations/builders/referenced/many.rb +26 -0
  155. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +29 -0
  156. data/lib/mongoid/relations/builders/referenced/one.rb +30 -0
  157. data/lib/mongoid/relations/builders.rb +79 -0
  158. data/lib/mongoid/relations/cascading/delete.rb +19 -0
  159. data/lib/mongoid/relations/cascading/destroy.rb +19 -0
  160. data/lib/mongoid/relations/cascading/nullify.rb +18 -0
  161. data/lib/mongoid/relations/cascading/strategy.rb +26 -0
  162. data/lib/mongoid/relations/cascading.rb +55 -0
  163. data/lib/mongoid/relations/constraint.rb +45 -0
  164. data/lib/mongoid/relations/cyclic.rb +97 -0
  165. data/lib/mongoid/relations/embedded/in.rb +173 -0
  166. data/lib/mongoid/relations/embedded/many.rb +483 -0
  167. data/lib/mongoid/relations/embedded/one.rb +170 -0
  168. data/lib/mongoid/relations/macros.rb +306 -0
  169. data/lib/mongoid/relations/many.rb +171 -0
  170. data/lib/mongoid/relations/metadata.rb +533 -0
  171. data/lib/mongoid/relations/nested_builder.rb +68 -0
  172. data/lib/mongoid/relations/one.rb +47 -0
  173. data/lib/mongoid/relations/polymorphic.rb +54 -0
  174. data/lib/mongoid/relations/proxy.rb +128 -0
  175. data/lib/mongoid/relations/referenced/in.rb +216 -0
  176. data/lib/mongoid/relations/referenced/many.rb +443 -0
  177. data/lib/mongoid/relations/referenced/many_to_many.rb +344 -0
  178. data/lib/mongoid/relations/referenced/one.rb +206 -0
  179. data/lib/mongoid/relations/reflections.rb +45 -0
  180. data/lib/mongoid/relations.rb +105 -0
  181. data/lib/mongoid/safe.rb +23 -0
  182. data/lib/mongoid/safety.rb +207 -0
  183. data/lib/mongoid/scope.rb +31 -0
  184. data/lib/mongoid/serialization.rb +99 -0
  185. data/lib/mongoid/state.rb +66 -0
  186. data/lib/mongoid/timestamps.rb +38 -0
  187. data/lib/mongoid/validations/associated.rb +42 -0
  188. data/lib/mongoid/validations/uniqueness.rb +85 -0
  189. data/lib/mongoid/validations.rb +117 -0
  190. data/lib/mongoid/version.rb +4 -0
  191. data/lib/mongoid/versioning.rb +51 -0
  192. data/lib/mongoid.rb +139 -0
  193. data/lib/rails/generators/mongoid/config/config_generator.rb +25 -0
  194. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +23 -0
  195. data/lib/rails/generators/mongoid/model/model_generator.rb +24 -0
  196. data/lib/rails/generators/mongoid/model/templates/model.rb +17 -0
  197. data/lib/rails/generators/mongoid_generator.rb +61 -0
  198. data/lib/rails/mongoid.rb +57 -0
  199. metadata +380 -0
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Extras #:nodoc:
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ class_attribute :cached, :enslaved
7
+ self.cached = false
8
+ self.enslaved = false
9
+ delegate :cached?, :enslaved?, :to => "self.class"
10
+ end
11
+
12
+ module ClassMethods #:nodoc
13
+ # Sets caching on for this class. This class level configuration will
14
+ # default all queries to cache the results of the first iteration over
15
+ # the cursor into an internal array. This should only be used for queries
16
+ # that return a small number of results or have small documents, as after
17
+ # the first iteration the entire results will be stored in memory.
18
+ #
19
+ # Example:
20
+ #
21
+ # class Person
22
+ # include Mongoid::Document
23
+ # cache
24
+ # end
25
+ def cache
26
+ self.cached = true
27
+ end
28
+
29
+ # Determines if the class is cached or not.
30
+ #
31
+ # Returns:
32
+ #
33
+ # True if cached, false if not.
34
+ def cached?
35
+ !!self.cached
36
+ end
37
+
38
+ # Set whether or not this documents read operations should delegate to
39
+ # the slave database by default.
40
+ #
41
+ # Example:
42
+ #
43
+ # class Person
44
+ # include Mongoid::Document
45
+ # enslave
46
+ # end
47
+ def enslave
48
+ self.enslaved = true
49
+ end
50
+
51
+ # Determines if the class is enslaved or not.
52
+ #
53
+ # Returns:
54
+ #
55
+ # True if enslaved, false if not.
56
+ def enslaved?
57
+ !!self.enslaved
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ class Factory #:nodoc:
4
+ # Builds a new +Document+ from the supplied attributes.
5
+ #
6
+ # Example:
7
+ #
8
+ # <tt>Mongoid::Factory.build(Person, {})</tt>
9
+ #
10
+ # Options:
11
+ #
12
+ # klass: The class to instantiate from if _type is not present.
13
+ # attributes: The +Document+ attributes.
14
+ def self.build(klass, attributes)
15
+ attrs = {}.merge(attributes)
16
+ type = attrs["_type"]
17
+ type ? type.constantize.instantiate(attrs) : klass.instantiate(attrs)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,95 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ class Field
4
+ attr_reader :copyable, :klass, :label, :name, :options, :type
5
+
6
+ # Get the default value for the field.
7
+ #
8
+ # Returns:
9
+ #
10
+ # The typecast default value.
11
+ def default
12
+ copy.respond_to?(:call) ? copy : set(copy)
13
+ end
14
+
15
+ # Create the new field with a name and optional additional options. Valid
16
+ # options are :default
17
+ #
18
+ # Options:
19
+ #
20
+ # name: The name of the field as a +Symbol+.
21
+ # options: A +Hash+ of options for the field.
22
+ #
23
+ # Example:
24
+ #
25
+ # <tt>Field.new(:score, :default => 0)</tt>
26
+ def initialize(name, options = {})
27
+ check_name!(name)
28
+ @type = options[:type] || Object
29
+ @name, @default = name, options[:default]
30
+ @copyable = (@default.is_a?(Array) || @default.is_a?(Hash))
31
+ @label = options[:label]
32
+ @options = options
33
+ check_default!
34
+ end
35
+
36
+ # Used for setting an object in the attributes hash.
37
+ #
38
+ # If nil is provided the default will get returned if it exists.
39
+ #
40
+ # If the field is an identity field, ie an id, it performs the necessary
41
+ # cast.
42
+ #
43
+ # Example:
44
+ #
45
+ # <tt>field.set("New Value")</tt>
46
+ #
47
+ # Returns:
48
+ #
49
+ # The new value.
50
+ def set(object)
51
+ unless options[:identity]
52
+ type.set(object)
53
+ else
54
+ if object.blank?
55
+ type.set(object)
56
+ else
57
+ options[:metadata].constraint.convert(object)
58
+ end
59
+ end
60
+ end
61
+
62
+ # Used for retrieving the object out of the attributes hash.
63
+ #
64
+ # Example:
65
+ #
66
+ # <tt>field.get("Value")</tt>
67
+ #
68
+ # Returns:
69
+ #
70
+ # The converted value.
71
+ def get(object)
72
+ type.get(object)
73
+ end
74
+
75
+ protected
76
+ # Slightly faster default check.
77
+ def copy
78
+ copyable ? @default.dup : @default
79
+ end
80
+
81
+ # Check if the name is valid.
82
+ def check_name!(name)
83
+ if Mongoid.destructive_fields.include?(name.to_s)
84
+ raise Mongoid::Errors::InvalidField.new(name)
85
+ end
86
+ end
87
+
88
+ def check_default!
89
+ return if @default.is_a?(Proc)
90
+ if !@default.nil? && !@default.is_a?(type)
91
+ raise Mongoid::Errors::InvalidType.new(type, @default)
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,138 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+
4
+ # This module defines behaviour for fields.
5
+ module Fields
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ # Set up the class attributes that must be available to all subclasses.
10
+ # These include defaults, fields
11
+ delegate :defaults, :fields, :to => "self.class"
12
+ end
13
+
14
+ module ClassMethods #:nodoc
15
+
16
+ # Defines all the fields that are accessable on the Document
17
+ # For each field that is defined, a getter and setter will be
18
+ # added as an instance method to the Document.
19
+ #
20
+ # @example Define a field.
21
+ # field :score, :type => Integer, :default => 0
22
+ #
23
+ # @param [ Symbol ] name The name of the field.
24
+ # @param [ Hash ] options The options to pass to the field.
25
+ #
26
+ # @option options [ Class ] :type The type of the field.
27
+ # @option options [ String ] :label The label for the field.
28
+ # @option options [ Object, Proc ] :default The field's default
29
+ def field(name, options = {})
30
+ access = name.to_s
31
+ set_field(access, options)
32
+ attr_protected name if options[:accessible] == false
33
+ end
34
+
35
+ # Return the fields for this class.
36
+ #
37
+ # @example Get the fields.
38
+ # Person.fields
39
+ #
40
+ # @return [ Hash ] The fields for this document.
41
+ #
42
+ # @since 2.0.0.rc.6
43
+ def fields
44
+ @fields ||= {}
45
+ end
46
+
47
+ # Set the fields for the class.
48
+ #
49
+ # @example Set the fields.
50
+ # Person.fields = fields
51
+ #
52
+ # @param [ Hash ] fields The hash of fields to set.
53
+ #
54
+ # @since 2.0.0.rc.6
55
+ def fields=(fields)
56
+ @fields = fields
57
+ end
58
+
59
+ # Returns the default values for the fields on the document.
60
+ #
61
+ # @example Get the defaults.
62
+ # Person.defaults
63
+ #
64
+ # @return [ Hash ] The field defaults.
65
+ def defaults
66
+ fields.inject({}) do |defs, (field_name,field)|
67
+ next(defs) if field.default.nil?
68
+ defs[field_name.to_s] = field.default
69
+ defs
70
+ end
71
+ end
72
+
73
+ # When inheriting, we want to copy the fields from the parent class and
74
+ # set the on the child to start, mimicing the behaviour of the old
75
+ # class_inheritable_accessor that was deprecated in Rails edge.
76
+ #
77
+ # @example Inherit from this class.
78
+ # Person.inherited(Doctor)
79
+ #
80
+ # @param [ Class ] subclass The inheriting class.
81
+ #
82
+ # @since 2.0.0.rc.6
83
+ def inherited(subclass)
84
+ subclass.fields = fields.dup
85
+ end
86
+
87
+ protected
88
+
89
+ # Define a field attribute for the +Document+.
90
+ #
91
+ # @example Set the field.
92
+ # Person.set_field(:name, :default => "Test")
93
+ #
94
+ # @param [ Symbol ] name The name of the field.
95
+ # @param [ Hash ] options The hash of options.
96
+ def set_field(name, options = {})
97
+ meth = options.delete(:as) || name
98
+ fields[name] = Field.new(name, options)
99
+ create_accessors(name, meth, options)
100
+ add_dirty_methods(name)
101
+ end
102
+
103
+ # Create the field accessors.
104
+ #
105
+ # @example Generate the accessors.
106
+ # Person.create_accessors(:name, "name")
107
+ # person.name #=> returns the field
108
+ # person.name = "" #=> sets the field
109
+ # person.name? #=> Is the field present?
110
+ #
111
+ # @param [ Symbol ] name The name of the field.
112
+ # @param [ Symbol ] meth The name of the accessor.
113
+ # @param [ Hash ] options The options.
114
+ def create_accessors(name, meth, options = {})
115
+ generated_field_methods.module_eval do
116
+ define_method(meth) { read_attribute(name) }
117
+ define_method("#{meth}=") { |value| write_attribute(name, value) }
118
+ define_method("#{meth}?") do
119
+ attr = read_attribute(name)
120
+ (options[:type] == Boolean) ? attr == true : attr.present?
121
+ end
122
+ end
123
+ end
124
+
125
+ # Include the field methods as a module, so they can be overridden.
126
+ #
127
+ # @example Include the fields.
128
+ # Person.generated_field_methods
129
+ def generated_field_methods
130
+ @generated_field_methods ||= begin
131
+ Module.new.tap do |mod|
132
+ include mod
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,173 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Finders #:nodoc:
4
+
5
+ # Delegate to the criteria methods that are natural for creating a new
6
+ # criteria.
7
+ [ :all_in, :any_in, :any_of, :asc, :ascending, :avg, :desc, :descending,
8
+ :excludes, :limit, :max, :min, :not_in, :only, :order_by,
9
+ :skip, :sum, :where, :update, :update_all, :near ].each do |name|
10
+ define_method(name) do |*args|
11
+ criteria.send(name, *args)
12
+ end
13
+ end
14
+
15
+ # Find +Documents+ given the conditions.
16
+ #
17
+ # Options:
18
+ #
19
+ # args: A +Hash+ with a conditions key and other options
20
+ #
21
+ # <tt>Person.all(:conditions => { :attribute => "value" })</tt>
22
+ def all(*args)
23
+ find(:all, *args)
24
+ end
25
+
26
+ # Returns a count of matching records in the database based on the
27
+ # provided arguments.
28
+ #
29
+ # <tt>Person.count(:conditions => { :attribute => "value" })</tt>
30
+ def count(*args)
31
+ Criteria.translate(self, false, *args).count
32
+ end
33
+
34
+ # Returns true if there are on document in database based on the
35
+ # provided arguments.
36
+ #
37
+ # <tt>Person.exists?(:conditions => { :attribute => "value" })</tt>
38
+ def exists?(*args)
39
+ Criteria.translate(self, false, *args).limit(1).count == 1
40
+ end
41
+
42
+ # Helper to initialize a new +Criteria+ object for this class, or return
43
+ # the currently scoped +Criteria+ object.
44
+ #
45
+ # Example:
46
+ #
47
+ # <tt>Person.criteria</tt>
48
+ def criteria(embedded = false)
49
+ scope_stack.last || Criteria.new(self, embedded)
50
+ end
51
+
52
+ # Find a +Document+ in several different ways.
53
+ #
54
+ # If a +String+ is provided, it will be assumed that it is a
55
+ # representation of a Mongo::ObjectID and will attempt to find a single
56
+ # +Document+ based on that id. If a +Symbol+ and +Hash+ is provided then
57
+ # it will attempt to find either a single +Document+ or multiples based
58
+ # on the conditions provided and the first parameter.
59
+ #
60
+ # Example:
61
+ #
62
+ # <tt>Person.find(:first, :conditions => { :attribute => "value" })</tt>
63
+ # <tt>Person.find(:all, :conditions => { :attribute => "value" })</tt>
64
+ # <tt>Person.find(BSON::ObjectId)</tt>
65
+ #
66
+ # Options:
67
+ #
68
+ # args: An assortment of finder options.
69
+ #
70
+ # Returns:
71
+ #
72
+ # A document or criteria.
73
+ def find(*args)
74
+ raise Errors::InvalidOptions.new(
75
+ :calling_document_find_with_nil_is_invalid, {}
76
+ ) if args[0].nil?
77
+ type, criteria = Criteria.parse!(self, false, *args)
78
+ case type
79
+ when :first then return criteria.one
80
+ when :last then return criteria.last
81
+ else
82
+ return criteria
83
+ end
84
+ end
85
+
86
+ # Find the first +Document+ given the conditions, or creates a new document
87
+ # with the conditions that were supplied
88
+ #
89
+ # Options:
90
+ #
91
+ # args: A +Hash+ of attributes
92
+ #
93
+ # <tt>Person.find_or_create_by(:attribute => "value")</tt>
94
+ def find_or_create_by(attrs = {})
95
+ find_or(:create, attrs)
96
+ end
97
+
98
+ # Find the first +Document+ given the conditions, or instantiates a new document
99
+ # with the conditions that were supplied
100
+ #
101
+ # Options:
102
+ #
103
+ # args: A +Hash+ of attributes
104
+ #
105
+ # <tt>Person.find_or_initialize_by(:attribute => "value")</tt>
106
+ def find_or_initialize_by(attrs = {})
107
+ find_or(:new, attrs)
108
+ end
109
+
110
+ # Find the first +Document+ given the conditions.
111
+ #
112
+ # Options:
113
+ #
114
+ # args: A +Hash+ with a conditions key and other options
115
+ #
116
+ # <tt>Person.first(:conditions => { :attribute => "value" })</tt>
117
+ def first(*args)
118
+ find(:first, *args)
119
+ end
120
+
121
+ # Find the last +Document+ given the conditions.
122
+ #
123
+ # Options:
124
+ #
125
+ # args: A +Hash+ with a conditions key and other options
126
+ #
127
+ # <tt>Person.last(:conditions => { :attribute => "value" })</tt>
128
+ def last(*args)
129
+ find(:last, *args)
130
+ end
131
+
132
+ # Find all documents in paginated fashion given the supplied arguments.
133
+ # If no parameters are passed just default to offset 0 and limit 20.
134
+ #
135
+ # Options:
136
+ #
137
+ # params: A +Hash+ of params to pass to the Criteria API.
138
+ #
139
+ # Example:
140
+ #
141
+ # <tt>Person.paginate(:conditions => { :field => "Test" }, :page => 1,
142
+ # :per_page => 20)</tt>
143
+ #
144
+ # Returns paginated array of docs.
145
+ def paginate(params = {})
146
+ Criteria.translate(self, false, params).paginate
147
+ end
148
+
149
+ protected
150
+ # Find the first object or create/initialize it.
151
+ def find_or(method, attrs = {})
152
+ first(:conditions => attrs) || send(method, attrs)
153
+ end
154
+
155
+ # Initializes and returns the current scope stack.
156
+ def scope_stack
157
+ scope_stack_for = Thread.current[:mongoid_scope_stack] ||= {}
158
+ scope_stack_for[object_id] ||= []
159
+ end
160
+
161
+ # Pushes the provided criteria onto the scope stack, and removes it after the
162
+ # provided block is yielded.
163
+ def with_scope(criteria)
164
+ scope_stack = self.scope_stack
165
+ scope_stack << criteria
166
+ begin
167
+ yield criteria
168
+ ensure
169
+ scope_stack.pop
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,85 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+ module Hierarchy #:nodoc
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ attr_accessor :_parent
7
+ end
8
+
9
+ module ClassMethods #:nodoc:
10
+
11
+ # Determines if the document is a subclass of another document.
12
+ #
13
+ # @example Check if the document is a subclass.
14
+ # Square.hereditary?
15
+ #
16
+ # @return [ true, false ] True if hereditary, false if not.
17
+ def hereditary?
18
+ Mongoid::Document > superclass
19
+ end
20
+ end
21
+
22
+ module InstanceMethods #:nodoc:
23
+
24
+ # Get all child +Documents+ to this +Document+, going n levels deep if
25
+ # necessary. This is used when calling update persistence operations from
26
+ # the root document, where changes in the entire tree need to be
27
+ # determined. Note that persistence from the embedded documents will
28
+ # always be preferred, since they are optimized calls... This operation
29
+ # can get expensive in domains with large hierarchies.
30
+ #
31
+ # @example Get all the document's children.
32
+ # person._children
33
+ #
34
+ # @return [ Array<Document> ] All child documents in the hierarchy.
35
+ def _children
36
+ relations.inject([]) do |children, (name, metadata)|
37
+ children.tap do |kids|
38
+ if metadata.embedded? && name != "versions"
39
+ child = send(name)
40
+ child.to_a.each do |doc|
41
+ kids.push(doc).concat(doc._children)
42
+ end unless child.blank?
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ # Determines if the document is a subclass of another document.
49
+ #
50
+ # @example Check if the document is a subclass
51
+ # Square.new.hereditary?
52
+ #
53
+ # @return [ true, false ] True if hereditary, false if not.
54
+ def hereditary?
55
+ self.class.hereditary?
56
+ end
57
+
58
+ # Sets up a child/parent association. This is used for newly created
59
+ # objects so they can be properly added to the graph.
60
+ #
61
+ # @example Set the parent document.
62
+ # document.parentize(parent)
63
+ #
64
+ # @param [ Document ] document The parent document.
65
+ #
66
+ # @return [ Document ] The parent document.
67
+ def parentize(document)
68
+ self._parent = document
69
+ end
70
+
71
+ # Return the root document in the object graph. If the current document
72
+ # is the root object in the graph it will return self.
73
+ #
74
+ # @example Get the root document in the hierarchy.
75
+ # document._root
76
+ #
77
+ # @return [ Document ] The root document in the hierarchy.
78
+ def _root
79
+ object = self
80
+ while (object._parent) do object = object._parent; end
81
+ object || self
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,89 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ class Identity #:nodoc:
4
+
5
+ attr_reader :document
6
+
7
+ # Create the identity for the document. The id will be set in either in
8
+ # the form of a Mongo object id or a composite key set up by defining
9
+ # a key on the document. The _type will be set to the document's class
10
+ # name.
11
+ #
12
+ # @example Create the id and set the type.
13
+ # identity.create
14
+ def create
15
+ identify and type
16
+ end
17
+
18
+ # Create the new identity generator - this will be expanded in the future
19
+ # to support pk generators.
20
+ #
21
+ # @example
22
+ # Identity.new(document)
23
+ #
24
+ # @param [ Document ] document The document to generate an id for.
25
+ #
26
+ # @return [ Identity ] The new identity object.
27
+ def initialize(document)
28
+ @document = document
29
+ end
30
+
31
+ private
32
+
33
+ # Return the proper id for the document. Will be an object id or its string
34
+ # representation depending on the configuration.
35
+ #
36
+ # @example Generate the id.
37
+ # identity.generate_id
38
+ #
39
+ # @return [ Object ] The bson object id or its string equivalent.
40
+ def generate_id
41
+ id = BSON::ObjectId.new
42
+ document.using_object_ids? ? id : id.to_s
43
+ end
44
+
45
+ # Sets the id on the document. Will either set a newly generated id or
46
+ # build the composite key.
47
+ #
48
+ # @example Set the id.
49
+ # identity.identify
50
+ def identify
51
+ document.id = compose.join(" ").identify if document.primary_key
52
+ document.id = generate_id if document.id.blank?
53
+ end
54
+
55
+ # Set the _type field on the document if the document is hereditary or in a
56
+ # polymorphic relation.
57
+ #
58
+ # @example Set the type.
59
+ # identity.type
60
+ def type
61
+ document._type = document.class.name if typed?
62
+ end
63
+
64
+ # Generates the array of keys to build the id.
65
+ #
66
+ # @example Build the array for the keys.
67
+ # identity.compose.
68
+ #
69
+ # @return [ Array<Object> ] The array of keys.
70
+ def compose
71
+ document.primary_key.collect do |key|
72
+ document.attributes[key]
73
+ end.reject { |val| val.nil? }
74
+ end
75
+
76
+ # Determines if the document stores the type information. This is if it is
77
+ # in a hierarchy, has subclasses, or is in a polymorphic relation.
78
+ #
79
+ # @example Check if the document is typed.
80
+ # identity.typed?
81
+ #
82
+ # @return [ true, false ] True if typed, false if not.
83
+ def typed?
84
+ document.hereditary? ||
85
+ document.class.descendants.any? ||
86
+ document.polymorphic?
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+ module Indexes #:nodoc
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ cattr_accessor :index_options
7
+ self.index_options = {}
8
+ end
9
+
10
+ module ClassMethods #:nodoc
11
+
12
+ # Send the actual index creation comments to the MongoDB driver
13
+ def create_indexes
14
+ return unless index_options
15
+ current_collection = self._collection || set_collection
16
+ index_options.each do |name, options|
17
+ current_collection.create_index(name, options)
18
+ end
19
+ end
20
+
21
+ # Add the default indexes to the root document if they do not already
22
+ # exist. Currently this is only _type.
23
+ def add_indexes
24
+ if hereditary? && !index_options[:_type]
25
+ self.index_options[:_type] = {:unique => false, :background => true}
26
+ end
27
+ create_indexes if Mongoid.autocreate_indexes
28
+ end
29
+
30
+ # Adds an index on the field specified. Options can be :unique => true or
31
+ # :unique => false. It will default to the latter.
32
+ def index(name, options = { :unique => false })
33
+ self.index_options[name] = options
34
+ create_indexes if Mongoid.autocreate_indexes
35
+ end
36
+ end
37
+ end
38
+ end