mongoid 2.0.1 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|