mongoid 2.0.0.rc.6 → 2.0.0.rc.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/Rakefile +2 -2
  2. data/lib/config/locales/bg.yml +3 -6
  3. data/lib/config/locales/de.yml +2 -5
  4. data/lib/config/locales/en.yml +2 -5
  5. data/lib/config/locales/es.yml +2 -5
  6. data/lib/config/locales/fr.yml +2 -5
  7. data/lib/config/locales/hu.yml +2 -5
  8. data/lib/config/locales/it.yml +2 -5
  9. data/lib/config/locales/kr.yml +2 -5
  10. data/lib/config/locales/nl.yml +2 -5
  11. data/lib/config/locales/pl.yml +2 -5
  12. data/lib/config/locales/pt-br.yml +2 -5
  13. data/lib/config/locales/pt.yml +2 -5
  14. data/lib/config/locales/ro.yml +2 -5
  15. data/lib/config/locales/ru.yml +41 -0
  16. data/lib/config/locales/sv.yml +2 -5
  17. data/lib/config/locales/zh-CN.yml +3 -6
  18. data/lib/mongoid/atomicity.rb +2 -2
  19. data/lib/mongoid/attributes.rb +40 -69
  20. data/lib/mongoid/attributes/processing.rb +142 -0
  21. data/lib/mongoid/collections.rb +1 -0
  22. data/lib/mongoid/components.rb +1 -1
  23. data/lib/mongoid/config.rb +0 -1
  24. data/lib/mongoid/contexts/mongo.rb +13 -9
  25. data/lib/mongoid/criteria.rb +15 -1
  26. data/lib/mongoid/criterion/inclusion.rb +36 -11
  27. data/lib/mongoid/criterion/inspection.rb +3 -1
  28. data/lib/mongoid/criterion/optional.rb +2 -2
  29. data/lib/mongoid/dirty.rb +1 -0
  30. data/lib/mongoid/document.rb +11 -5
  31. data/lib/mongoid/extensions/integer/conversions.rb +2 -2
  32. data/lib/mongoid/extensions/object_id/conversions.rb +62 -32
  33. data/lib/mongoid/field.rb +5 -2
  34. data/lib/mongoid/identity.rb +2 -1
  35. data/lib/mongoid/matchers.rb +5 -32
  36. data/lib/mongoid/matchers/default.rb +53 -10
  37. data/lib/mongoid/matchers/or.rb +30 -0
  38. data/lib/mongoid/matchers/strategies.rb +63 -0
  39. data/lib/mongoid/multi_parameter_attributes.rb +4 -2
  40. data/lib/mongoid/nested_attributes.rb +8 -0
  41. data/lib/mongoid/persistence.rb +2 -1
  42. data/lib/mongoid/persistence/insert.rb +3 -3
  43. data/lib/mongoid/persistence/insert_embedded.rb +2 -1
  44. data/lib/mongoid/relations.rb +2 -1
  45. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +4 -2
  46. data/lib/mongoid/relations/builders/nested_attributes/many.rb +2 -0
  47. data/lib/mongoid/relations/cascading.rb +1 -1
  48. data/lib/mongoid/relations/cascading/nullify.rb +1 -1
  49. data/lib/mongoid/relations/constraint.rb +42 -0
  50. data/lib/mongoid/relations/cyclic.rb +6 -0
  51. data/lib/mongoid/relations/embedded/many.rb +4 -4
  52. data/lib/mongoid/relations/macros.rb +1 -1
  53. data/lib/mongoid/relations/many.rb +1 -0
  54. data/lib/mongoid/relations/metadata.rb +12 -4
  55. data/lib/mongoid/relations/nested_builder.rb +1 -3
  56. data/lib/mongoid/relations/referenced/many.rb +11 -18
  57. data/lib/mongoid/relations/referenced/many_to_many.rb +19 -32
  58. data/lib/mongoid/relations/referenced/one.rb +3 -1
  59. data/lib/mongoid/timestamps.rb +1 -1
  60. data/lib/mongoid/validations/associated.rb +11 -9
  61. data/lib/mongoid/validations/uniqueness.rb +1 -1
  62. data/lib/mongoid/version.rb +1 -1
  63. data/lib/mongoid/versioning.rb +2 -2
  64. metadata +9 -4
@@ -51,8 +51,11 @@ module Mongoid #:nodoc:
51
51
  unless options[:identity]
52
52
  type.set(object)
53
53
  else
54
- metadata = options[:metadata]
55
- object.blank? ? type.set(object) : BSON::ObjectId.cast!(metadata.inverse_klass, object)
54
+ if object.blank?
55
+ type.set(object)
56
+ else
57
+ options[:metadata].constraint.convert(object)
58
+ end
56
59
  end
57
60
  end
58
61
 
@@ -12,7 +12,8 @@ module Mongoid #:nodoc:
12
12
  # @example Create the id and set the type.
13
13
  # identity.create
14
14
  def create
15
- identify and type
15
+ identify
16
+ type
16
17
  end
17
18
 
18
19
  # Create the new identity generator - this will be expanded in the future
@@ -1,15 +1,5 @@
1
1
  # encoding: utf-8
2
- require "mongoid/matchers/default"
3
- require "mongoid/matchers/all"
4
- require "mongoid/matchers/exists"
5
- require "mongoid/matchers/gt"
6
- require "mongoid/matchers/gte"
7
- require "mongoid/matchers/in"
8
- require "mongoid/matchers/lt"
9
- require "mongoid/matchers/lte"
10
- require "mongoid/matchers/ne"
11
- require "mongoid/matchers/nin"
12
- require "mongoid/matchers/size"
2
+ require "mongoid/matchers/strategies"
13
3
 
14
4
  module Mongoid #:nodoc:
15
5
 
@@ -28,28 +18,11 @@ module Mongoid #:nodoc:
28
18
  # @return [ true, false ] True if matches, false if not.
29
19
  def matches?(selector)
30
20
  selector.each_pair do |key, value|
31
- return false unless matcher(key, value).matches?(value)
32
- end; true
33
- end
34
-
35
- protected
36
-
37
- # Get the matcher for the supplied key and value. Will determine the class
38
- # name from the key.
39
- #
40
- # @example Get the matcher.
41
- # document.matcher(:title, { "$in" => [ "test" ] })
42
- #
43
- # @param [ Symbol, String ] key The field name.
44
- # @param [ Object, Hash ] The value or selector.
45
- #
46
- # @return [ Matcher ] The matcher.
47
- def matcher(key, value)
48
- if value.is_a?(Hash)
49
- name = "Mongoid::Matchers::#{value.keys.first.gsub("$", "").camelize}"
50
- return name.constantize.new(attributes[key])
21
+ unless Strategies.matcher(self, key, value).matches?(value)
22
+ return false
23
+ end
51
24
  end
52
- Default.new(attributes[key])
25
+ return true
53
26
  end
54
27
  end
55
28
  end
@@ -1,26 +1,69 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
3
  module Matchers #:nodoc:
4
+
5
+ # Contains all the default behavior for checking for matching documents
6
+ # given MongoDB expressions.
4
7
  class Default
8
+
9
+ attr_accessor :attribute, :document
10
+
5
11
  # Creating a new matcher only requires the value.
6
- def initialize(attribute)
7
- @attribute = attribute
12
+ #
13
+ # @example Create a new matcher.
14
+ # Default.new("attribute")
15
+ #
16
+ # @param [ Object ] attribute The current attribute to check against.
17
+ #
18
+ # @since 1.0.0
19
+ def initialize(attribute, document = nil)
20
+ @attribute, @document = attribute, document
8
21
  end
9
- # Return true if the attribute and value are equal.
22
+
23
+ # Return true if the attribute and value are equal, or if it is an array
24
+ # if the value is included.
25
+ #
26
+ # @example Does this value match?
27
+ # default.matches?("value")
28
+ #
29
+ # @param [ Object ] value The value to check if it matches.
30
+ #
31
+ # @return [ true, false ] True if matches, false if not.
32
+ #
33
+ # @since 1.0.0
10
34
  def matches?(value)
11
- @attribute.is_a?(Array) && value.is_a?(String) ?
12
- @attribute.include?(value) : value === @attribute
35
+ attribute.is_a?(Array) ? attribute.include?(value) : value === attribute
13
36
  end
14
37
 
15
38
  protected
16
- # Return the first value in the hash.
17
- def first(value)
18
- value.values.first
39
+
40
+ # Convenience method for getting the first value in a hash.
41
+ #
42
+ # @example Get the first value.
43
+ # matcher.first(:test => "value")
44
+ #
45
+ # @param [ Hash ] hash The has to pull from.
46
+ #
47
+ # @return [ Object ] The first value.
48
+ #
49
+ # @since 1.0.0
50
+ def first(hash)
51
+ hash.values.first
19
52
  end
20
53
 
21
- # If object exists then compare, else return false
54
+ # If object exists then compare the two, otherwise return false
55
+ #
56
+ # @example Determine if we can compare.
57
+ # matcher.determine("test", "$in")
58
+ #
59
+ # @param [ Object ] value The value to compare with.
60
+ # @param [ Symbol, String ] operator The comparison operation.
61
+ #
62
+ # @return [ true, false ] The comparison or false.
63
+ #
64
+ # @since 1.0.0
22
65
  def determine(value, operator)
23
- @attribute ? @attribute.send(operator, first(value)) : false
66
+ attribute ? attribute.send(operator, first(value)) : false
24
67
  end
25
68
  end
26
69
  end
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Matchers #:nodoc:
4
+
5
+ # Defines behavior for handling $or expressions in embedded documents.
6
+ class Or < Default
7
+
8
+ # Does the supplied query match the attribute?
9
+ #
10
+ # @example Does this match?
11
+ # matcher.matches?("$or" => [ { field => value } ])
12
+ #
13
+ # @param [ Array ] conditions The or expression.
14
+ #
15
+ # @return [ true, false ] True if matches, false if not.
16
+ #
17
+ # @since 2.0.0.rc.7
18
+ def matches?(conditions)
19
+ conditions.each do |condition|
20
+ key = condition.keys.first
21
+ value = condition.values.first
22
+ if Strategies.matcher(document, key, value).matches?(value)
23
+ return true
24
+ end
25
+ end
26
+ return false
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+ require "mongoid/matchers/default"
3
+ require "mongoid/matchers/all"
4
+ require "mongoid/matchers/exists"
5
+ require "mongoid/matchers/gt"
6
+ require "mongoid/matchers/gte"
7
+ require "mongoid/matchers/in"
8
+ require "mongoid/matchers/lt"
9
+ require "mongoid/matchers/lte"
10
+ require "mongoid/matchers/ne"
11
+ require "mongoid/matchers/nin"
12
+ require "mongoid/matchers/or"
13
+ require "mongoid/matchers/size"
14
+
15
+ module Mongoid #:nodoc:
16
+ module Matchers #:nodoc:
17
+
18
+ # This module is responsible for returning the correct matcher given a
19
+ # MongoDB query expression.
20
+ module Strategies
21
+ extend self
22
+
23
+ MATCHERS = {
24
+ "$all" => Matchers::All,
25
+ "$exists" => Matchers::Exists,
26
+ "$gt" => Matchers::Gt,
27
+ "$gte" => Matchers::Gte,
28
+ "$in" => Matchers::In,
29
+ "$lt" => Matchers::Lt,
30
+ "$lte" => Matchers::Lte,
31
+ "$ne" => Matchers::Ne,
32
+ "$nin" => Matchers::Nin,
33
+ "$or" => Matchers::Or,
34
+ "$size" => Matchers::Size
35
+ }
36
+
37
+ # Get the matcher for the supplied key and value. Will determine the class
38
+ # name from the key.
39
+ #
40
+ # @example Get the matcher.
41
+ # document.matcher(:title, { "$in" => [ "test" ] })
42
+ #
43
+ # @param [ Document ] document The document to check.
44
+ # @param [ Symbol, String ] key The field name.
45
+ # @param [ Object, Hash ] The value or selector.
46
+ #
47
+ # @return [ Matcher ] The matcher.
48
+ #
49
+ # @since 2.0.0.rc.7
50
+ def matcher(document, key, value)
51
+ if value.is_a?(Hash)
52
+ MATCHERS[value.keys.first].new(document.attributes[key])
53
+ else
54
+ if key == "$or"
55
+ Matchers::Or.new(value, document)
56
+ else
57
+ Default.new(document.attributes[key])
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
+
4
+ # @todo: Durran: This module needs an overhaul.
3
5
  module MultiParameterAttributes
4
6
  module Errors
5
7
  # Raised when an error occurred while doing a mass assignment to an attribute through the
@@ -64,9 +66,9 @@ module Mongoid #:nodoc:
64
66
 
65
67
  def instantiate_object(klass, values_with_empty_parameters)
66
68
  return nil if values_with_empty_parameters.all? { |v| v.nil? }
67
-
69
+
68
70
  values = values_with_empty_parameters.collect { |v| v.nil? ? 1 : v }
69
-
71
+
70
72
  if klass == DateTime || klass == Date || klass == Time
71
73
  klass.send(:convert_to_time, values)
72
74
  elsif klass
@@ -3,6 +3,13 @@ module Mongoid #:nodoc:
3
3
  module NestedAttributes
4
4
  extend ActiveSupport::Concern
5
5
 
6
+ included do
7
+ class_attribute :nested_attributes
8
+ self.nested_attributes = []
9
+
10
+ delegate :nested_attributes, :to => "self.class"
11
+ end
12
+
6
13
  module ClassMethods
7
14
  REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |_, value| value.blank? } }
8
15
 
@@ -32,6 +39,7 @@ module Mongoid #:nodoc:
32
39
  options = args.extract_options!
33
40
  options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
34
41
  args.each do |name|
42
+ self.nested_attributes += [ "#{name}_attributes=" ]
35
43
  define_method("#{name}_attributes=") do |attrs|
36
44
  relation = relations[name.to_s]
37
45
  relation.nested_builder(attrs, options).build(self)
@@ -55,6 +55,7 @@ module Mongoid #:nodoc:
55
55
  # @return [ TrueClass ] True.
56
56
  def remove(options = {})
57
57
  if Remove.new(self, options).persist
58
+ raw_attributes.freeze
58
59
  self.destroyed = true
59
60
  cascade!
60
61
  end; true
@@ -71,7 +72,7 @@ module Mongoid #:nodoc:
71
72
  #
72
73
  # @return [ true, false ] True if validation passed.
73
74
  def save!(options = {})
74
- self.class.fail_validate!(self) unless upsert; true
75
+ self.class.fail_validate!(self) unless upsert(options); true
75
76
  end
76
77
 
77
78
  # Update the document in the datbase.
@@ -25,8 +25,8 @@ module Mongoid #:nodoc:
25
25
  # The +Document+, whether the insert succeeded or not.
26
26
  def persist
27
27
  return document if validate && document.invalid?(:create)
28
- document.run_callbacks(:create) do
29
- document.run_callbacks(:save) do
28
+ document.run_callbacks(:save) do
29
+ document.run_callbacks(:create) do
30
30
  if insert
31
31
  document.new_record = false
32
32
  document._children.each { |child| child.new_record = false }
@@ -45,7 +45,7 @@ module Mongoid #:nodoc:
45
45
  options.merge(:validate => validate)
46
46
  ).persist
47
47
  else
48
- collection.insert(document.to_hash, options)
48
+ collection.insert(document.as_document, options)
49
49
  end
50
50
  end
51
51
  end
@@ -31,9 +31,10 @@ module Mongoid #:nodoc:
31
31
  if parent.new_record?
32
32
  parent.insert
33
33
  else
34
- update = { document._inserter => { document._position => document.raw_attributes } }
34
+ update = { document._inserter => { document._position => document.as_document } }
35
35
  collection.update(parent._selector, update, options.merge(:multi => false))
36
36
  document.new_record = false
37
+ document.move_changes
37
38
  end
38
39
  document
39
40
  end
@@ -2,6 +2,7 @@
2
2
  require "mongoid/relations/accessors"
3
3
  require "mongoid/relations/auto_save"
4
4
  require "mongoid/relations/cascading"
5
+ require "mongoid/relations/constraint"
5
6
  require "mongoid/relations/cyclic"
6
7
  require "mongoid/relations/proxy"
7
8
  require "mongoid/relations/bindings"
@@ -50,7 +51,7 @@ module Mongoid # :nodoc:
50
51
  #
51
52
  # @since 2.0.0.rc.1
52
53
  def embedded?
53
- _parent.present?
54
+ cyclic ? _parent.present? : self.class.embedded?
54
55
  end
55
56
 
56
57
  # Determine if the document is part of an embeds_many relation.
@@ -50,7 +50,7 @@ module Mongoid # :nodoc:
50
50
  inverse,
51
51
  false,
52
52
  OPTIONS
53
- ).push(base, :continue => false)
53
+ ).push(base, :binding => true, :continue => false)
54
54
  end
55
55
  end
56
56
  end
@@ -88,7 +88,9 @@ module Mongoid # :nodoc:
88
88
  if options[:continue]
89
89
  inverse = metadata.inverse(target)
90
90
  if inverse
91
- doc.do_or_do_not(inverse).delete(base)
91
+ doc.do_or_do_not(
92
+ inverse, false, OPTIONS
93
+ ).delete(base, :binding => true, :continue => false)
92
94
  end
93
95
  end
94
96
  end
@@ -106,6 +106,8 @@ module Mongoid # :nodoc:
106
106
  document = existing.find(convert_id(attrs[:id]))
107
107
  destroyable?(attrs) ? document.destroy : document.update_attributes(attrs)
108
108
  else
109
+ # @todo: Durran: Tell the push not to save the base and call it
110
+ # after all processing is done. This is related to #581.
109
111
  existing.push(metadata.klass.new(attrs)) unless destroyable?(attrs)
110
112
  end
111
113
  end
@@ -47,7 +47,7 @@ module Mongoid # :nodoc:
47
47
  #
48
48
  # @since 2.0.0.rc.1
49
49
  def cascade(metadata)
50
- tap { cascades << metadata.name.to_s if metadata.dependent? }
50
+ tap { self.cascades += [ metadata.name.to_s ] if metadata.dependent? }
51
51
  end
52
52
  end
53
53
  end
@@ -10,7 +10,7 @@ module Mongoid # :nodoc:
10
10
  # @example Nullify the reference.
11
11
  # strategy.cascade
12
12
  def cascade
13
- relation.nullify
13
+ relation.nullify if relation
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+
5
+ # Used for converting foreign key values to the correct type based on the
6
+ # types of ids that the document stores.
7
+ #
8
+ # @note Durran: The name of this class is this way to match the metadata
9
+ # getter, and foreign_key was already taken there.
10
+ class Constraint
11
+ attr_reader :metadata
12
+
13
+ # Create the new constraint with the metadata.
14
+ #
15
+ # @example Instantiate the constraint.
16
+ # Constraint.new(metdata)
17
+ #
18
+ # @param [ Metadata ] metadata The metadata of the relation.
19
+ #
20
+ # @since 2.0.0.rc.7
21
+ def initialize(metadata)
22
+ @metadata = metadata
23
+ end
24
+
25
+ # Convert the supplied object to the appropriate type to set as the
26
+ # foreign key for a relation.
27
+ #
28
+ # @example Convert the object.
29
+ # constraint.convert("12345")
30
+ #
31
+ # @param [ Object ] object The object to convert.
32
+ #
33
+ # @return [ Object ] The object cast to the correct type.
34
+ #
35
+ # @since 2.0.0.rc.7
36
+ def convert(object)
37
+ return object if metadata.polymorphic?
38
+ BSON::ObjectId.convert(metadata.klass, object)
39
+ end
40
+ end
41
+ end
42
+ end