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.
- data/LICENSE +20 -0
- data/README.rdoc +50 -0
- data/Rakefile +51 -0
- data/lib/config/locales/bg.yml +44 -0
- data/lib/config/locales/de.yml +44 -0
- data/lib/config/locales/en.yml +45 -0
- data/lib/config/locales/es.yml +44 -0
- data/lib/config/locales/fr.yml +45 -0
- data/lib/config/locales/hu.yml +47 -0
- data/lib/config/locales/it.yml +42 -0
- data/lib/config/locales/kr.yml +68 -0
- data/lib/config/locales/nl.yml +42 -0
- data/lib/config/locales/pl.yml +42 -0
- data/lib/config/locales/pt-br.yml +43 -0
- data/lib/config/locales/pt.yml +43 -0
- data/lib/config/locales/ro.yml +49 -0
- data/lib/config/locales/sv.yml +43 -0
- data/lib/config/locales/zh-CN.yml +34 -0
- data/lib/mongoid/atomicity.rb +111 -0
- data/lib/mongoid/attributes.rb +251 -0
- data/lib/mongoid/callbacks.rb +13 -0
- data/lib/mongoid/collection.rb +137 -0
- data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
- data/lib/mongoid/collections/master.rb +29 -0
- data/lib/mongoid/collections/operations.rb +42 -0
- data/lib/mongoid/collections/slaves.rb +45 -0
- data/lib/mongoid/collections.rb +70 -0
- data/lib/mongoid/components.rb +45 -0
- data/lib/mongoid/config/database.rb +167 -0
- data/lib/mongoid/config/replset_database.rb +48 -0
- data/lib/mongoid/config.rb +343 -0
- data/lib/mongoid/contexts/enumerable/sort.rb +43 -0
- data/lib/mongoid/contexts/enumerable.rb +226 -0
- data/lib/mongoid/contexts/ids.rb +25 -0
- data/lib/mongoid/contexts/mongo.rb +345 -0
- data/lib/mongoid/contexts/paging.rb +50 -0
- data/lib/mongoid/contexts.rb +21 -0
- data/lib/mongoid/copyable.rb +44 -0
- data/lib/mongoid/criteria.rb +325 -0
- data/lib/mongoid/criterion/complex.rb +34 -0
- data/lib/mongoid/criterion/creational.rb +34 -0
- data/lib/mongoid/criterion/exclusion.rb +67 -0
- data/lib/mongoid/criterion/inclusion.rb +134 -0
- data/lib/mongoid/criterion/inspection.rb +20 -0
- data/lib/mongoid/criterion/optional.rb +213 -0
- data/lib/mongoid/criterion/selector.rb +74 -0
- data/lib/mongoid/cursor.rb +81 -0
- data/lib/mongoid/default_scope.rb +28 -0
- data/lib/mongoid/dirty.rb +251 -0
- data/lib/mongoid/document.rb +256 -0
- data/lib/mongoid/errors/document_not_found.rb +29 -0
- data/lib/mongoid/errors/invalid_collection.rb +19 -0
- data/lib/mongoid/errors/invalid_database.rb +20 -0
- data/lib/mongoid/errors/invalid_field.rb +19 -0
- data/lib/mongoid/errors/invalid_options.rb +16 -0
- data/lib/mongoid/errors/invalid_type.rb +26 -0
- data/lib/mongoid/errors/mongoid_error.rb +27 -0
- data/lib/mongoid/errors/too_many_nested_attribute_records.rb +21 -0
- data/lib/mongoid/errors/unsaved_document.rb +23 -0
- data/lib/mongoid/errors/unsupported_version.rb +21 -0
- data/lib/mongoid/errors/validations.rb +24 -0
- data/lib/mongoid/errors.rb +12 -0
- data/lib/mongoid/extensions/array/conversions.rb +23 -0
- data/lib/mongoid/extensions/array/parentization.rb +13 -0
- data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
- data/lib/mongoid/extensions/binary/conversions.rb +17 -0
- data/lib/mongoid/extensions/boolean/conversions.rb +27 -0
- data/lib/mongoid/extensions/date/conversions.rb +25 -0
- data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
- data/lib/mongoid/extensions/false_class/equality.rb +13 -0
- data/lib/mongoid/extensions/float/conversions.rb +20 -0
- data/lib/mongoid/extensions/hash/conversions.rb +19 -0
- data/lib/mongoid/extensions/hash/criteria_helpers.rb +22 -0
- data/lib/mongoid/extensions/hash/scoping.rb +12 -0
- data/lib/mongoid/extensions/integer/conversions.rb +20 -0
- data/lib/mongoid/extensions/nil/collectionization.rb +12 -0
- data/lib/mongoid/extensions/object/conversions.rb +25 -0
- data/lib/mongoid/extensions/object/reflections.rb +17 -0
- data/lib/mongoid/extensions/object/yoda.rb +27 -0
- data/lib/mongoid/extensions/object_id/conversions.rb +57 -0
- data/lib/mongoid/extensions/proc/scoping.rb +12 -0
- data/lib/mongoid/extensions/set/conversions.rb +20 -0
- data/lib/mongoid/extensions/string/conversions.rb +34 -0
- data/lib/mongoid/extensions/string/inflections.rb +97 -0
- data/lib/mongoid/extensions/symbol/conversions.rb +21 -0
- data/lib/mongoid/extensions/symbol/inflections.rb +40 -0
- data/lib/mongoid/extensions/time_conversions.rb +38 -0
- data/lib/mongoid/extensions/true_class/equality.rb +13 -0
- data/lib/mongoid/extensions.rb +116 -0
- data/lib/mongoid/extras.rb +61 -0
- data/lib/mongoid/factory.rb +20 -0
- data/lib/mongoid/field.rb +95 -0
- data/lib/mongoid/fields.rb +138 -0
- data/lib/mongoid/finders.rb +173 -0
- data/lib/mongoid/hierarchy.rb +85 -0
- data/lib/mongoid/identity.rb +89 -0
- data/lib/mongoid/indexes.rb +38 -0
- data/lib/mongoid/inspection.rb +58 -0
- data/lib/mongoid/javascript/functions.yml +37 -0
- data/lib/mongoid/javascript.rb +21 -0
- data/lib/mongoid/json.rb +16 -0
- data/lib/mongoid/keys.rb +77 -0
- data/lib/mongoid/logger.rb +18 -0
- data/lib/mongoid/matchers/all.rb +11 -0
- data/lib/mongoid/matchers/default.rb +27 -0
- data/lib/mongoid/matchers/exists.rb +13 -0
- data/lib/mongoid/matchers/gt.rb +11 -0
- data/lib/mongoid/matchers/gte.rb +11 -0
- data/lib/mongoid/matchers/in.rb +11 -0
- data/lib/mongoid/matchers/lt.rb +11 -0
- data/lib/mongoid/matchers/lte.rb +11 -0
- data/lib/mongoid/matchers/ne.rb +11 -0
- data/lib/mongoid/matchers/nin.rb +11 -0
- data/lib/mongoid/matchers/size.rb +11 -0
- data/lib/mongoid/matchers.rb +55 -0
- data/lib/mongoid/modifiers/command.rb +18 -0
- data/lib/mongoid/modifiers/inc.rb +24 -0
- data/lib/mongoid/modifiers.rb +24 -0
- data/lib/mongoid/multi_database.rb +11 -0
- data/lib/mongoid/multi_parameter_attributes.rb +80 -0
- data/lib/mongoid/named_scope.rb +36 -0
- data/lib/mongoid/nested_attributes.rb +43 -0
- data/lib/mongoid/paranoia.rb +103 -0
- data/lib/mongoid/paths.rb +61 -0
- data/lib/mongoid/persistence/command.rb +59 -0
- data/lib/mongoid/persistence/insert.rb +53 -0
- data/lib/mongoid/persistence/insert_embedded.rb +42 -0
- data/lib/mongoid/persistence/remove.rb +44 -0
- data/lib/mongoid/persistence/remove_all.rb +40 -0
- data/lib/mongoid/persistence/remove_embedded.rb +48 -0
- data/lib/mongoid/persistence/update.rb +76 -0
- data/lib/mongoid/persistence.rb +237 -0
- data/lib/mongoid/railtie.rb +129 -0
- data/lib/mongoid/railties/database.rake +171 -0
- data/lib/mongoid/railties/document.rb +12 -0
- data/lib/mongoid/relations/accessors.rb +157 -0
- data/lib/mongoid/relations/auto_save.rb +34 -0
- data/lib/mongoid/relations/binding.rb +26 -0
- data/lib/mongoid/relations/bindings/embedded/in.rb +82 -0
- data/lib/mongoid/relations/bindings/embedded/many.rb +98 -0
- data/lib/mongoid/relations/bindings/embedded/one.rb +66 -0
- data/lib/mongoid/relations/bindings/referenced/in.rb +74 -0
- data/lib/mongoid/relations/bindings/referenced/many.rb +96 -0
- data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +99 -0
- data/lib/mongoid/relations/bindings/referenced/one.rb +66 -0
- data/lib/mongoid/relations/bindings.rb +9 -0
- data/lib/mongoid/relations/builder.rb +42 -0
- data/lib/mongoid/relations/builders/embedded/in.rb +25 -0
- data/lib/mongoid/relations/builders/embedded/many.rb +32 -0
- data/lib/mongoid/relations/builders/embedded/one.rb +26 -0
- data/lib/mongoid/relations/builders/nested_attributes/many.rb +116 -0
- data/lib/mongoid/relations/builders/nested_attributes/one.rb +135 -0
- data/lib/mongoid/relations/builders/referenced/in.rb +32 -0
- data/lib/mongoid/relations/builders/referenced/many.rb +26 -0
- data/lib/mongoid/relations/builders/referenced/many_to_many.rb +29 -0
- data/lib/mongoid/relations/builders/referenced/one.rb +30 -0
- data/lib/mongoid/relations/builders.rb +79 -0
- data/lib/mongoid/relations/cascading/delete.rb +19 -0
- data/lib/mongoid/relations/cascading/destroy.rb +19 -0
- data/lib/mongoid/relations/cascading/nullify.rb +18 -0
- data/lib/mongoid/relations/cascading/strategy.rb +26 -0
- data/lib/mongoid/relations/cascading.rb +55 -0
- data/lib/mongoid/relations/constraint.rb +45 -0
- data/lib/mongoid/relations/cyclic.rb +97 -0
- data/lib/mongoid/relations/embedded/in.rb +173 -0
- data/lib/mongoid/relations/embedded/many.rb +483 -0
- data/lib/mongoid/relations/embedded/one.rb +170 -0
- data/lib/mongoid/relations/macros.rb +306 -0
- data/lib/mongoid/relations/many.rb +171 -0
- data/lib/mongoid/relations/metadata.rb +533 -0
- data/lib/mongoid/relations/nested_builder.rb +68 -0
- data/lib/mongoid/relations/one.rb +47 -0
- data/lib/mongoid/relations/polymorphic.rb +54 -0
- data/lib/mongoid/relations/proxy.rb +128 -0
- data/lib/mongoid/relations/referenced/in.rb +216 -0
- data/lib/mongoid/relations/referenced/many.rb +443 -0
- data/lib/mongoid/relations/referenced/many_to_many.rb +344 -0
- data/lib/mongoid/relations/referenced/one.rb +206 -0
- data/lib/mongoid/relations/reflections.rb +45 -0
- data/lib/mongoid/relations.rb +105 -0
- data/lib/mongoid/safe.rb +23 -0
- data/lib/mongoid/safety.rb +207 -0
- data/lib/mongoid/scope.rb +31 -0
- data/lib/mongoid/serialization.rb +99 -0
- data/lib/mongoid/state.rb +66 -0
- data/lib/mongoid/timestamps.rb +38 -0
- data/lib/mongoid/validations/associated.rb +42 -0
- data/lib/mongoid/validations/uniqueness.rb +85 -0
- data/lib/mongoid/validations.rb +117 -0
- data/lib/mongoid/version.rb +4 -0
- data/lib/mongoid/versioning.rb +51 -0
- data/lib/mongoid.rb +139 -0
- data/lib/rails/generators/mongoid/config/config_generator.rb +25 -0
- data/lib/rails/generators/mongoid/config/templates/mongoid.yml +23 -0
- data/lib/rails/generators/mongoid/model/model_generator.rb +24 -0
- data/lib/rails/generators/mongoid/model/templates/model.rb +17 -0
- data/lib/rails/generators/mongoid_generator.rb +61 -0
- data/lib/rails/mongoid.rb +57 -0
- metadata +380 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Mongoid #:nodoc:
|
|
3
|
+
module Criterion #:nodoc:
|
|
4
|
+
module Optional
|
|
5
|
+
|
|
6
|
+
# Adds fields to be sorted in ascending order. Will add them in the order
|
|
7
|
+
# they were passed into the method.
|
|
8
|
+
#
|
|
9
|
+
# Example:
|
|
10
|
+
#
|
|
11
|
+
# <tt>criteria.ascending(:title, :dob)</tt>
|
|
12
|
+
def ascending(*fields)
|
|
13
|
+
clone.tap do |crit|
|
|
14
|
+
crit.options[:sort] = [] unless options[:sort] || fields.first.nil?
|
|
15
|
+
fields.flatten.each { |field| crit.options[:sort] << [ field, :asc ] }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
alias :asc :ascending
|
|
19
|
+
|
|
20
|
+
# Tells the criteria that the cursor that gets returned needs to be
|
|
21
|
+
# cached. This is so multiple iterations don't hit the database multiple
|
|
22
|
+
# times, however this is not advisable when working with large data sets
|
|
23
|
+
# as the entire results will get stored in memory.
|
|
24
|
+
#
|
|
25
|
+
# Example:
|
|
26
|
+
#
|
|
27
|
+
# <tt>criteria.cache</tt>
|
|
28
|
+
def cache
|
|
29
|
+
clone.tap { |crit| crit.options.merge!(:cache => true) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Will return true if the cache option has been set.
|
|
33
|
+
#
|
|
34
|
+
# Example:
|
|
35
|
+
#
|
|
36
|
+
# <tt>criteria.cached?</tt>
|
|
37
|
+
def cached?
|
|
38
|
+
options[:cache] == true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Adds fields to be sorted in descending order. Will add them in the order
|
|
42
|
+
# they were passed into the method.
|
|
43
|
+
#
|
|
44
|
+
# Example:
|
|
45
|
+
#
|
|
46
|
+
# <tt>criteria.descending(:title, :dob)</tt>
|
|
47
|
+
def descending(*fields)
|
|
48
|
+
clone.tap do |crit|
|
|
49
|
+
crit.options[:sort] = [] unless options[:sort] || fields.first.nil?
|
|
50
|
+
fields.flatten.each { |field| crit.options[:sort] << [ field, :desc ] }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
alias :desc :descending
|
|
54
|
+
|
|
55
|
+
# Flags the criteria to execute against a read-only slave in the pool
|
|
56
|
+
# instead of master.
|
|
57
|
+
#
|
|
58
|
+
# Example:
|
|
59
|
+
#
|
|
60
|
+
# <tt>criteria.enslave</tt>
|
|
61
|
+
def enslave
|
|
62
|
+
clone.tap { |crit| crit.options.merge!(:enslave => true) }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Will return true if the criteria is enslaved.
|
|
66
|
+
#
|
|
67
|
+
# Example:
|
|
68
|
+
#
|
|
69
|
+
# <tt>criteria.enslaved?</tt>
|
|
70
|
+
def enslaved?
|
|
71
|
+
options[:enslave] == true
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Adds a criterion to the +Criteria+ that specifies additional options
|
|
75
|
+
# to be passed to the Ruby driver, in the exact format for the driver.
|
|
76
|
+
#
|
|
77
|
+
# Options:
|
|
78
|
+
#
|
|
79
|
+
# extras: A +Hash+ that gets set to the driver options.
|
|
80
|
+
#
|
|
81
|
+
# Example:
|
|
82
|
+
#
|
|
83
|
+
# <tt>criteria.extras(:limit => 20, :skip => 40)</tt>
|
|
84
|
+
#
|
|
85
|
+
# Returns: <tt>self</tt>
|
|
86
|
+
def extras(extras)
|
|
87
|
+
clone.tap do |crit|
|
|
88
|
+
crit.options.merge!(extras)
|
|
89
|
+
crit.filter_options
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Adds a criterion to the +Criteria+ that specifies an id that must be matched.
|
|
94
|
+
#
|
|
95
|
+
# Options:
|
|
96
|
+
#
|
|
97
|
+
# object_id: A single id or an array of ids in +String+ or <tt>BSON::ObjectId</tt> format
|
|
98
|
+
#
|
|
99
|
+
# Example:
|
|
100
|
+
#
|
|
101
|
+
# <tt>criteria.id("4ab2bc4b8ad548971900005c")</tt>
|
|
102
|
+
# <tt>criteria.id(["4ab2bc4b8ad548971900005c", "4c454e7ebf4b98032d000001"])</tt>
|
|
103
|
+
#
|
|
104
|
+
# Returns: <tt>self</tt>
|
|
105
|
+
def id(*ids)
|
|
106
|
+
ids.flatten!
|
|
107
|
+
if ids.size > 1
|
|
108
|
+
any_in(
|
|
109
|
+
:_id => ::BSON::ObjectId.cast!(klass, ids, klass.primary_key.nil?)
|
|
110
|
+
)
|
|
111
|
+
else
|
|
112
|
+
clone.tap do |crit|
|
|
113
|
+
crit.selector[:_id] =
|
|
114
|
+
::BSON::ObjectId.cast!(klass, ids.first, klass.primary_key.nil?)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Adds a criterion to the +Criteria+ that specifies the maximum number of
|
|
120
|
+
# results to return. This is mostly used in conjunction with <tt>skip()</tt>
|
|
121
|
+
# to handle paginated results.
|
|
122
|
+
#
|
|
123
|
+
# Options:
|
|
124
|
+
#
|
|
125
|
+
# value: An +Integer+ specifying the max number of results. Defaults to 20.
|
|
126
|
+
#
|
|
127
|
+
# Example:
|
|
128
|
+
#
|
|
129
|
+
# <tt>criteria.limit(100)</tt>
|
|
130
|
+
#
|
|
131
|
+
# Returns: <tt>self</tt>
|
|
132
|
+
def limit(value = 20)
|
|
133
|
+
clone.tap { |crit| crit.options[:limit] = value }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Returns the offset option. If a per_page option is in the list then it
|
|
137
|
+
# will replace it with a skip parameter and return the same value. Defaults
|
|
138
|
+
# to 20 if nothing was provided.
|
|
139
|
+
def offset(*args)
|
|
140
|
+
args.size > 0 ? skip(args.first) : options[:skip]
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Adds a criterion to the +Criteria+ that specifies the sort order of
|
|
144
|
+
# the returned documents in the database. Similar to a SQL "ORDER BY".
|
|
145
|
+
#
|
|
146
|
+
# Options:
|
|
147
|
+
#
|
|
148
|
+
# params: An +Array+ of [field, direction] sorting pairs.
|
|
149
|
+
#
|
|
150
|
+
# Example:
|
|
151
|
+
#
|
|
152
|
+
# <tt>criteria.order_by([[:field1, :asc], [:field2, :desc]])</tt>
|
|
153
|
+
#
|
|
154
|
+
# Returns: <tt>self</tt>
|
|
155
|
+
def order_by(*args)
|
|
156
|
+
clone.tap do |crit|
|
|
157
|
+
crit.options[:sort] = [] unless options[:sort] || args.first.nil?
|
|
158
|
+
arguments = args.first
|
|
159
|
+
case arguments
|
|
160
|
+
when Hash
|
|
161
|
+
arguments.each do |field, direction|
|
|
162
|
+
crit.options[:sort] << [ field, direction ]
|
|
163
|
+
end
|
|
164
|
+
when Array
|
|
165
|
+
crit.options[:sort].concat(arguments)
|
|
166
|
+
when Complex
|
|
167
|
+
args.flatten.each do |complex|
|
|
168
|
+
crit.options[:sort] << [ complex.key, complex.operator.to_sym ]
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
alias :order :order_by
|
|
174
|
+
|
|
175
|
+
# Adds a criterion to the +Criteria+ that specifies how many results to skip
|
|
176
|
+
# when returning Documents. This is mostly used in conjunction with
|
|
177
|
+
# <tt>limit()</tt> to handle paginated results, and is similar to the
|
|
178
|
+
# traditional "offset" parameter.
|
|
179
|
+
#
|
|
180
|
+
# Options:
|
|
181
|
+
#
|
|
182
|
+
# value: An +Integer+ specifying the number of results to skip. Defaults to 0.
|
|
183
|
+
#
|
|
184
|
+
# Example:
|
|
185
|
+
#
|
|
186
|
+
# <tt>criteria.skip(20)</tt>
|
|
187
|
+
#
|
|
188
|
+
# Returns: <tt>self</tt>
|
|
189
|
+
def skip(value = 0)
|
|
190
|
+
clone.tap { |crit| crit.options[:skip] = value }
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Adds a criterion to the +Criteria+ that specifies a type or an Array of
|
|
194
|
+
# type that must be matched.
|
|
195
|
+
#
|
|
196
|
+
# Options:
|
|
197
|
+
#
|
|
198
|
+
# types : An +Array+ of types of a +String+ representing the Type of you search
|
|
199
|
+
#
|
|
200
|
+
# Example:
|
|
201
|
+
#
|
|
202
|
+
# <tt>criteria.type('Browser')</tt>
|
|
203
|
+
# <tt>criteria.type(['Firefox', 'Browser'])</tt>
|
|
204
|
+
#
|
|
205
|
+
# Returns: <tt>self</tt>
|
|
206
|
+
def type(types)
|
|
207
|
+
types = [types] unless types.is_a?(Array)
|
|
208
|
+
any_in(:_type => types)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Mongoid #:nodoc:
|
|
3
|
+
module Criterion #:nodoc:
|
|
4
|
+
|
|
5
|
+
class Selector < Hash
|
|
6
|
+
attr_reader :klass
|
|
7
|
+
|
|
8
|
+
def initialize(klass)
|
|
9
|
+
@klass = klass
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def []=(key, value)
|
|
13
|
+
super(key, try_to_typecast(key, value))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def merge!(other)
|
|
17
|
+
other.each_pair do |key, value|
|
|
18
|
+
self[key] = value
|
|
19
|
+
end
|
|
20
|
+
self
|
|
21
|
+
end
|
|
22
|
+
alias update merge!
|
|
23
|
+
|
|
24
|
+
if RUBY_VERSION < '1.9'
|
|
25
|
+
def inspect
|
|
26
|
+
ret = self.keys.inject([]) do |ret, key|
|
|
27
|
+
ret << "#{key.inspect}=>#{self[key].inspect}"
|
|
28
|
+
end
|
|
29
|
+
"{#{ret.sort.join(', ')}}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def try_to_typecast(key, value)
|
|
36
|
+
access = key.to_s
|
|
37
|
+
return value unless klass.fields.has_key?(access)
|
|
38
|
+
|
|
39
|
+
field = klass.fields[access]
|
|
40
|
+
typecast_value_for(field, value)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def typecast_value_for(field, value)
|
|
44
|
+
return field.set(value) if field.type === value
|
|
45
|
+
case value
|
|
46
|
+
when Hash
|
|
47
|
+
value = value.dup
|
|
48
|
+
value.each_pair do |k, v|
|
|
49
|
+
value[k] = typecast_hash_value(field, k, v)
|
|
50
|
+
end
|
|
51
|
+
when Array
|
|
52
|
+
value.map { |v| typecast_value_for(field, v) }
|
|
53
|
+
when Regexp
|
|
54
|
+
value
|
|
55
|
+
else
|
|
56
|
+
field.type == Array ? value.class.set(value) : field.set(value)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def typecast_hash_value(field, key, value)
|
|
61
|
+
case key
|
|
62
|
+
when "$exists"
|
|
63
|
+
Boolean.set(value)
|
|
64
|
+
when "$size"
|
|
65
|
+
Integer.set(value)
|
|
66
|
+
else
|
|
67
|
+
typecast_value_for(field, value)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Mongoid #:nodoc
|
|
3
|
+
class Cursor
|
|
4
|
+
include Enumerable
|
|
5
|
+
# Operations on the Mongo::Cursor object that will not get overriden by the
|
|
6
|
+
# Mongoid::Cursor are defined here.
|
|
7
|
+
OPERATIONS = [
|
|
8
|
+
:close,
|
|
9
|
+
:closed?,
|
|
10
|
+
:count,
|
|
11
|
+
:explain,
|
|
12
|
+
:fields,
|
|
13
|
+
:full_collection_name,
|
|
14
|
+
:hint,
|
|
15
|
+
:limit,
|
|
16
|
+
:order,
|
|
17
|
+
:query_options_hash,
|
|
18
|
+
:query_opts,
|
|
19
|
+
:selector,
|
|
20
|
+
:skip,
|
|
21
|
+
:snapshot,
|
|
22
|
+
:sort,
|
|
23
|
+
:timeout
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
attr_reader :collection
|
|
27
|
+
|
|
28
|
+
# The operations above will all delegate to the proxied Mongo::Cursor.
|
|
29
|
+
#
|
|
30
|
+
# Example:
|
|
31
|
+
#
|
|
32
|
+
# <tt>cursor.close</tt>
|
|
33
|
+
OPERATIONS.each do |name|
|
|
34
|
+
define_method(name) { |*args| @cursor.send(name, *args) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Iterate over each document in the cursor and yield to it.
|
|
38
|
+
#
|
|
39
|
+
# Example:
|
|
40
|
+
#
|
|
41
|
+
# <tt>cursor.each { |doc| p doc.title }</tt>
|
|
42
|
+
def each
|
|
43
|
+
@cursor.each do |document|
|
|
44
|
+
yield Mongoid::Factory.build(@klass, document)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Create the new +Mongoid::Cursor+.
|
|
49
|
+
#
|
|
50
|
+
# Options:
|
|
51
|
+
#
|
|
52
|
+
# collection: The Mongoid::Collection instance.
|
|
53
|
+
# cursor: The Mongo::Cursor to be proxied.
|
|
54
|
+
#
|
|
55
|
+
# Example:
|
|
56
|
+
#
|
|
57
|
+
# <tt>Mongoid::Cursor.new(Person, cursor)</tt>
|
|
58
|
+
def initialize(klass, collection, cursor)
|
|
59
|
+
@klass, @collection, @cursor = klass, collection, cursor
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Return the next document in the cursor. Will instantiate a new Mongoid
|
|
63
|
+
# document with the attributes.
|
|
64
|
+
#
|
|
65
|
+
# Example:
|
|
66
|
+
#
|
|
67
|
+
# <tt>cursor.next_document</tt>
|
|
68
|
+
def next_document
|
|
69
|
+
Mongoid::Factory.build(@klass, @cursor.next_document)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Returns an array of all the documents in the cursor.
|
|
73
|
+
#
|
|
74
|
+
# Example:
|
|
75
|
+
#
|
|
76
|
+
# <tt>cursor.to_a</tt>
|
|
77
|
+
def to_a
|
|
78
|
+
@cursor.to_a.collect { |attrs| Mongoid::Factory.build(@klass, attrs) }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Mongoid #:nodoc:
|
|
3
|
+
|
|
4
|
+
# This module handles functionality for creating default scopes.
|
|
5
|
+
module DefaultScope
|
|
6
|
+
|
|
7
|
+
# Creates a default_scope for the +Document+, similar to ActiveRecord's
|
|
8
|
+
# default_scope. +DefaultScopes+ are proxied +Criteria+ objects that are
|
|
9
|
+
# applied by default to all queries for the class.
|
|
10
|
+
#
|
|
11
|
+
# @example Create a default scope.
|
|
12
|
+
#
|
|
13
|
+
# class Person
|
|
14
|
+
# include Mongoid::Document
|
|
15
|
+
# field :active, :type => Boolean
|
|
16
|
+
# field :count, :type => Integer
|
|
17
|
+
#
|
|
18
|
+
# default_scope :where => { :active => true }
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# @param [ Hash ] conditions The conditions to create with.
|
|
22
|
+
#
|
|
23
|
+
# @since 2.0.0.rc.1
|
|
24
|
+
def default_scope(conditions = {}, &block)
|
|
25
|
+
self.scope_stack << criteria.fuse(Scope.new(conditions, &block).conditions.scoped)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Mongoid #:nodoc:
|
|
3
|
+
module Dirty #:nodoc:
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
# Gets the changes for a specific field.
|
|
7
|
+
#
|
|
8
|
+
# Example:
|
|
9
|
+
#
|
|
10
|
+
# person = Person.new(:title => "Sir")
|
|
11
|
+
# person.title = "Madam"
|
|
12
|
+
# person.attribute_change("title") # [ "Sir", "Madam" ]
|
|
13
|
+
#
|
|
14
|
+
# Returns:
|
|
15
|
+
#
|
|
16
|
+
# An +Array+ containing the old and new values.
|
|
17
|
+
def attribute_change(name)
|
|
18
|
+
modifications[name]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Determines if a specific field has chaged.
|
|
22
|
+
#
|
|
23
|
+
# Example:
|
|
24
|
+
#
|
|
25
|
+
# person = Person.new(:title => "Sir")
|
|
26
|
+
# person.title = "Madam"
|
|
27
|
+
# person.attribute_changed?("title") # true
|
|
28
|
+
#
|
|
29
|
+
# Returns:
|
|
30
|
+
#
|
|
31
|
+
# +true+ if changed, +false+ if not.
|
|
32
|
+
def attribute_changed?(name)
|
|
33
|
+
modifications.include?(name)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Gets the old value for a specific field.
|
|
37
|
+
#
|
|
38
|
+
# Example:
|
|
39
|
+
#
|
|
40
|
+
# person = Person.new(:title => "Sir")
|
|
41
|
+
# person.title = "Madam"
|
|
42
|
+
# person.attribute_was("title") # "Sir"
|
|
43
|
+
#
|
|
44
|
+
# Returns:
|
|
45
|
+
#
|
|
46
|
+
# The old field value.
|
|
47
|
+
def attribute_was(name)
|
|
48
|
+
change = modifications[name]
|
|
49
|
+
change ? change[0] : @attributes[name]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Gets the names of all the fields that have changed in the document.
|
|
53
|
+
#
|
|
54
|
+
# Example:
|
|
55
|
+
#
|
|
56
|
+
# person = Person.new(:title => "Sir")
|
|
57
|
+
# person.title = "Madam"
|
|
58
|
+
# person.changed # returns [ "title" ]
|
|
59
|
+
#
|
|
60
|
+
# Returns:
|
|
61
|
+
#
|
|
62
|
+
# An +Array+ of changed field names.
|
|
63
|
+
def changed
|
|
64
|
+
modifications.keys
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Alerts to whether the document has been modified or not.
|
|
68
|
+
#
|
|
69
|
+
# Example:
|
|
70
|
+
#
|
|
71
|
+
# person = Person.new(:title => "Sir")
|
|
72
|
+
# person.title = "Madam"
|
|
73
|
+
# person.changed? # returns true
|
|
74
|
+
#
|
|
75
|
+
# Returns:
|
|
76
|
+
#
|
|
77
|
+
# +true+ if changed, +false+ if not.
|
|
78
|
+
def changed?
|
|
79
|
+
!modifications.empty?
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Gets all the modifications that have happened to the object as a +Hash+
|
|
83
|
+
# with the keys being the names of the fields, and the values being an
|
|
84
|
+
# +Array+ with the old value and new value.
|
|
85
|
+
#
|
|
86
|
+
# Example:
|
|
87
|
+
#
|
|
88
|
+
# person = Person.new(:title => "Sir")
|
|
89
|
+
# person.title = "Madam"
|
|
90
|
+
# person.changes # returns { "title" => [ "Sir", "Madam" ] }
|
|
91
|
+
#
|
|
92
|
+
# Returns:
|
|
93
|
+
#
|
|
94
|
+
# A +Hash+ of changes.
|
|
95
|
+
def changes
|
|
96
|
+
modifications
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Call this method after save, so the changes can be properly switched.
|
|
100
|
+
#
|
|
101
|
+
# Example:
|
|
102
|
+
#
|
|
103
|
+
# <tt>person.move_changes</tt>
|
|
104
|
+
def move_changes
|
|
105
|
+
@previous_modifications = modifications.dup
|
|
106
|
+
@modifications = {}
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Gets all the new values for each of the changed fields, to be passed to
|
|
110
|
+
# a MongoDB $set modifier.
|
|
111
|
+
#
|
|
112
|
+
# Example:
|
|
113
|
+
#
|
|
114
|
+
# person = Person.new(:title => "Sir")
|
|
115
|
+
# person.title = "Madam"
|
|
116
|
+
# person.setters # returns { "title" => "Madam" }
|
|
117
|
+
#
|
|
118
|
+
# Returns:
|
|
119
|
+
#
|
|
120
|
+
# A +Hash+ of new values.
|
|
121
|
+
def setters
|
|
122
|
+
modifications.inject({}) do |sets, (field, changes)|
|
|
123
|
+
key = embedded? ? "#{_position}.#{field}" : field
|
|
124
|
+
sets[key] = changes[1]; sets
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Gets all the modifications that have happened to the object before the
|
|
129
|
+
# object was saved.
|
|
130
|
+
#
|
|
131
|
+
# Example:
|
|
132
|
+
#
|
|
133
|
+
# person = Person.new(:title => "Sir")
|
|
134
|
+
# person.title = "Madam"
|
|
135
|
+
# person.save!
|
|
136
|
+
# person.previous_changes # returns { "title" => [ "Sir", "Madam" ] }
|
|
137
|
+
#
|
|
138
|
+
# Returns:
|
|
139
|
+
#
|
|
140
|
+
# A +Hash+ of changes before save.
|
|
141
|
+
def previous_changes
|
|
142
|
+
@previous_modifications
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Resets a changed field back to its old value.
|
|
146
|
+
#
|
|
147
|
+
# Example:
|
|
148
|
+
#
|
|
149
|
+
# person = Person.new(:title => "Sir")
|
|
150
|
+
# person.title = "Madam"
|
|
151
|
+
# person.reset_attribute!("title")
|
|
152
|
+
# person.title # "Sir"
|
|
153
|
+
#
|
|
154
|
+
# Returns:
|
|
155
|
+
#
|
|
156
|
+
# The old field value.
|
|
157
|
+
def reset_attribute!(name)
|
|
158
|
+
value = attribute_was(name)
|
|
159
|
+
value ? @attributes[name] = value : @attributes.delete(name)
|
|
160
|
+
modifications.delete(name)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Sets up the modifications hash. This occurs just after the document is
|
|
164
|
+
# instantiated.
|
|
165
|
+
#
|
|
166
|
+
# Example:
|
|
167
|
+
#
|
|
168
|
+
# <tt>document.setup_notifications</tt>
|
|
169
|
+
def setup_modifications
|
|
170
|
+
@accessed ||= {}
|
|
171
|
+
@modifications ||= {}
|
|
172
|
+
@previous_modifications ||= {}
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Reset all modifications for the document. This will wipe all the marked
|
|
176
|
+
# changes, but not reset the values.
|
|
177
|
+
#
|
|
178
|
+
# Example:
|
|
179
|
+
#
|
|
180
|
+
# <tt>document.reset_modifications</tt>
|
|
181
|
+
def reset_modifications
|
|
182
|
+
@accessed = {}
|
|
183
|
+
@modifications = {}
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
protected
|
|
187
|
+
|
|
188
|
+
# Audit the original value for a field that can be modified in place.
|
|
189
|
+
#
|
|
190
|
+
# Example:
|
|
191
|
+
#
|
|
192
|
+
# <tt>person.accessed("aliases", [ "007" ])</tt>
|
|
193
|
+
def accessed(name, value)
|
|
194
|
+
@accessed ||= {}
|
|
195
|
+
@accessed[name] = value.dup if (value.is_a?(Array) || value.is_a?(Hash)) && !@accessed.has_key?(name)
|
|
196
|
+
value
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Get all normal modifications plus in place potential changes.
|
|
200
|
+
#
|
|
201
|
+
# Example:
|
|
202
|
+
#
|
|
203
|
+
# <tt>person.modifications</tt>
|
|
204
|
+
#
|
|
205
|
+
# Returns:
|
|
206
|
+
#
|
|
207
|
+
# All changes to the document.
|
|
208
|
+
def modifications
|
|
209
|
+
reset_modifications unless @modifications && @accessed
|
|
210
|
+
@accessed.each_pair do |field, value|
|
|
211
|
+
current = @attributes[field]
|
|
212
|
+
@modifications[field] = [ value, current ] if current != value
|
|
213
|
+
end
|
|
214
|
+
@accessed.clear
|
|
215
|
+
@modifications
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Audit the change of a field's value.
|
|
219
|
+
#
|
|
220
|
+
# Example:
|
|
221
|
+
#
|
|
222
|
+
# <tt>person.modify("name", "Jack", "John")</tt>
|
|
223
|
+
def modify(name, old_value, new_value)
|
|
224
|
+
@attributes[name] = new_value
|
|
225
|
+
if @modifications && (old_value != new_value)
|
|
226
|
+
original = @modifications[name].first if @modifications[name]
|
|
227
|
+
@modifications[name] = [ (original || old_value), new_value ]
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
module ClassMethods #:nodoc:
|
|
232
|
+
# Add the dynamic dirty methods. These are custom methods defined on a
|
|
233
|
+
# field by field basis that wrap the dirty attribute methods.
|
|
234
|
+
#
|
|
235
|
+
# Example:
|
|
236
|
+
#
|
|
237
|
+
# person = Person.new(:title => "Sir")
|
|
238
|
+
# person.title = "Madam"
|
|
239
|
+
# person.title_change # [ "Sir", "Madam" ]
|
|
240
|
+
# person.title_changed? # true
|
|
241
|
+
# person.title_was # "Sir"
|
|
242
|
+
# person.reset_title!
|
|
243
|
+
def add_dirty_methods(name)
|
|
244
|
+
define_method("#{name}_change") { attribute_change(name) } unless instance_methods.include?("#{name}_change")
|
|
245
|
+
define_method("#{name}_changed?") { attribute_changed?(name) } unless instance_methods.include?("#{name}_changed?")
|
|
246
|
+
define_method("#{name}_was") { attribute_was(name) } unless instance_methods.include?("#{name}_was")
|
|
247
|
+
define_method("reset_#{name}!") { reset_attribute!(name) } unless instance_methods.include?("reset_#{name}!")
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|