mongoid 2.0.0.beta.15 → 2.0.0.beta.16
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/config/locales/en.yml +40 -0
- data/lib/config/locales/es.yml +41 -0
- data/lib/config/locales/fr.yml +42 -0
- data/lib/config/locales/it.yml +39 -0
- data/lib/config/locales/pl.yml +39 -0
- data/lib/config/locales/pt.yml +40 -0
- data/lib/config/locales/sv.yml +40 -0
- data/lib/mongoid.rb +7 -2
- data/lib/mongoid/associations.rb +16 -9
- data/lib/mongoid/associations/embedded_in.rb +11 -0
- data/lib/mongoid/associations/embeds_many.rb +28 -2
- data/lib/mongoid/associations/embeds_one.rb +18 -2
- data/lib/mongoid/associations/proxy.rb +28 -1
- data/lib/mongoid/associations/referenced_in.rb +10 -0
- data/lib/mongoid/associations/references_many.rb +10 -7
- data/lib/mongoid/associations/references_many_as_array.rb +29 -0
- data/lib/mongoid/associations/references_one.rb +9 -1
- data/lib/mongoid/attributes.rb +13 -3
- data/lib/mongoid/callbacks.rb +1 -0
- data/lib/mongoid/collections.rb +1 -1
- data/lib/mongoid/components.rb +1 -0
- data/lib/mongoid/config.rb +16 -1
- data/lib/mongoid/contexts/enumerable.rb +28 -1
- data/lib/mongoid/contexts/enumerable/sort.rb +43 -0
- data/lib/mongoid/contexts/mongo.rb +13 -1
- data/lib/mongoid/criteria.rb +4 -2
- data/lib/mongoid/criterion/inclusion.rb +22 -1
- data/lib/mongoid/criterion/optional.rb +14 -39
- data/lib/mongoid/criterion/selector.rb +65 -0
- data/lib/mongoid/document.rb +5 -11
- data/lib/mongoid/errors.rb +10 -130
- 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/unsupported_version.rb +21 -0
- data/lib/mongoid/errors/validations.rb +22 -0
- data/lib/mongoid/extensions/hash/assimilation.rb +1 -1
- data/lib/mongoid/extensions/hash/criteria_helpers.rb +5 -3
- data/lib/mongoid/extensions/object/conversions.rb +5 -1
- data/lib/mongoid/extensions/objectid/conversions.rb +43 -1
- data/lib/mongoid/field.rb +13 -6
- data/lib/mongoid/finders.rb +1 -1
- data/lib/mongoid/hierarchy.rb +9 -4
- data/lib/mongoid/identity.rb +2 -2
- data/lib/mongoid/indexes.rb +1 -1
- data/lib/mongoid/matchers/default.rb +1 -1
- data/lib/mongoid/modifiers.rb +24 -0
- data/lib/mongoid/modifiers/command.rb +18 -0
- data/lib/mongoid/modifiers/inc.rb +24 -0
- data/lib/mongoid/persistence/command.rb +2 -10
- data/lib/mongoid/persistence/remove_all.rb +1 -1
- data/lib/mongoid/railtie.rb +2 -0
- data/lib/mongoid/railties/database.rake +102 -11
- data/lib/mongoid/railties/document.rb +12 -0
- data/lib/mongoid/safe.rb +13 -0
- data/lib/mongoid/safety.rb +12 -0
- data/lib/mongoid/validations.rb +0 -4
- data/lib/mongoid/version.rb +1 -1
- data/lib/mongoid/versioning.rb +11 -1
- metadata +55 -28
- data/lib/mongoid/validations/locale/en.yml +0 -5
@@ -58,9 +58,14 @@ module Mongoid #:nodoc:
|
|
58
58
|
#
|
59
59
|
# A new target document.
|
60
60
|
def nested_build(attributes, options = nil)
|
61
|
-
|
61
|
+
options ||= {}
|
62
|
+
_destroy = Boolean.set(attributes.delete('_destroy'))
|
63
|
+
if options[:allow_destroy] && _destroy
|
64
|
+
@target = nil
|
65
|
+
elsif @target.present? || !options[:update_only]
|
62
66
|
@target.write_attributes(attributes)
|
63
|
-
end
|
67
|
+
end
|
68
|
+
target
|
64
69
|
end
|
65
70
|
|
66
71
|
class << self
|
@@ -89,6 +94,17 @@ module Mongoid #:nodoc:
|
|
89
94
|
child.assimilate(parent, options)
|
90
95
|
new(parent, options, child.is_a?(Hash) ? nil : child)
|
91
96
|
end
|
97
|
+
|
98
|
+
# Validate the options passed to the embeds one macro, to encapsulate
|
99
|
+
# the behavior in this class instead of the associations module.
|
100
|
+
#
|
101
|
+
# Options:
|
102
|
+
#
|
103
|
+
# options: Thank you captain obvious.
|
104
|
+
def validate_options(options = {})
|
105
|
+
check_dependent_not_allowed!(options)
|
106
|
+
check_inverse_not_allowed!(options)
|
107
|
+
end
|
92
108
|
end
|
93
109
|
end
|
94
110
|
end
|
@@ -3,7 +3,7 @@ module Mongoid #:nodoc
|
|
3
3
|
module Associations #:nodoc
|
4
4
|
class Proxy #:nodoc
|
5
5
|
instance_methods.each do |method|
|
6
|
-
undef_method(method) unless method =~ /(^__|^
|
6
|
+
undef_method(method) unless method =~ /(^__|^send$|^object_id$|^extend$)/
|
7
7
|
end
|
8
8
|
attr_reader \
|
9
9
|
:options,
|
@@ -28,6 +28,33 @@ module Mongoid #:nodoc
|
|
28
28
|
@foreign_key = options.foreign_key
|
29
29
|
extends(options)
|
30
30
|
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
class << self
|
34
|
+
def check_dependent_not_allowed!(options)
|
35
|
+
if options.has_key?(:dependent)
|
36
|
+
raise Errors::InvalidOptions.new(
|
37
|
+
"dependent_only_references_one_or_many", {}
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def check_inverse_not_allowed!(options)
|
43
|
+
if options.has_key?(:inverse_of)
|
44
|
+
raise Errors::InvalidOptions.new(
|
45
|
+
"association_cant_have_inverse_of", {}
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def check_inverse_must_be_defined!(options)
|
51
|
+
unless options.has_key?(:inverse_of)
|
52
|
+
raise Errors::InvalidOptions.new(
|
53
|
+
"embedded_in_must_have_inverse_of", {}
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
31
58
|
end
|
32
59
|
end
|
33
60
|
end
|
@@ -54,6 +54,16 @@ module Mongoid #:nodoc:
|
|
54
54
|
document.send("#{options.foreign_key}=", target ? target.id : nil)
|
55
55
|
new(document, options, target)
|
56
56
|
end
|
57
|
+
|
58
|
+
# Validate the options passed to the referenced in macro, to encapsulate
|
59
|
+
# the behavior in this class instead of the associations module.
|
60
|
+
#
|
61
|
+
# Options:
|
62
|
+
#
|
63
|
+
# options: Thank you captain obvious.
|
64
|
+
def validate_options(options = {})
|
65
|
+
check_dependent_not_allowed!(options)
|
66
|
+
end
|
57
67
|
end
|
58
68
|
end
|
59
69
|
end
|
@@ -227,19 +227,22 @@ module Mongoid #:nodoc:
|
|
227
227
|
protected
|
228
228
|
def determine_name(document, options)
|
229
229
|
target = document.class
|
230
|
-
|
231
230
|
if (inverse = options.inverse_of) && inverse.is_a?(Array)
|
232
231
|
inverse = [*inverse].detect { |name| target.respond_to?(name) }
|
233
232
|
end
|
234
|
-
|
235
233
|
if !inverse
|
236
|
-
association = options
|
237
|
-
|
238
|
-
|
239
|
-
inverse = association.name if association
|
234
|
+
association = detect_association(target, options, false)
|
235
|
+
association = detect_association(target, options, true) if association.blank?
|
236
|
+
inferred = association.name if association
|
240
237
|
end
|
238
|
+
inverse || inferred || target.to_s.underscore
|
239
|
+
end
|
241
240
|
|
242
|
-
|
241
|
+
def detect_association(target, options, with_class_name = false)
|
242
|
+
association = options.klass.associations.values.detect do |metadata|
|
243
|
+
metadata.options.klass == target &&
|
244
|
+
(with_class_name ? true : metadata.options[:class_name].nil?)
|
245
|
+
end
|
243
246
|
end
|
244
247
|
end
|
245
248
|
end
|
@@ -34,6 +34,7 @@ module Mongoid #:nodoc:
|
|
34
34
|
@target << object
|
35
35
|
object.save unless @parent.new_record?
|
36
36
|
end
|
37
|
+
@parent.save unless @parent.new_record?
|
37
38
|
end
|
38
39
|
|
39
40
|
alias :concat :<<
|
@@ -50,6 +51,34 @@ module Mongoid #:nodoc:
|
|
50
51
|
push(document); document
|
51
52
|
end
|
52
53
|
|
54
|
+
# Destroy all the associated objects.
|
55
|
+
#
|
56
|
+
# Example:
|
57
|
+
#
|
58
|
+
# <tt>person.posts.destroy_all</tt>
|
59
|
+
#
|
60
|
+
# Returns:
|
61
|
+
#
|
62
|
+
# The number of objects destroyed.
|
63
|
+
def destroy_all(conditions = {})
|
64
|
+
removed = query.call.destroy_all(:conditions => conditions)
|
65
|
+
reset; removed
|
66
|
+
end
|
67
|
+
|
68
|
+
# Delete all the associated objects.
|
69
|
+
#
|
70
|
+
# Example:
|
71
|
+
#
|
72
|
+
# <tt>person.posts.delete_all</tt>
|
73
|
+
#
|
74
|
+
# Returns:
|
75
|
+
#
|
76
|
+
# The number of objects deleted.
|
77
|
+
def delete_all(conditions = {})
|
78
|
+
removed = query.call.delete_all(:conditions => conditions)
|
79
|
+
reset; removed
|
80
|
+
end
|
81
|
+
|
53
82
|
protected
|
54
83
|
|
55
84
|
# Find the inverse key for the supplied document.
|
@@ -61,7 +61,15 @@ module Mongoid #:nodoc:
|
|
61
61
|
#
|
62
62
|
# A new target document.
|
63
63
|
def nested_build(attributes, options = nil)
|
64
|
-
|
64
|
+
options ||= {}
|
65
|
+
_destroy = Boolean.set(attributes.delete('_destroy'))
|
66
|
+
if options[:allow_destroy] && _destroy
|
67
|
+
@target.destroy
|
68
|
+
@target = nil
|
69
|
+
elsif @target.present? || !options[:update_only]
|
70
|
+
build(attributes)
|
71
|
+
end
|
72
|
+
@target
|
65
73
|
end
|
66
74
|
|
67
75
|
class << self
|
data/lib/mongoid/attributes.rb
CHANGED
@@ -29,6 +29,11 @@ module Mongoid #:nodoc:
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
# Override respond_to? so it responds properly for dynamic attributes
|
33
|
+
def respond_to?(sym)
|
34
|
+
(Mongoid.allow_dynamic_fields && @attributes && @attributes.has_key?(sym.to_s)) || super
|
35
|
+
end
|
36
|
+
|
32
37
|
# Process the provided attributes casting them to their proper values if a
|
33
38
|
# field exists for them on the +Document+. This will be limited to only the
|
34
39
|
# attributes provided in the suppied +Hash+ so that no extra nil values get
|
@@ -123,8 +128,7 @@ module Mongoid #:nodoc:
|
|
123
128
|
# there is any.
|
124
129
|
def write_attribute(name, value)
|
125
130
|
access = name.to_s
|
126
|
-
|
127
|
-
modify(access, @attributes[access], typed_value)
|
131
|
+
modify(access, @attributes[access], typed_value_for(access, value))
|
128
132
|
notify if !id.blank? && new_record?
|
129
133
|
end
|
130
134
|
|
@@ -152,11 +156,17 @@ module Mongoid #:nodoc:
|
|
152
156
|
alias :attributes= :write_attributes
|
153
157
|
|
154
158
|
protected
|
159
|
+
|
160
|
+
# Return the typecast value for a field.
|
161
|
+
def typed_value_for(key, value)
|
162
|
+
fields.has_key?(key) ? fields[key].set(value) : value
|
163
|
+
end
|
164
|
+
|
155
165
|
# apply default values to attributes - calling procs as required
|
156
166
|
def default_attributes
|
157
167
|
default_values = defaults
|
158
168
|
default_values.each_pair do |key, val|
|
159
|
-
default_values[key] = val.call if val.respond_to?(:call)
|
169
|
+
default_values[key] = typed_value_for(key, val.call) if val.respond_to?(:call)
|
160
170
|
end
|
161
171
|
default_values || {}
|
162
172
|
end
|
data/lib/mongoid/callbacks.rb
CHANGED
data/lib/mongoid/collections.rb
CHANGED
data/lib/mongoid/components.rb
CHANGED
data/lib/mongoid/config.rb
CHANGED
@@ -169,6 +169,21 @@ module Mongoid #:nodoc
|
|
169
169
|
end
|
170
170
|
end
|
171
171
|
|
172
|
+
# Adds a new I18n locale file to the load path
|
173
|
+
#
|
174
|
+
# Example:
|
175
|
+
#
|
176
|
+
# Add portuguese locale
|
177
|
+
# <tt>Mongoid::config.add_language('pt')</tt>
|
178
|
+
#
|
179
|
+
# Adds all available languages
|
180
|
+
# <tt>Mongoid::Config.add_language('*')</tt>
|
181
|
+
def add_language(language_code = nil)
|
182
|
+
Dir[File.join(File.dirname(__FILE__), "..", "config", "locales", "#{language_code}.yml")].each do |file|
|
183
|
+
I18n.load_path << File.expand_path(file)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
172
187
|
# Convenience method for connecting to the master database after forking a
|
173
188
|
# new process.
|
174
189
|
#
|
@@ -177,7 +192,7 @@ module Mongoid #:nodoc
|
|
177
192
|
# <tt>Mongoid.reconnect!</tt>
|
178
193
|
def reconnect!(now = true)
|
179
194
|
if now
|
180
|
-
master.connection.
|
195
|
+
master.connection.connect
|
181
196
|
else
|
182
197
|
# We set a @reconnect flag so that #master knows to reconnect the next
|
183
198
|
# time the connection is accessed.
|
@@ -1,4 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'mongoid/contexts/enumerable/sort'
|
4
|
+
|
2
5
|
module Mongoid #:nodoc:
|
3
6
|
module Contexts #:nodoc:
|
4
7
|
class Enumerable
|
@@ -56,7 +59,7 @@ module Mongoid #:nodoc:
|
|
56
59
|
#
|
57
60
|
# An +Array+ of documents that matched the selector.
|
58
61
|
def execute(paginating = false)
|
59
|
-
limit(filter) || []
|
62
|
+
limit(sort(filter)) || []
|
60
63
|
end
|
61
64
|
|
62
65
|
# Groups the documents by the first field supplied in the field options.
|
@@ -115,6 +118,18 @@ module Mongoid #:nodoc:
|
|
115
118
|
# The first document in the +Array+
|
116
119
|
alias :one :first
|
117
120
|
|
121
|
+
# Get one document and tell the criteria to skip this record on
|
122
|
+
# successive calls.
|
123
|
+
#
|
124
|
+
# Returns:
|
125
|
+
#
|
126
|
+
# The first document in the +Array+
|
127
|
+
def shift
|
128
|
+
document = first
|
129
|
+
criteria.skip((options[:skip] || 0) + 1)
|
130
|
+
document
|
131
|
+
end
|
132
|
+
|
118
133
|
# Get the sum of the field values for all the documents.
|
119
134
|
#
|
120
135
|
# Returns:
|
@@ -148,9 +163,21 @@ module Mongoid #:nodoc:
|
|
148
163
|
return documents.slice(skip, limit)
|
149
164
|
elsif limit
|
150
165
|
return documents.first(limit)
|
166
|
+
elsif skip
|
167
|
+
return documents.slice(skip..-1)
|
151
168
|
end
|
152
169
|
documents
|
153
170
|
end
|
171
|
+
|
172
|
+
# Sorts the result set if sort options have been set.
|
173
|
+
def sort(documents)
|
174
|
+
return documents if options[:sort].blank?
|
175
|
+
documents.sort_by do |document|
|
176
|
+
options[:sort].map do |key, direction|
|
177
|
+
Sort.new(document.read_attribute(key), direction)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
154
181
|
end
|
155
182
|
end
|
156
183
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
module Contexts #:nodoc:
|
4
|
+
class Enumerable
|
5
|
+
class Sort
|
6
|
+
attr_reader :value, :direction
|
7
|
+
|
8
|
+
# Create a new sorting object. This requires a value and a sort
|
9
|
+
# direction of +:asc+ or +:desc+.
|
10
|
+
def initialize(value, direction)
|
11
|
+
@value = value
|
12
|
+
@direction = direction
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return +true+ if the direction is +:asc+, otherwise false.
|
16
|
+
def ascending?
|
17
|
+
direction == :asc
|
18
|
+
end
|
19
|
+
|
20
|
+
# Compare two +Sort+ objects against each other, taking into
|
21
|
+
# consideration the direction of the sorting.
|
22
|
+
def <=>(other)
|
23
|
+
cmp = compare(value, other.value)
|
24
|
+
ascending? ? cmp : cmp * -1
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# Compare two values allowing for nil values.
|
30
|
+
def compare(a, b)
|
31
|
+
case
|
32
|
+
when a.nil?
|
33
|
+
b.nil? ? 0 : 1
|
34
|
+
when b.nil?
|
35
|
+
-1
|
36
|
+
else
|
37
|
+
a <=> b
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -133,7 +133,7 @@ module Mongoid #:nodoc:
|
|
133
133
|
# <tt>Mongoid::Contexts::Mongo.new(criteria)</tt>
|
134
134
|
def initialize(criteria)
|
135
135
|
@criteria = criteria
|
136
|
-
if klass.hereditary && !criteria.selector.keys.include?(:_type)
|
136
|
+
if klass.hereditary? && !criteria.selector.keys.include?(:_type)
|
137
137
|
criteria.in(:_type => criteria.klass._types)
|
138
138
|
end
|
139
139
|
criteria.enslave if klass.enslaved?
|
@@ -225,6 +225,18 @@ module Mongoid #:nodoc:
|
|
225
225
|
|
226
226
|
alias :first :one
|
227
227
|
|
228
|
+
# Return the first result for the +Context+ and skip it
|
229
|
+
# for successive calls.
|
230
|
+
#
|
231
|
+
# Returns:
|
232
|
+
#
|
233
|
+
# The first document in the collection.
|
234
|
+
def shift
|
235
|
+
document = first
|
236
|
+
criteria.skip((options[:skip] || 0) + 1)
|
237
|
+
document
|
238
|
+
end
|
239
|
+
|
228
240
|
# Sum the context.
|
229
241
|
#
|
230
242
|
# This will take the internally built selector and options
|
data/lib/mongoid/criteria.rb
CHANGED
@@ -3,6 +3,7 @@ require "mongoid/criterion/complex"
|
|
3
3
|
require "mongoid/criterion/exclusion"
|
4
4
|
require "mongoid/criterion/inclusion"
|
5
5
|
require "mongoid/criterion/optional"
|
6
|
+
require "mongoid/criterion/selector"
|
6
7
|
|
7
8
|
module Mongoid #:nodoc:
|
8
9
|
# The +Criteria+ class is the core object needed in Mongoid to retrieve
|
@@ -30,7 +31,7 @@ module Mongoid #:nodoc:
|
|
30
31
|
|
31
32
|
delegate :aggregate, :avg, :blank?, :count, :distinct, :empty?,
|
32
33
|
:execute, :first, :group, :id_criteria, :last, :max,
|
33
|
-
:min, :one, :page, :paginate, :per_page, :sum, :to => :context
|
34
|
+
:min, :one, :page, :paginate, :per_page, :shift, :sum, :to => :context
|
34
35
|
|
35
36
|
# Concatinate the criteria with another enumerable. If the other is a
|
36
37
|
# +Criteria+ then it needs to get the collection from it.
|
@@ -116,7 +117,8 @@ module Mongoid #:nodoc:
|
|
116
117
|
# type: One of :all, :first:, or :last
|
117
118
|
# klass: The class to execute on.
|
118
119
|
def initialize(klass)
|
119
|
-
@selector
|
120
|
+
@selector = Mongoid::Criterion::Selector.new(klass)
|
121
|
+
@options, @klass, @documents = {}, klass, []
|
120
122
|
end
|
121
123
|
|
122
124
|
# Merges another object into this +Criteria+. The other object may be a
|
@@ -43,6 +43,27 @@ module Mongoid #:nodoc:
|
|
43
43
|
where(selector)
|
44
44
|
end
|
45
45
|
|
46
|
+
# Adds a criterion to the +Criteria+ that specifies a set of expressions
|
47
|
+
# to match if any of them return true. This is a $or query in MongoDB and
|
48
|
+
# is similar to a SQL OR. This is named #any_of and aliased "or" for
|
49
|
+
# readability.
|
50
|
+
#
|
51
|
+
# Options:
|
52
|
+
#
|
53
|
+
# selector: Multiple +Hash+ expressions that any can match.
|
54
|
+
#
|
55
|
+
# Example:
|
56
|
+
#
|
57
|
+
# <tt>criteria.any_of({ :field1 => "value" }, { :field2 => "value2" })</tt>
|
58
|
+
#
|
59
|
+
# Returns: <tt>self</tt>
|
60
|
+
def any_of(*args)
|
61
|
+
criterion = @selector["$or"] || []
|
62
|
+
@selector["$or"] = criterion.concat(args)
|
63
|
+
self
|
64
|
+
end
|
65
|
+
alias :or :any_of
|
66
|
+
|
46
67
|
# Adds a criterion to the +Criteria+ that specifies values where any can
|
47
68
|
# be matched in order to return results. This is similar to an SQL "IN"
|
48
69
|
# clause. The MongoDB conditional operator that will be used is "$in".
|
@@ -89,7 +110,7 @@ module Mongoid #:nodoc:
|
|
89
110
|
#
|
90
111
|
# Options:
|
91
112
|
#
|
92
|
-
#
|
113
|
+
# selector: A +Hash+ that must match the attributes of the +Document+.
|
93
114
|
#
|
94
115
|
# Example:
|
95
116
|
#
|