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

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