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
@@ -113,7 +113,7 @@ module Mongoid #:nodoc:
113
113
  # @return [ Document ] A new document.
114
114
  def initialize(attrs = nil)
115
115
  @new_record = true
116
- @attributes = default_attributes
116
+ @attributes = apply_default_attributes
117
117
  process(attrs) do |document|
118
118
  yield self if block_given?
119
119
  identify
@@ -147,6 +147,7 @@ module Mongoid #:nodoc:
147
147
  raise Errors::DocumentNotFound.new(self.class, id) if reloaded.nil?
148
148
  end
149
149
  @attributes = {}.merge(reloaded || {})
150
+ apply_default_attributes
150
151
  reset_modifications
151
152
  tap do
152
153
  relations.keys.each do |name|
@@ -233,7 +234,9 @@ module Mongoid #:nodoc:
233
234
  if attributes["_id"]
234
235
  allocate.tap do |doc|
235
236
  doc.instance_variable_set(:@attributes, attributes)
237
+ doc.send(:apply_default_attributes)
236
238
  doc.setup_modifications
239
+ doc.run_callbacks(:initialize) { doc }
237
240
  end
238
241
  else
239
242
  new(attrs)
@@ -255,5 +258,27 @@ module Mongoid #:nodoc:
255
258
  :mongoid
256
259
  end
257
260
  end
261
+
262
+ # Freezes the internal attributes of the document.
263
+ #
264
+ # @example Freeze the document
265
+ # document.freeze
266
+ #
267
+ # @return [ Document ] The document.
268
+ def freeze
269
+ raw_attributes.freeze
270
+ self
271
+ end
272
+
273
+ # Checks if the document is frozen
274
+ #
275
+ # @example Check if frozen
276
+ # document.frozen?
277
+ #
278
+ # @return [ true, false ] True if frozen, else false.
279
+ def frozen?
280
+ raw_attributes.frozen?
281
+ end
282
+
258
283
  end
259
284
  end
@@ -6,6 +6,7 @@ require "mongoid/errors/invalid_database"
6
6
  require "mongoid/errors/invalid_field"
7
7
  require "mongoid/errors/invalid_options"
8
8
  require "mongoid/errors/invalid_type"
9
+ require "mongoid/errors/mixed_relations"
9
10
  require "mongoid/errors/too_many_nested_attribute_records"
10
11
  require "mongoid/errors/unsaved_document"
11
12
  require "mongoid/errors/unsupported_version"
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+ module Errors #:nodoc
4
+
5
+ # This error is raised when trying to reference an embedded document from
6
+ # a document in another collection that is not it's parent.
7
+ #
8
+ # @example An illegal reference to an embedded document.
9
+ # class Post
10
+ # include Mongoid::Document
11
+ # references_many :addresses
12
+ # end
13
+ #
14
+ # class Address
15
+ # include Mongoid::Document
16
+ # embedded_in :person
17
+ # referenced_in :post
18
+ # end
19
+ #
20
+ # @since 2.0.0
21
+ class MixedRelations < MongoidError
22
+
23
+ attr_reader :root_klass, :embedded_klass
24
+
25
+ def initialize(root_klass, embedded_klass)
26
+ @root_klass, @embedded_klass = root_klass, embedded_klass
27
+
28
+ super(
29
+ translate(
30
+ "mixed_relations",
31
+ { :root => root_klass, :embedded => embedded_klass }
32
+ )
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
@@ -42,6 +42,8 @@ module Mongoid #:nodoc:
42
42
  # Convert the supplied arguments to object ids based on the class
43
43
  # settings.
44
44
  #
45
+ # @todo Durran: This method can be refactored.
46
+ #
45
47
  # @example Convert a string to an object id
46
48
  # BSON::ObjectId.convert(Person, "4c52c439931a90ab29000003")
47
49
  #
@@ -64,16 +66,17 @@ module Mongoid #:nodoc:
64
66
  return args if args.is_a?(BSON::ObjectId) || !klass.using_object_ids?
65
67
  case args
66
68
  when ::String
67
- BSON::ObjectId.from_string(args)
69
+ args.blank? ? nil : BSON::ObjectId.from_string(args)
68
70
  when ::Array
69
- args.map do |arg|
71
+ args.reject(&:blank?).map do |arg|
70
72
  convert(klass, arg)
71
73
  end
72
74
  when ::Hash
73
75
  args.tap do |hash|
74
- args.each_pair do |key, value|
76
+ hash.each_pair do |key, value|
77
+ next unless key.to_s =~ /id/
75
78
  begin
76
- args[key] = convert(klass, value)
79
+ hash[key] = convert(klass, value)
77
80
  rescue BSON::InvalidObjectId; end
78
81
  end
79
82
  end
@@ -14,7 +14,7 @@ module Mongoid #:nodoc:
14
14
  def self.build(klass, attributes)
15
15
  attrs = {}.merge(attributes)
16
16
  type = attrs["_type"]
17
- type ? type.constantize.instantiate(attrs) : klass.instantiate(attrs)
17
+ type.present? ? type.constantize.instantiate(attrs) : klass.instantiate(attrs)
18
18
  end
19
19
  end
20
20
  end
@@ -1,34 +1,40 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
+
4
+ # Defines the behaviour for defined fields in the document.
3
5
  class Field
4
- attr_reader :copyable, :klass, :label, :name, :options, :type
6
+
7
+ attr_accessor :type
8
+ attr_reader :copyable, :klass, :label, :name, :options
5
9
 
6
10
  # Get the default value for the field.
7
11
  #
8
- # Returns:
12
+ # @example Get the default.
13
+ # field.default
9
14
  #
10
- # The typecast default value.
15
+ # @return [ Object ] The typecast default value.
16
+ #
17
+ # @since 1.0.0
11
18
  def default
12
19
  copy.respond_to?(:call) ? copy : set(copy)
13
20
  end
14
21
 
15
- # Create the new field with a name and optional additional options. Valid
16
- # options are :default
22
+ # Create the new field with a name and optional additional options.
17
23
  #
18
- # Options:
24
+ # @example Create the new field.
25
+ # Field.new(:name, :type => String)
19
26
  #
20
- # name: The name of the field as a +Symbol+.
21
- # options: A +Hash+ of options for the field.
27
+ # @param [ Hash ] options The field options.
22
28
  #
23
- # Example:
29
+ # @option options [ Class ] :type The class of the field.
30
+ # @option options [ Object ] :default The default value for the field.
31
+ # @option options [ String ] :label The field's label.
24
32
  #
25
- # <tt>Field.new(:score, :default => 0)</tt>
33
+ # @since 1.0.0
26
34
  def initialize(name, options = {})
27
- check_name!(name)
28
35
  @type = options[:type] || Object
29
- @name, @default = name, options[:default]
36
+ @name, @default, @label = name, options[:default], options[:label]
30
37
  @copyable = (@default.is_a?(Array) || @default.is_a?(Hash))
31
- @label = options[:label]
32
38
  @options = options
33
39
  check_default!
34
40
  end
@@ -40,19 +46,20 @@ module Mongoid #:nodoc:
40
46
  # If the field is an identity field, ie an id, it performs the necessary
41
47
  # cast.
42
48
  #
43
- # Example:
49
+ # @example Get the setter value.
50
+ # field.set("New Value")
44
51
  #
45
- # <tt>field.set("New Value")</tt>
52
+ # @param [ Object ] object The value to cast to a database value.
46
53
  #
47
- # Returns:
54
+ # @return [ Object ] The typecast value.
48
55
  #
49
- # The new value.
56
+ # @since 1.0.0
50
57
  def set(object)
51
58
  unless options[:identity]
52
59
  type.set(object)
53
60
  else
54
61
  if object.blank?
55
- type.set(object)
62
+ type.set(object) if object.is_a?(Array)
56
63
  else
57
64
  options[:metadata].constraint.convert(object)
58
65
  end
@@ -61,30 +68,40 @@ module Mongoid #:nodoc:
61
68
 
62
69
  # Used for retrieving the object out of the attributes hash.
63
70
  #
64
- # Example:
71
+ # @example Get the value.
72
+ # field.get("Value")
65
73
  #
66
- # <tt>field.get("Value")</tt>
74
+ # @param [ Object ] The object to cast from the database.
67
75
  #
68
- # Returns:
76
+ # @return [ Object ] The converted value.
69
77
  #
70
- # The converted value.
78
+ # @since 1.0.0
71
79
  def get(object)
72
80
  type.get(object)
73
81
  end
74
82
 
75
83
  protected
76
- # Slightly faster default check.
84
+
85
+ # Copy the default value if copyable.
86
+ #
87
+ # @example Copy the default.
88
+ # field.copy
89
+ #
90
+ # @return [ Object ] The copied object or the original.
91
+ #
92
+ # @since 1.0.0
77
93
  def copy
78
94
  copyable ? @default.dup : @default
79
95
  end
80
96
 
81
- # Check if the name is valid.
82
- def check_name!(name)
83
- if Mongoid.destructive_fields.include?(name.to_s)
84
- raise Mongoid::Errors::InvalidField.new(name)
85
- end
86
- end
87
-
97
+ # Checks if the default value is of the same type as the field.
98
+ #
99
+ # @example Check the default value.
100
+ # field.check_default!
101
+ #
102
+ # @raise [ Errors::InvalidType ] If the types differ.
103
+ #
104
+ # @since 1.0.0
88
105
  def check_default!
89
106
  return if @default.is_a?(Proc)
90
107
  if !@default.nil? && !@default.is_a?(type)
@@ -9,11 +9,17 @@ module Mongoid #:nodoc
9
9
  # Set up the class attributes that must be available to all subclasses.
10
10
  # These include defaults, fields
11
11
  delegate :defaults, :fields, :to => "self.class"
12
+
13
+ field(:_type, :type => String)
14
+ field(:_id, :type => BSON::ObjectId)
15
+
16
+ alias :id :_id
17
+ alias :id= :_id=
12
18
  end
13
19
 
14
20
  module ClassMethods #:nodoc
15
21
 
16
- # Defines all the fields that are accessable on the Document
22
+ # Defines all the fields that are accessible on the Document
17
23
  # For each field that is defined, a getter and setter will be
18
24
  # added as an instance method to the Document.
19
25
  #
@@ -71,7 +77,7 @@ module Mongoid #:nodoc
71
77
  end
72
78
 
73
79
  # When inheriting, we want to copy the fields from the parent class and
74
- # set the on the child to start, mimicing the behaviour of the old
80
+ # set the on the child to start, mimicking the behaviour of the old
75
81
  # class_inheritable_accessor that was deprecated in Rails edge.
76
82
  #
77
83
  # @example Inherit from this class.
@@ -81,6 +87,7 @@ module Mongoid #:nodoc
81
87
  #
82
88
  # @since 2.0.0.rc.6
83
89
  def inherited(subclass)
90
+ super
84
91
  subclass.fields = fields.dup
85
92
  end
86
93
 
@@ -1,12 +1,15 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
- module Finders #:nodoc:
3
+
4
+ # This module defines the finder methods that hang off the document at the
5
+ # class level.
6
+ module Finders
4
7
 
5
8
  # Delegate to the criteria methods that are natural for creating a new
6
9
  # criteria.
7
10
  [ :all_in, :any_in, :any_of, :asc, :ascending, :avg, :desc, :descending,
8
11
  :excludes, :limit, :max, :min, :not_in, :only, :order_by,
9
- :skip, :sum, :where, :update, :update_all, :near ].each do |name|
12
+ :skip, :sum, :without, :where, :update, :update_all, :near ].each do |name|
10
13
  define_method(name) do |*args|
11
14
  criteria.send(name, *args)
12
15
  end
@@ -28,7 +31,7 @@ module Mongoid #:nodoc:
28
31
  #
29
32
  # <tt>Person.count(:conditions => { :attribute => "value" })</tt>
30
33
  def count(*args)
31
- Criteria.translate(self, false, *args).count
34
+ find(:all, *args).count
32
35
  end
33
36
 
34
37
  # Returns true if there are on document in database based on the
@@ -36,17 +39,7 @@ module Mongoid #:nodoc:
36
39
  #
37
40
  # <tt>Person.exists?(:conditions => { :attribute => "value" })</tt>
38
41
  def exists?(*args)
39
- Criteria.translate(self, false, *args).limit(1).count == 1
40
- end
41
-
42
- # Helper to initialize a new +Criteria+ object for this class, or return
43
- # the currently scoped +Criteria+ object.
44
- #
45
- # Example:
46
- #
47
- # <tt>Person.criteria</tt>
48
- def criteria(embedded = false)
49
- scope_stack.last || Criteria.new(self, embedded)
42
+ find(:all, *args).limit(1).count == 1
50
43
  end
51
44
 
52
45
  # Find a +Document+ in several different ways.
@@ -71,16 +64,7 @@ module Mongoid #:nodoc:
71
64
  #
72
65
  # A document or criteria.
73
66
  def find(*args)
74
- raise Errors::InvalidOptions.new(
75
- :calling_document_find_with_nil_is_invalid, {}
76
- ) if args[0].nil?
77
- type, criteria = Criteria.parse!(self, false, *args)
78
- case type
79
- when :first then return criteria.one
80
- when :last then return criteria.last
81
- else
82
- return criteria
83
- end
67
+ criteria.find(*args)
84
68
  end
85
69
 
86
70
  # Find the first +Document+ given the conditions, or creates a new document
@@ -91,8 +75,8 @@ module Mongoid #:nodoc:
91
75
  # args: A +Hash+ of attributes
92
76
  #
93
77
  # <tt>Person.find_or_create_by(:attribute => "value")</tt>
94
- def find_or_create_by(attrs = {})
95
- find_or(:create, attrs)
78
+ def find_or_create_by(attrs = {}, &block)
79
+ find_or(:create, attrs, &block)
96
80
  end
97
81
 
98
82
  # Find the first +Document+ given the conditions, or instantiates a new document
@@ -103,8 +87,8 @@ module Mongoid #:nodoc:
103
87
  # args: A +Hash+ of attributes
104
88
  #
105
89
  # <tt>Person.find_or_initialize_by(:attribute => "value")</tt>
106
- def find_or_initialize_by(attrs = {})
107
- find_or(:new, attrs)
90
+ def find_or_initialize_by(attrs = {}, &block)
91
+ find_or(:new, attrs, &block)
108
92
  end
109
93
 
110
94
  # Find the first +Document+ given the conditions.
@@ -143,31 +127,13 @@ module Mongoid #:nodoc:
143
127
  #
144
128
  # Returns paginated array of docs.
145
129
  def paginate(params = {})
146
- Criteria.translate(self, false, params).paginate
130
+ find(:all, params).paginate
147
131
  end
148
132
 
149
133
  protected
150
134
  # Find the first object or create/initialize it.
151
- def find_or(method, attrs = {})
152
- first(:conditions => attrs) || send(method, attrs)
153
- end
154
-
155
- # Initializes and returns the current scope stack.
156
- def scope_stack
157
- scope_stack_for = Thread.current[:mongoid_scope_stack] ||= {}
158
- scope_stack_for[object_id] ||= []
159
- end
160
-
161
- # Pushes the provided criteria onto the scope stack, and removes it after the
162
- # provided block is yielded.
163
- def with_scope(criteria)
164
- scope_stack = self.scope_stack
165
- scope_stack << criteria
166
- begin
167
- yield criteria
168
- ensure
169
- scope_stack.pop
170
- end
135
+ def find_or(method, attrs = {}, &block)
136
+ first(:conditions => attrs) || send(method, attrs, &block)
171
137
  end
172
138
  end
173
139
  end
@@ -12,8 +12,7 @@ module Mongoid #:nodoc:
12
12
  # @example Create the id and set the type.
13
13
  # identity.create
14
14
  def create
15
- identify
16
- type
15
+ identify.tap { type }
17
16
  end
18
17
 
19
18
  # Create the new identity generator - this will be expanded in the future
@@ -49,8 +48,11 @@ module Mongoid #:nodoc:
49
48
  # @example Set the id.
50
49
  # identity.identify
51
50
  def identify
52
- document.id = compose.join(" ").identify if document.primary_key
53
- document.id = generate_id if document.id.blank?
51
+ if !document.embedded? || Mongoid.embedded_object_id
52
+ document.id = compose.join(" ").identify if document.primary_key
53
+ document.id = generate_id if document.id.blank?
54
+ end
55
+ document.id
54
56
  end
55
57
 
56
58
  # Set the _type field on the document if the document is hereditary or in a