mongoid 2.0.1 → 2.0.2
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/Rakefile +4 -4
- data/lib/config/locales/{pt-br.yml → pt-BR.yml} +5 -5
- data/lib/config/locales/ru.yml +1 -1
- data/lib/config/locales/zh-CN.yml +2 -0
- data/lib/mongoid.rb +0 -1
- data/lib/mongoid/attributes.rb +9 -6
- data/lib/mongoid/collection.rb +21 -0
- data/lib/mongoid/config.rb +31 -8
- data/lib/mongoid/config/replset_database.rb +32 -2
- data/lib/mongoid/contexts.rb +0 -1
- data/lib/mongoid/contexts/enumerable.rb +73 -36
- data/lib/mongoid/contexts/mongo.rb +5 -12
- data/lib/mongoid/copyable.rb +2 -2
- data/lib/mongoid/criteria.rb +4 -23
- data/lib/mongoid/criterion/exclusion.rb +15 -0
- data/lib/mongoid/criterion/inclusion.rb +1 -1
- data/lib/mongoid/criterion/optional.rb +0 -1
- data/lib/mongoid/criterion/unconvertable.rb +20 -0
- data/lib/mongoid/cursor.rb +3 -3
- data/lib/mongoid/dirty.rb +8 -8
- data/lib/mongoid/document.rb +33 -36
- data/lib/mongoid/extensions.rb +7 -0
- data/lib/mongoid/extensions/object/checks.rb +32 -0
- data/lib/mongoid/extensions/object/conversions.rb +1 -1
- data/lib/mongoid/extensions/object_id/conversions.rb +6 -1
- data/lib/mongoid/extensions/range/conversions.rb +25 -0
- data/lib/mongoid/factory.rb +27 -10
- data/lib/mongoid/field.rb +50 -0
- data/lib/mongoid/fields.rb +42 -7
- data/lib/mongoid/finders.rb +5 -17
- data/lib/mongoid/identity.rb +1 -1
- data/lib/mongoid/inspection.rb +17 -21
- data/lib/mongoid/matchers.rb +6 -2
- data/lib/mongoid/matchers/strategies.rb +2 -2
- data/lib/mongoid/named_scope.rb +1 -1
- data/lib/mongoid/observer.rb +45 -14
- data/lib/mongoid/paranoia.rb +2 -2
- data/lib/mongoid/persistence.rb +2 -2
- data/lib/mongoid/persistence/update.rb +2 -1
- data/lib/mongoid/railtie.rb +3 -5
- data/lib/mongoid/relations.rb +1 -0
- data/lib/mongoid/relations/builders.rb +3 -3
- data/lib/mongoid/relations/builders/embedded/in.rb +1 -1
- data/lib/mongoid/relations/builders/embedded/many.rb +1 -1
- data/lib/mongoid/relations/builders/embedded/one.rb +1 -2
- data/lib/mongoid/relations/builders/referenced/in.rb +0 -3
- data/lib/mongoid/relations/builders/referenced/many.rb +21 -1
- data/lib/mongoid/relations/builders/referenced/one.rb +0 -4
- data/lib/mongoid/relations/embedded/many.rb +1 -17
- data/lib/mongoid/relations/macros.rb +3 -2
- data/lib/mongoid/relations/many.rb +2 -0
- data/lib/mongoid/relations/proxy.rb +1 -1
- data/lib/mongoid/relations/referenced/batch.rb +71 -0
- data/lib/mongoid/relations/referenced/batch/insert.rb +57 -0
- data/lib/mongoid/relations/referenced/many.rb +61 -2
- data/lib/mongoid/serialization.rb +1 -1
- data/lib/mongoid/validations/uniqueness.rb +1 -1
- data/lib/mongoid/version.rb +1 -1
- data/lib/rails/generators/mongoid/config/templates/mongoid.yml +8 -11
- metadata +22 -64
- data/lib/mongoid/contexts/paging.rb +0 -50
@@ -66,7 +66,12 @@ module Mongoid #:nodoc:
|
|
66
66
|
return args if args.is_a?(BSON::ObjectId) || !klass.using_object_ids?
|
67
67
|
case args
|
68
68
|
when ::String
|
69
|
-
|
69
|
+
return nil if args.blank?
|
70
|
+
if args.is_a?(Mongoid::Criterion::Unconvertable)
|
71
|
+
args
|
72
|
+
else
|
73
|
+
BSON::ObjectId.from_string(args)
|
74
|
+
end
|
70
75
|
when ::Array
|
71
76
|
args = args.reject(&:blank?) if reject_blank
|
72
77
|
args.map do |arg|
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
module Extensions #:nodoc:
|
4
|
+
module Range #:nodoc:
|
5
|
+
module Conversions #:nodoc:
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def to_hash
|
9
|
+
{ "min" => min, "max" => max }
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods #:nodoc:
|
13
|
+
|
14
|
+
def get(value)
|
15
|
+
value.nil? ? nil : ::Range.new(value["min"], value["max"])
|
16
|
+
end
|
17
|
+
|
18
|
+
def set(value)
|
19
|
+
value.nil? ? nil : value.to_hash
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/mongoid/factory.rb
CHANGED
@@ -1,20 +1,37 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Mongoid #:nodoc:
|
3
|
-
|
3
|
+
|
4
|
+
# Instantiates documents that came from the database.
|
5
|
+
module Factory
|
6
|
+
extend self
|
7
|
+
|
4
8
|
# Builds a new +Document+ from the supplied attributes.
|
5
9
|
#
|
6
|
-
#
|
10
|
+
# @example Build the document.
|
11
|
+
# Mongoid::Factory.build(Person, { "name" => "Durran" })
|
7
12
|
#
|
8
|
-
#
|
13
|
+
# @param [ Class ] klass The class to instantiate from if _type is not present.
|
14
|
+
# @param [ Hash ] attributes The document attributes.
|
9
15
|
#
|
10
|
-
#
|
16
|
+
# @return [ Document ] The instantiated document.
|
17
|
+
def build(klass, attributes = {})
|
18
|
+
type = (attributes || {})["_type"]
|
19
|
+
type.blank? ? klass.new(attributes) : type.constantize.new(attributes)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Builds a new +Document+ from the supplied attributes loaded from the
|
23
|
+
# database.
|
24
|
+
#
|
25
|
+
# @example Build the document.
|
26
|
+
# Mongoid::Factory.from_db(Person, { "name" => "Durran" })
|
27
|
+
#
|
28
|
+
# @param [ Class ] klass The class to instantiate from if _type is not present.
|
29
|
+
# @param [ Hash ] attributes The document attributes.
|
11
30
|
#
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
type = attrs["_type"]
|
17
|
-
type.present? ? type.constantize.instantiate(attrs) : klass.instantiate(attrs)
|
31
|
+
# @return [ Document ] The instantiated document.
|
32
|
+
def from_db(klass, attributes = {})
|
33
|
+
type = attributes["_type"]
|
34
|
+
type.blank? ? klass.instantiate(attributes) : type.constantize.instantiate(attributes)
|
18
35
|
end
|
19
36
|
end
|
20
37
|
end
|
data/lib/mongoid/field.rb
CHANGED
@@ -4,9 +4,59 @@ module Mongoid #:nodoc:
|
|
4
4
|
# Defines the behaviour for defined fields in the document.
|
5
5
|
class Field
|
6
6
|
|
7
|
+
NO_CAST_ON_READ = [
|
8
|
+
Array, Binary, Boolean, Float, Hash,
|
9
|
+
Integer, BSON::ObjectId, Set, String, Symbol
|
10
|
+
]
|
11
|
+
|
7
12
|
attr_accessor :type
|
8
13
|
attr_reader :copyable, :klass, :label, :name, :options
|
9
14
|
|
15
|
+
class << self
|
16
|
+
|
17
|
+
# Return a map of custom option names to their handlers.
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# Mongoid::Field.options
|
21
|
+
# # => { :required => #<Proc:0x00000100976b38> }
|
22
|
+
#
|
23
|
+
# @return [ Hash ] the option map
|
24
|
+
def options
|
25
|
+
@options ||= {}
|
26
|
+
end
|
27
|
+
|
28
|
+
# Stores the provided block to be run when the option name specified is
|
29
|
+
# defined on a field.
|
30
|
+
#
|
31
|
+
# No assumptions are made about what sort of work the handler might
|
32
|
+
# perform, so it will always be called if the `option_name` key is
|
33
|
+
# provided in the field definition -- even if it is false or nil.
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# Mongoid::Field.option :required do |model, field, value|
|
37
|
+
# model.validates_presence_of field if value
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# @param [ Symbol ] option_name the option name to match against
|
41
|
+
# @param [ Proc ] block the handler to execute when the option is
|
42
|
+
# provided.
|
43
|
+
def option(option_name, &block)
|
44
|
+
options[option_name] = block
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
# When reading the field do we need to cast the value? This holds true when
|
50
|
+
# times are stored or for big decimals which are stored as strings.
|
51
|
+
#
|
52
|
+
# @example Typecast on a read?
|
53
|
+
# field.cast_on_read?
|
54
|
+
#
|
55
|
+
# @return [ true, false ] If the field should be cast.
|
56
|
+
def cast_on_read?
|
57
|
+
!NO_CAST_ON_READ.include?(type)
|
58
|
+
end
|
59
|
+
|
10
60
|
# Get the default value for the field.
|
11
61
|
#
|
12
62
|
# @example Get the default.
|
data/lib/mongoid/fields.rb
CHANGED
@@ -32,6 +32,8 @@ module Mongoid #:nodoc
|
|
32
32
|
# @option options [ Class ] :type The type of the field.
|
33
33
|
# @option options [ String ] :label The label for the field.
|
34
34
|
# @option options [ Object, Proc ] :default The field's default
|
35
|
+
#
|
36
|
+
# @return [ Field ] The generated field
|
35
37
|
def field(name, options = {})
|
36
38
|
access = name.to_s
|
37
39
|
set_field(access, options)
|
@@ -101,9 +103,35 @@ module Mongoid #:nodoc
|
|
101
103
|
# @param [ Hash ] options The hash of options.
|
102
104
|
def set_field(name, options = {})
|
103
105
|
meth = options.delete(:as) || name
|
104
|
-
|
105
|
-
|
106
|
-
|
106
|
+
Field.new(name, options).tap do |field|
|
107
|
+
fields[name] = field
|
108
|
+
create_accessors(name, meth, options)
|
109
|
+
add_dirty_methods(name)
|
110
|
+
process_options(field)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Run through all custom options stored in Mongoid::Field.options and
|
115
|
+
# execute the handler if the option is provided.
|
116
|
+
#
|
117
|
+
# @example
|
118
|
+
# Mongoid::Field.option :custom do
|
119
|
+
# puts "called"
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# field = Mongoid::Field.new(:test, :custom => true)
|
123
|
+
# Person.process_options(field)
|
124
|
+
# # => "called"
|
125
|
+
#
|
126
|
+
# @param [ Field ] field the field to process
|
127
|
+
def process_options(field)
|
128
|
+
options = field.options
|
129
|
+
|
130
|
+
Field.options.each do |option_name, handler|
|
131
|
+
if options.has_key?(option_name)
|
132
|
+
handler.call(self, field, options[option_name])
|
133
|
+
end
|
134
|
+
end
|
107
135
|
end
|
108
136
|
|
109
137
|
# Create the field accessors.
|
@@ -118,13 +146,20 @@ module Mongoid #:nodoc
|
|
118
146
|
# @param [ Symbol ] meth The name of the accessor.
|
119
147
|
# @param [ Hash ] options The options.
|
120
148
|
def create_accessors(name, meth, options = {})
|
149
|
+
field = fields[name]
|
121
150
|
generated_field_methods.module_eval do
|
122
|
-
if
|
123
|
-
define_method(meth)
|
151
|
+
if field.cast_on_read?
|
152
|
+
define_method(meth) do
|
153
|
+
field.get(read_attribute(name))
|
154
|
+
end
|
124
155
|
else
|
125
|
-
define_method(meth)
|
156
|
+
define_method(meth) do
|
157
|
+
read_attribute(name)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
define_method("#{meth}=") do |value|
|
161
|
+
write_attribute(name, value)
|
126
162
|
end
|
127
|
-
define_method("#{meth}=") { |value| write_attribute(name, value) }
|
128
163
|
define_method("#{meth}?") do
|
129
164
|
attr = read_attribute(name)
|
130
165
|
(options[:type] == Boolean) ? attr == true : attr.present?
|
data/lib/mongoid/finders.rb
CHANGED
@@ -34,6 +34,11 @@ module Mongoid #:nodoc:
|
|
34
34
|
find(:all, *args).count
|
35
35
|
end
|
36
36
|
|
37
|
+
# Returns true if count is zero
|
38
|
+
def empty?
|
39
|
+
count == 0
|
40
|
+
end
|
41
|
+
|
37
42
|
# Returns true if there are on document in database based on the
|
38
43
|
# provided arguments.
|
39
44
|
#
|
@@ -113,23 +118,6 @@ module Mongoid #:nodoc:
|
|
113
118
|
find(:last, *args)
|
114
119
|
end
|
115
120
|
|
116
|
-
# Find all documents in paginated fashion given the supplied arguments.
|
117
|
-
# If no parameters are passed just default to offset 0 and limit 20.
|
118
|
-
#
|
119
|
-
# Options:
|
120
|
-
#
|
121
|
-
# params: A +Hash+ of params to pass to the Criteria API.
|
122
|
-
#
|
123
|
-
# Example:
|
124
|
-
#
|
125
|
-
# <tt>Person.paginate(:conditions => { :field => "Test" }, :page => 1,
|
126
|
-
# :per_page => 20)</tt>
|
127
|
-
#
|
128
|
-
# Returns paginated array of docs.
|
129
|
-
def paginate(params = {})
|
130
|
-
find(:all, params).paginate
|
131
|
-
end
|
132
|
-
|
133
121
|
protected
|
134
122
|
# Find the first object or create/initialize it.
|
135
123
|
def find_or(method, attrs = {}, &block)
|
data/lib/mongoid/identity.rb
CHANGED
data/lib/mongoid/inspection.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Mongoid #:nodoc
|
3
|
-
|
3
|
+
|
4
|
+
# Contains the bahviour around inspecting documents via
|
5
|
+
# <tt>Object#inspect</tt>.
|
6
|
+
module Inspection
|
4
7
|
|
5
8
|
# Returns the class name plus its attributes. If using dynamic fields will
|
6
9
|
# include those as well.
|
7
10
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# <tt>person.inspect</tt>
|
11
|
-
#
|
12
|
-
# Returns:
|
11
|
+
# @example Inspect the document.
|
12
|
+
# person.inspect
|
13
13
|
#
|
14
|
-
# A nice pretty string to look at.
|
14
|
+
# @return [ String ] A nice pretty string to look at.
|
15
15
|
def inspect
|
16
16
|
inspection = []
|
17
17
|
inspection.concat(inspect_fields).concat(inspect_dynamic_fields)
|
@@ -22,28 +22,24 @@ module Mongoid #:nodoc
|
|
22
22
|
|
23
23
|
# Get an array of inspected fields for the document.
|
24
24
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# <tt>inspect_fields</tt>
|
28
|
-
#
|
29
|
-
# Returns:
|
25
|
+
# @example Inspect the defined fields.
|
26
|
+
# document.inspect_fields
|
30
27
|
#
|
31
|
-
# An array of pretty printed field values.
|
28
|
+
# @return [ String ] An array of pretty printed field values.
|
32
29
|
def inspect_fields
|
33
30
|
fields.map do |name, field|
|
34
|
-
|
35
|
-
|
31
|
+
unless name == "_id"
|
32
|
+
"#{name}: #{@attributes[name].inspect}"
|
33
|
+
end
|
34
|
+
end.compact
|
36
35
|
end
|
37
36
|
|
38
37
|
# Get an array of inspected dynamic fields for the document.
|
39
38
|
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
# <tt>inspect_dynamic_fields</tt>
|
43
|
-
#
|
44
|
-
# Returns:
|
39
|
+
# @example Inspect the dynamic fields.
|
40
|
+
# document.inspect_dynamic_fields
|
45
41
|
#
|
46
|
-
# An array of pretty printed dynamic field values.
|
42
|
+
# @return [ String ] An array of pretty printed dynamic field values.
|
47
43
|
def inspect_dynamic_fields
|
48
44
|
if Mongoid.allow_dynamic_fields
|
49
45
|
keys = @attributes.keys - fields.keys - relations.keys - ["_id", "_type"]
|
data/lib/mongoid/matchers.rb
CHANGED
@@ -18,8 +18,12 @@ module Mongoid #:nodoc:
|
|
18
18
|
# @return [ true, false ] True if matches, false if not.
|
19
19
|
def matches?(selector)
|
20
20
|
selector.each_pair do |key, value|
|
21
|
-
|
22
|
-
|
21
|
+
if value.is_a?(Hash)
|
22
|
+
value.each do |item|
|
23
|
+
return false unless Strategies.matcher(self, key, Hash[*item]).matches?(Hash[*item])
|
24
|
+
end
|
25
|
+
else
|
26
|
+
return false unless Strategies.matcher(self, key, value).matches?(value)
|
23
27
|
end
|
24
28
|
end
|
25
29
|
return true
|
@@ -49,12 +49,12 @@ module Mongoid #:nodoc:
|
|
49
49
|
# @since 2.0.0.rc.7
|
50
50
|
def matcher(document, key, value)
|
51
51
|
if value.is_a?(Hash)
|
52
|
-
MATCHERS[value.keys.first].new(document.attributes[key])
|
52
|
+
MATCHERS[value.keys.first].new(document.attributes[key.to_s])
|
53
53
|
else
|
54
54
|
if key == "$or"
|
55
55
|
Matchers::Or.new(value, document)
|
56
56
|
else
|
57
|
-
Default.new(document.attributes[key])
|
57
|
+
Default.new(document.attributes[key.to_s])
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
data/lib/mongoid/named_scope.rb
CHANGED
@@ -129,7 +129,7 @@ module Mongoid #:nodoc:
|
|
129
129
|
def valid_scope_name?(name)
|
130
130
|
if !scopes[name] && respond_to?(name, true)
|
131
131
|
Mongoid.logger.warn "Creating scope :#{name}. " \
|
132
|
-
"Overwriting existing method #{self.name}.#{name}."
|
132
|
+
"Overwriting existing method #{self.name}.#{name}." if Mongoid.logger
|
133
133
|
end
|
134
134
|
end
|
135
135
|
end
|
data/lib/mongoid/observer.rb
CHANGED
@@ -1,34 +1,65 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Mongoid #:nodoc:
|
3
|
+
|
4
|
+
# Mongoid observers hook into the lifecycle of documents.
|
3
5
|
class Observer < ActiveModel::Observer
|
6
|
+
|
7
|
+
# Instantiate the new observer. Will add all child observers as well.
|
8
|
+
#
|
9
|
+
# @example Instantiate the observer.
|
10
|
+
# Mongoid::Observer.new
|
11
|
+
#
|
12
|
+
# @since 2.0.0
|
4
13
|
def initialize
|
5
|
-
super
|
6
|
-
observed_descendants.each { |klass| add_observer!(klass) }
|
14
|
+
super and observed_descendants.each { |klass| add_observer!(klass) }
|
7
15
|
end
|
8
16
|
|
9
17
|
protected
|
10
18
|
|
19
|
+
# Get all the child observers.
|
20
|
+
#
|
21
|
+
# @example Get the children.
|
22
|
+
# observer.observed_descendants
|
23
|
+
#
|
24
|
+
# @return [ Array<Class> ] The children.
|
25
|
+
#
|
26
|
+
# @since 2.0.0
|
11
27
|
def observed_descendants
|
12
28
|
observed_classes.sum([]) { |klass| klass.descendants }
|
13
29
|
end
|
14
30
|
|
31
|
+
# Adds the specified observer to the class.
|
32
|
+
#
|
33
|
+
# @example Add the observer.
|
34
|
+
# observer.add_observer!(Document)
|
35
|
+
#
|
36
|
+
# @param [ Class ] klass The child observer to add.
|
37
|
+
#
|
38
|
+
# @since 2.0.0
|
15
39
|
def add_observer!(klass)
|
16
|
-
super
|
17
|
-
define_callbacks klass
|
40
|
+
super and define_callbacks(klass)
|
18
41
|
end
|
19
42
|
|
43
|
+
# Defines all the callbacks for each observer of the model.
|
44
|
+
#
|
45
|
+
# @example Define all the callbacks.
|
46
|
+
# observer.define_callbacks(Document)
|
47
|
+
#
|
48
|
+
# @param [ Class ] klass The model to define them on.
|
49
|
+
#
|
50
|
+
# @since 2.0.0
|
20
51
|
def define_callbacks(klass)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
52
|
+
tap do |observer|
|
53
|
+
observer_name = observer.class.name.underscore.gsub('/', '__')
|
54
|
+
Mongoid::Callbacks::CALLBACKS.each do |callback|
|
55
|
+
next unless respond_to?(callback)
|
56
|
+
callback_meth = :"_notify_#{observer_name}_for_#{callback}"
|
57
|
+
unless klass.respond_to?(callback_meth)
|
58
|
+
klass.send(:define_method, callback_meth) do |&block|
|
59
|
+
observer.send(callback, self, &block)
|
60
|
+
end
|
61
|
+
klass.send(callback, callback_meth)
|
30
62
|
end
|
31
|
-
klass.send(callback, callback_meth)
|
32
63
|
end
|
33
64
|
end
|
34
65
|
end
|