mongoid 2.0.0.rc.7 → 2.0.0.rc.8

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 (90) hide show
  1. data/lib/config/locales/en.yml +3 -0
  2. data/lib/config/locales/id.yml +46 -0
  3. data/lib/config/locales/ja.yml +40 -0
  4. data/lib/config/locales/vi.yml +45 -0
  5. data/lib/mongoid.rb +5 -3
  6. data/lib/mongoid/attributes.rb +24 -63
  7. data/lib/mongoid/attributes/processing.rb +5 -2
  8. data/lib/mongoid/callbacks.rb +10 -0
  9. data/lib/mongoid/collection.rb +24 -0
  10. data/lib/mongoid/collections/master.rb +14 -6
  11. data/lib/mongoid/collections/operations.rb +1 -1
  12. data/lib/mongoid/collections/retry.rb +39 -0
  13. data/lib/mongoid/collections/slaves.rb +26 -10
  14. data/lib/mongoid/components.rb +4 -4
  15. data/lib/mongoid/config.rb +6 -3
  16. data/lib/mongoid/contexts.rb +0 -1
  17. data/lib/mongoid/contexts/enumerable.rb +19 -7
  18. data/lib/mongoid/contexts/mongo.rb +9 -5
  19. data/lib/mongoid/copyable.rb +10 -8
  20. data/lib/mongoid/criteria.rb +83 -61
  21. data/lib/mongoid/criterion/builder.rb +34 -0
  22. data/lib/mongoid/criterion/creational.rb +2 -2
  23. data/lib/mongoid/criterion/exclusion.rb +58 -32
  24. data/lib/mongoid/criterion/inclusion.rb +49 -10
  25. data/lib/mongoid/criterion/optional.rb +1 -1
  26. data/lib/mongoid/criterion/selector.rb +80 -11
  27. data/lib/mongoid/cursor.rb +6 -1
  28. data/lib/mongoid/default_scope.rb +27 -19
  29. data/lib/mongoid/document.rb +26 -1
  30. data/lib/mongoid/errors.rb +1 -0
  31. data/lib/mongoid/errors/mixed_relations.rb +37 -0
  32. data/lib/mongoid/extensions/object_id/conversions.rb +7 -4
  33. data/lib/mongoid/factory.rb +1 -1
  34. data/lib/mongoid/field.rb +47 -30
  35. data/lib/mongoid/fields.rb +9 -2
  36. data/lib/mongoid/finders.rb +15 -49
  37. data/lib/mongoid/identity.rb +6 -4
  38. data/lib/mongoid/keys.rb +85 -31
  39. data/lib/mongoid/multi_parameter_attributes.rb +2 -2
  40. data/lib/mongoid/named_scope.rb +129 -28
  41. data/lib/mongoid/observer.rb +36 -0
  42. data/lib/mongoid/paranoia.rb +3 -3
  43. data/lib/mongoid/paths.rb +1 -1
  44. data/lib/mongoid/persistence.rb +2 -0
  45. data/lib/mongoid/persistence/atomic.rb +88 -0
  46. data/lib/mongoid/persistence/atomic/add_to_set.rb +30 -0
  47. data/lib/mongoid/persistence/atomic/inc.rb +28 -0
  48. data/lib/mongoid/persistence/atomic/operation.rb +44 -0
  49. data/lib/mongoid/persistence/atomic/pull_all.rb +33 -0
  50. data/lib/mongoid/persistence/atomic/push.rb +28 -0
  51. data/lib/mongoid/railtie.rb +13 -1
  52. data/lib/mongoid/relations.rb +1 -0
  53. data/lib/mongoid/relations/accessors.rb +20 -2
  54. data/lib/mongoid/relations/builders/embedded/one.rb +1 -0
  55. data/lib/mongoid/relations/builders/nested_attributes/many.rb +17 -6
  56. data/lib/mongoid/relations/builders/referenced/many.rb +2 -1
  57. data/lib/mongoid/relations/builders/referenced/one.rb +1 -0
  58. data/lib/mongoid/relations/embedded/atomic.rb +86 -0
  59. data/lib/mongoid/relations/embedded/atomic/operation.rb +63 -0
  60. data/lib/mongoid/relations/embedded/atomic/pull.rb +65 -0
  61. data/lib/mongoid/relations/embedded/atomic/push_all.rb +59 -0
  62. data/lib/mongoid/relations/embedded/atomic/set.rb +61 -0
  63. data/lib/mongoid/relations/embedded/atomic/unset.rb +41 -0
  64. data/lib/mongoid/relations/embedded/many.rb +57 -25
  65. data/lib/mongoid/relations/macros.rb +6 -4
  66. data/lib/mongoid/relations/many.rb +51 -10
  67. data/lib/mongoid/relations/metadata.rb +4 -2
  68. data/lib/mongoid/relations/proxy.rb +39 -24
  69. data/lib/mongoid/relations/referenced/many.rb +47 -26
  70. data/lib/mongoid/relations/referenced/many_to_many.rb +47 -14
  71. data/lib/mongoid/relations/referenced/one.rb +14 -0
  72. data/lib/mongoid/sharding.rb +51 -0
  73. data/lib/mongoid/state.rb +3 -2
  74. data/lib/mongoid/timestamps.rb +5 -29
  75. data/lib/mongoid/timestamps/created.rb +31 -0
  76. data/lib/mongoid/timestamps/updated.rb +33 -0
  77. data/lib/mongoid/validations.rb +10 -3
  78. data/lib/mongoid/validations/referenced.rb +58 -0
  79. data/lib/mongoid/version.rb +1 -1
  80. data/lib/mongoid/versioning.rb +67 -5
  81. data/lib/rails/generators/mongoid/model/templates/model.rb +2 -0
  82. data/lib/rails/generators/mongoid/observer/observer_generator.rb +17 -0
  83. data/lib/rails/generators/mongoid/observer/templates/observer.rb +4 -0
  84. data/lib/rails/generators/mongoid_generator.rb +10 -1
  85. data/lib/rails/mongoid.rb +1 -0
  86. metadata +29 -8
  87. data/lib/mongoid/contexts/ids.rb +0 -25
  88. data/lib/mongoid/modifiers.rb +0 -24
  89. data/lib/mongoid/modifiers/command.rb +0 -18
  90. data/lib/mongoid/modifiers/inc.rb +0 -24
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+
5
+ # This module defines criteria behavior for building documents for
6
+ # specified conditions.
7
+ module Builder
8
+
9
+ # Build a document given the selector and return it.
10
+ # Complex criteria, such as $in and $or operations will get ignored.
11
+ #
12
+ # @example build the document.
13
+ # Person.where(:title => "Sir").build
14
+ #
15
+ # @example Build with selectors getting ignored.
16
+ # Person.where(:age.gt => 5).build
17
+ #
18
+ # @return [ Document ] A non-persisted document.
19
+ #
20
+ # @since 2.0.0
21
+ def build(attrs = {})
22
+ klass.new(
23
+ selector.inject(attrs) do |hash, (key, value)|
24
+ hash.tap do |attrs|
25
+ unless key.to_s =~ /\$/ || value.is_a?(Hash)
26
+ attrs[key] = value
27
+ end
28
+ end
29
+ end
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
@@ -18,9 +18,9 @@ module Mongoid #:nodoc:
18
18
  # @return [ Document ] A newly created document.
19
19
  #
20
20
  # @since 2.0.0.rc.1
21
- def create
21
+ def create(attrs = {})
22
22
  klass.create(
23
- selector.inject({}) do |hash, (key, value)|
23
+ selector.inject(attrs) do |hash, (key, value)|
24
24
  hash.tap do |attrs|
25
25
  unless key.to_s =~ /\$/ || value.is_a?(Hash)
26
26
  attrs[key] = value
@@ -1,23 +1,23 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
3
  module Criterion #:nodoc:
4
+
5
+ # This module contains criteria behaviour for exclusion of values.
4
6
  module Exclusion
5
- # Adds a criterion to the +Criteria+ that specifies values that are not allowed
6
- # to match any document in the database. The MongoDB conditional operator that
7
- # will be used is "$ne".
8
- #
9
- # Options:
10
- #
11
- # attributes: A +Hash+ where the key is the field name and the value is a
12
- # value that must not be equal to the corresponding field value in the database.
13
- #
14
- # Example:
7
+
8
+ # Adds a criterion to the +Criteria+ that specifies values that are not
9
+ # allowed to match any document in the database. The MongoDB
10
+ # conditional operator that will be used is "$ne".
15
11
  #
16
- # <tt>criteria.excludes(:field => "value1")</tt>
12
+ # @example Match documents without these values.
13
+ # criteria.excludes(:field => "value1")
14
+ # criteria.excludes(:field1 => "value1", :field2 => "value1")
17
15
  #
18
- # <tt>criteria.excludes(:field1 => "value1", :field2 => "value1")</tt>
16
+ # @param [ Hash ] attributes: A +Hash+ where the key is the field
17
+ # name and the value is a value that must not be equal to the
18
+ # corresponding field value in the database.
19
19
  #
20
- # Returns: <tt>self</tt>
20
+ # @return [ Criteria ] A newly cloned copy.
21
21
  def excludes(attributes = {})
22
22
  mongo_id = attributes.delete(:id)
23
23
  attributes = attributes.merge(:_id => mongo_id) if mongo_id
@@ -25,21 +25,18 @@ module Mongoid #:nodoc:
25
25
  end
26
26
 
27
27
  # Adds a criterion to the +Criteria+ that specifies values where none
28
- # should match in order to return results. This is similar to an SQL "NOT IN"
29
- # clause. The MongoDB conditional operator that will be used is "$nin".
28
+ # should match in order to return results. This is similar to an SQL
29
+ # "NOT IN" clause. The MongoDB conditional operator that will be
30
+ # used is "$nin".
30
31
  #
31
- # Options:
32
+ # @example Match documents with values not in the provided.
33
+ # criteria.not_in(:field => ["value1", "value2"])
34
+ # criteria.not_in(:field1 => ["value1", "value2"], :field2 => ["value1"])
32
35
  #
33
- # attributes: A +Hash+ where the key is the field name and the value is an
34
- # +Array+ of values that none can match.
36
+ # @param [ Hash ] attributes A +Hash+ where the key is the field name
37
+ # and the value is an +Array+ of values that none can match.
35
38
  #
36
- # Example:
37
- #
38
- # <tt>criteria.not_in(:field => ["value1", "value2"])</tt>
39
- #
40
- # <tt>criteria.not_in(:field1 => ["value1", "value2"], :field2 => ["value1"])</tt>
41
- #
42
- # Returns: <tt>self</tt>
39
+ # @return [ Criteria ] A newly cloned copy.
43
40
  def not_in(attributes)
44
41
  update_selector(attributes, "$nin")
45
42
  end
@@ -48,18 +45,47 @@ module Mongoid #:nodoc:
48
45
  # get returned from the Document. Used mainly for list views that do not
49
46
  # require all fields to be present. This is similar to SQL "SELECT" values.
50
47
  #
51
- # Options:
48
+ # @example Limit the fields to only the specified.
49
+ # criteria.only(:field1, :field2, :field3)
52
50
  #
53
- # args: A list of field names to retrict the returned fields to.
51
+ # @note #only and #without cannot be used together.
54
52
  #
55
- # Example:
53
+ # @param [ Array<Symbol> ] args A list of field names to limit to.
56
54
  #
57
- # <tt>criteria.only(:field1, :field2, :field3)</tt>
58
- #
59
- # Returns: <tt>self</tt>
55
+ # @return [ Criteria ] A newly cloned copy.
60
56
  def only(*args)
61
57
  clone.tap do |crit|
62
- crit.options[:fields] = args.flatten if args.any?
58
+ if args.any?
59
+ crit.options[:fields] = {:_type => 1}
60
+ crit.field_list = args.flatten
61
+ crit.field_list.each do |f|
62
+ crit.options[:fields][f] = 1
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ # Adds a criterion to the +Criteria+ that specifies the fields that will
69
+ # not get returned by the document.
70
+ #
71
+ # @example Filter out specific fields.
72
+ # criteria.without(:field2, :field2)
73
+ #
74
+ # @note #only and #without cannot be used together.
75
+ #
76
+ # @param [ Array<Symbol> args A list of fields to exclude.
77
+ #
78
+ # @return [ Criteria ] A newly cloned copy.
79
+ #
80
+ # @since 2.0.0
81
+ def without(*args)
82
+ clone.tap do |crit|
83
+ if args.any?
84
+ crit.options[:fields] = {}
85
+ args.flatten.each do |f|
86
+ crit.options[:fields][f] = 0
87
+ end
88
+ end
63
89
  end
64
90
  end
65
91
  end
@@ -20,6 +20,22 @@ module Mongoid #:nodoc:
20
20
  end
21
21
  alias :all_in :all
22
22
 
23
+ # Adds a criterion to the +Criteria+ that specifies values where any can
24
+ # be matched in order to return results. This is similar to an SQL "IN"
25
+ # clause. The MongoDB conditional operator that will be used is "$in".
26
+ # Any previously matching "$in" arrays will be unioned with new
27
+ # arguments.
28
+ #
29
+ # @example Adding the criterion.
30
+ # criteria.in(:field => ["value1"]).also_in(:field => ["value2"])
31
+ #
32
+ # @param [ Hash ] attributes Name/value pairs any can match.
33
+ #
34
+ # @return [ Criteria ] A new criteria with the added selector.
35
+ def also_in(attributes = {})
36
+ update_selector(attributes, "$in")
37
+ end
38
+
23
39
  # Adds a criterion to the +Criteria+ that specifies values that must
24
40
  # be matched in order to return results. This is similar to a SQL "WHERE"
25
41
  # clause. This is the actual selector that will be provided to MongoDB,
@@ -83,16 +99,13 @@ module Mongoid #:nodoc:
83
99
  #
84
100
  # @return [ Document, Criteria ] The matching document(s).
85
101
  def find(*args)
86
- raise Errors::InvalidOptions.new(
87
- :calling_document_find_with_nil_is_invalid, {}
88
- ) if args[0].nil?
89
- type, criteria = Criteria.parse!(klass, embedded, *args)
90
- criteria.merge(self) if criteria.is_a?(Criteria)
102
+ type, crit = search(*args)
91
103
  case type
92
- when :first then return criteria.one
93
- when :last then return criteria.last
104
+ when :first then crit.one
105
+ when :last then crit.last
106
+ when :ids then execute_or_raise(args, crit)
94
107
  else
95
- return criteria
108
+ crit
96
109
  end
97
110
  end
98
111
 
@@ -108,7 +121,7 @@ module Mongoid #:nodoc:
108
121
  #
109
122
  # @return [ Criteria ] A new criteria with the added selector.
110
123
  def in(attributes = {})
111
- update_selector(attributes, "$in")
124
+ update_selector(attributes, "$in", :&)
112
125
  end
113
126
  alias :any_in :in
114
127
 
@@ -147,13 +160,39 @@ module Mongoid #:nodoc:
147
160
 
148
161
  selector.each_pair do |key, value|
149
162
  if crit.selector.has_key?(key) && crit.selector[key].respond_to?(:merge!)
150
- crit.selector[key].merge!(value)
163
+ crit.selector[key] =
164
+ crit.selector[key].merge!(value) do |key, old, new|
165
+ key == '$in' ? old & new : new
166
+ end
151
167
  else
152
168
  crit.selector[key] = value
153
169
  end
154
170
  end
155
171
  end
156
172
  end
173
+
174
+ private
175
+
176
+ # Execute the criteria or raise an error if no documents found.
177
+ #
178
+ # @example Execute or raise
179
+ # criteria.execute_or_raise(id, criteria)
180
+ #
181
+ # @param [ Object ] args The arguments passed.
182
+ # @param [ Criteria ] criteria The criteria to execute.
183
+ #
184
+ # @raise [ Errors::DocumentNotFound ] If nothing returned.
185
+ #
186
+ # @return [ Document, Array<Document> ] The document(s).
187
+ #
188
+ # @since 2.0.0
189
+ def execute_or_raise(args, criteria)
190
+ (args[0].is_a?(Array) ? criteria.entries : criteria.one).tap do |result|
191
+ if Mongoid.raise_not_found_error && !args.flatten.blank?
192
+ raise Errors::DocumentNotFound.new(klass, args) if result.blank?
193
+ end
194
+ end
195
+ end
157
196
  end
158
197
  end
159
198
  end
@@ -102,7 +102,7 @@ module Mongoid #:nodoc:
102
102
  # <tt>criteria.id(["4ab2bc4b8ad548971900005c", "4c454e7ebf4b98032d000001"])</tt>
103
103
  #
104
104
  # Returns: <tt>self</tt>
105
- def id(*ids)
105
+ def for_ids(*ids)
106
106
  ids.flatten!
107
107
  if ids.size > 1
108
108
  any_in(
@@ -2,26 +2,64 @@
2
2
  module Mongoid #:nodoc:
3
3
  module Criterion #:nodoc:
4
4
 
5
+ # The selector is a hash-like object that has special behaviour for merging
6
+ # mongoid criteria selectors.
5
7
  class Selector < Hash
6
- attr_reader :klass
7
8
 
9
+ attr_reader :fields, :klass
10
+
11
+ # Create the new selector.
12
+ #
13
+ # @example Create the selector.
14
+ # Selector.new(Person)
15
+ #
16
+ # @param [ Class ] klass The class the selector is for.
17
+ #
18
+ # @since 1.0.0
8
19
  def initialize(klass)
9
- @klass = klass
20
+ @fields, @klass = klass.fields.except("_id", "_type"), klass
10
21
  end
11
22
 
23
+ # Set the value for the supplied key, attempting to typecast the value.
24
+ #
25
+ # @example Set the value for the key.
26
+ # selector["$ne"] = { :name => "Zorg" }
27
+ #
28
+ # @param [ String, Symbol ] key The hash key.
29
+ # @param [ Object ] value The value to set.
30
+ #
31
+ # @since 2.0.0
12
32
  def []=(key, value)
13
33
  super(key, try_to_typecast(key, value))
14
34
  end
15
35
 
36
+ # Merge the selector with another hash.
37
+ #
38
+ # @example Merge the objects.
39
+ # selector.merge!({ :key => "value" })
40
+ #
41
+ # @param [ Hash, Selector ] other The object to merge with.
42
+ #
43
+ # @return [ Selector ] The merged selector.
44
+ #
45
+ # @since 1.0.0
16
46
  def merge!(other)
17
- other.each_pair do |key, value|
18
- self[key] = value
47
+ tap do |selector|
48
+ other.each_pair do |key, value|
49
+ selector[key] = value
50
+ end
19
51
  end
20
- self
21
52
  end
22
- alias update merge!
53
+ alias :update :merge!
23
54
 
24
55
  if RUBY_VERSION < '1.9'
56
+
57
+ # Generate pretty inspection for old ruby versions.
58
+ #
59
+ # @example Inspect the selector.
60
+ # selector.inspect
61
+ #
62
+ # @return [ String ] The inspected selector.
25
63
  def inspect
26
64
  ret = self.keys.inject([]) do |ret, key|
27
65
  ret << "#{key.inspect}=>#{self[key].inspect}"
@@ -32,14 +70,35 @@ module Mongoid #:nodoc:
32
70
 
33
71
  private
34
72
 
73
+ # If the key is defined as a field, then attempt to typecast it.
74
+ #
75
+ # @example Try to cast.
76
+ # selector.try_to_typecast(:id, 1)
77
+ #
78
+ # @param [ String, Symbol ] key The field name.
79
+ # @param [ Object ] value The value.
80
+ #
81
+ # @return [ Object ] The typecasted value.
82
+ #
83
+ # @since 1.0.0
35
84
  def try_to_typecast(key, value)
36
85
  access = key.to_s
37
- return value unless klass.fields.has_key?(access)
38
-
39
- field = klass.fields[access]
86
+ return value unless fields.has_key?(access)
87
+ field = fields[access]
40
88
  typecast_value_for(field, value)
41
89
  end
42
90
 
91
+ # Get the typecast value for the defined field.
92
+ #
93
+ # @example Get the typecast value.
94
+ # selector.typecast_value_for(:name, "Corbin")
95
+ #
96
+ # @param [ Field ] field The defined field.
97
+ # @param [ Object ] value The value to cast.
98
+ #
99
+ # @return [ Object ] The cast value.
100
+ #
101
+ # @since 1.0.0
43
102
  def typecast_value_for(field, value)
44
103
  return field.set(value) if field.type === value
45
104
  case value
@@ -57,6 +116,18 @@ module Mongoid #:nodoc:
57
116
  end
58
117
  end
59
118
 
119
+ # Typecast the value for booleans and integers in hashes.
120
+ #
121
+ # @example Typecast the hash values.
122
+ # selector.typecast_hash_value(field, "$exists", "true")
123
+ #
124
+ # @param [ Field ] field The defined field.
125
+ # @param [ String ] key The modifier key.
126
+ # @param [ Object ] value The value to cast.
127
+ #
128
+ # @return [ Object ] The cast value.
129
+ #
130
+ # @since 1.0.0
60
131
  def typecast_hash_value(field, key, value)
61
132
  case key
62
133
  when "$exists"
@@ -67,8 +138,6 @@ module Mongoid #:nodoc:
67
138
  typecast_value_for(field, value)
68
139
  end
69
140
  end
70
-
71
141
  end
72
-
73
142
  end
74
143
  end
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc
3
3
  class Cursor
4
+ include Mongoid::Collections::Retry
4
5
  include Enumerable
5
6
  # Operations on the Mongo::Cursor object that will not get overriden by the
6
7
  # Mongoid::Cursor are defined here.
@@ -31,7 +32,11 @@ module Mongoid #:nodoc
31
32
  #
32
33
  # <tt>cursor.close</tt>
33
34
  OPERATIONS.each do |name|
34
- define_method(name) { |*args| @cursor.send(name, *args) }
35
+ define_method(name) do |*args|
36
+ retry_on_connection_failure do
37
+ @cursor.send(name, *args)
38
+ end
39
+ end
35
40
  end
36
41
 
37
42
  # Iterate over each document in the cursor and yield to it.
@@ -3,26 +3,34 @@ module Mongoid #:nodoc:
3
3
 
4
4
  # This module handles functionality for creating default scopes.
5
5
  module DefaultScope
6
+ extend ActiveSupport::Concern
6
7
 
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)
8
+ included do
9
+ class_attribute :default_scoping
10
+ end
11
+
12
+ module ClassMethods #:nodoc:
13
+
14
+ # Creates a default_scope for the +Document+, similar to ActiveRecord's
15
+ # default_scope. +DefaultScopes+ are proxied +Criteria+ objects that are
16
+ # applied by default to all queries for the class.
17
+ #
18
+ # @example Create a default scope.
19
+ #
20
+ # class Person
21
+ # include Mongoid::Document
22
+ # field :active, :type => Boolean
23
+ # field :count, :type => Integer
24
+ #
25
+ # default_scope :where => { :active => true }
26
+ # end
27
+ #
28
+ # @param [ Hash ] conditions The conditions to create with.
29
+ #
30
+ # @since 2.0.0.rc.1
31
+ def default_scope(conditions = {}, &block)
32
+ self.default_scoping = Scope.new(conditions, &block).conditions.scoped
33
+ end
26
34
  end
27
35
  end
28
36
  end