mongoid 2.0.0.beta.20 → 2.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. data/README.rdoc +8 -0
  2. data/Rakefile +51 -0
  3. data/lib/config/locales/nl.yml +39 -0
  4. data/lib/config/locales/ro.yml +1 -1
  5. data/lib/mongoid.rb +17 -17
  6. data/lib/mongoid/atomicity.rb +54 -22
  7. data/lib/mongoid/attributes.rb +145 -125
  8. data/lib/mongoid/callbacks.rb +7 -2
  9. data/lib/mongoid/collection.rb +49 -32
  10. data/lib/mongoid/collections.rb +0 -1
  11. data/lib/mongoid/components.rb +34 -29
  12. data/lib/mongoid/config.rb +207 -193
  13. data/lib/mongoid/config/database.rb +167 -0
  14. data/lib/mongoid/contexts.rb +2 -5
  15. data/lib/mongoid/contexts/enumerable.rb +30 -4
  16. data/lib/mongoid/contexts/ids.rb +2 -2
  17. data/lib/mongoid/contexts/mongo.rb +30 -5
  18. data/lib/mongoid/copyable.rb +44 -0
  19. data/lib/mongoid/criteria.rb +110 -56
  20. data/lib/mongoid/criterion/creational.rb +34 -0
  21. data/lib/mongoid/criterion/destructive.rb +37 -0
  22. data/lib/mongoid/criterion/exclusion.rb +3 -1
  23. data/lib/mongoid/criterion/inclusion.rb +59 -64
  24. data/lib/mongoid/criterion/inspection.rb +22 -0
  25. data/lib/mongoid/criterion/optional.rb +42 -54
  26. data/lib/mongoid/criterion/selector.rb +9 -0
  27. data/lib/mongoid/default_scope.rb +28 -0
  28. data/lib/mongoid/deprecation.rb +5 -5
  29. data/lib/mongoid/dirty.rb +4 -5
  30. data/lib/mongoid/document.rb +161 -114
  31. data/lib/mongoid/extensions.rb +7 -11
  32. data/lib/mongoid/extensions/array/parentization.rb +2 -2
  33. data/lib/mongoid/extensions/date/conversions.rb +1 -1
  34. data/lib/mongoid/extensions/hash/conversions.rb +0 -23
  35. data/lib/mongoid/extensions/nil/collectionization.rb +12 -0
  36. data/lib/mongoid/extensions/object/reflections.rb +17 -0
  37. data/lib/mongoid/extensions/object/yoda.rb +27 -0
  38. data/lib/mongoid/extensions/string/conversions.rb +23 -4
  39. data/lib/mongoid/extensions/time_conversions.rb +4 -4
  40. data/lib/mongoid/field.rb +30 -19
  41. data/lib/mongoid/fields.rb +15 -5
  42. data/lib/mongoid/finders.rb +19 -11
  43. data/lib/mongoid/hierarchy.rb +34 -28
  44. data/lib/mongoid/identity.rb +62 -20
  45. data/lib/mongoid/inspection.rb +58 -0
  46. data/lib/mongoid/matchers.rb +20 -0
  47. data/lib/mongoid/multi_database.rb +11 -0
  48. data/lib/mongoid/nested_attributes.rb +41 -0
  49. data/lib/mongoid/paranoia.rb +3 -4
  50. data/lib/mongoid/paths.rb +1 -1
  51. data/lib/mongoid/persistence.rb +89 -90
  52. data/lib/mongoid/persistence/command.rb +20 -4
  53. data/lib/mongoid/persistence/insert.rb +13 -11
  54. data/lib/mongoid/persistence/insert_embedded.rb +8 -6
  55. data/lib/mongoid/persistence/remove.rb +6 -4
  56. data/lib/mongoid/persistence/remove_all.rb +6 -4
  57. data/lib/mongoid/persistence/remove_embedded.rb +8 -6
  58. data/lib/mongoid/persistence/update.rb +12 -10
  59. data/lib/mongoid/railtie.rb +2 -2
  60. data/lib/mongoid/railties/database.rake +10 -9
  61. data/lib/mongoid/relations.rb +104 -0
  62. data/lib/mongoid/relations/accessors.rb +154 -0
  63. data/lib/mongoid/relations/auto_save.rb +34 -0
  64. data/lib/mongoid/relations/binding.rb +24 -0
  65. data/lib/mongoid/relations/bindings.rb +9 -0
  66. data/lib/mongoid/relations/bindings/embedded/in.rb +77 -0
  67. data/lib/mongoid/relations/bindings/embedded/many.rb +93 -0
  68. data/lib/mongoid/relations/bindings/embedded/one.rb +65 -0
  69. data/lib/mongoid/relations/bindings/referenced/in.rb +78 -0
  70. data/lib/mongoid/relations/bindings/referenced/many.rb +93 -0
  71. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +94 -0
  72. data/lib/mongoid/relations/bindings/referenced/one.rb +63 -0
  73. data/lib/mongoid/relations/builder.rb +41 -0
  74. data/lib/mongoid/relations/builders.rb +79 -0
  75. data/lib/mongoid/relations/builders/embedded/in.rb +25 -0
  76. data/lib/mongoid/relations/builders/embedded/many.rb +32 -0
  77. data/lib/mongoid/relations/builders/embedded/one.rb +26 -0
  78. data/lib/mongoid/relations/builders/nested_attributes/many.rb +116 -0
  79. data/lib/mongoid/relations/builders/nested_attributes/one.rb +135 -0
  80. data/lib/mongoid/relations/builders/referenced/in.rb +32 -0
  81. data/lib/mongoid/relations/builders/referenced/many.rb +26 -0
  82. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +29 -0
  83. data/lib/mongoid/relations/builders/referenced/one.rb +30 -0
  84. data/lib/mongoid/relations/cascading.rb +55 -0
  85. data/lib/mongoid/relations/cascading/delete.rb +19 -0
  86. data/lib/mongoid/relations/cascading/destroy.rb +19 -0
  87. data/lib/mongoid/relations/cascading/nullify.rb +18 -0
  88. data/lib/mongoid/relations/cascading/strategy.rb +26 -0
  89. data/lib/mongoid/relations/cyclic.rb +97 -0
  90. data/lib/mongoid/relations/embedded/in.rb +172 -0
  91. data/lib/mongoid/relations/embedded/many.rb +450 -0
  92. data/lib/mongoid/relations/embedded/one.rb +169 -0
  93. data/lib/mongoid/relations/macros.rb +302 -0
  94. data/lib/mongoid/relations/many.rb +185 -0
  95. data/lib/mongoid/relations/metadata.rb +529 -0
  96. data/lib/mongoid/relations/nested_builder.rb +52 -0
  97. data/lib/mongoid/relations/one.rb +29 -0
  98. data/lib/mongoid/relations/polymorphic.rb +54 -0
  99. data/lib/mongoid/relations/proxy.rb +122 -0
  100. data/lib/mongoid/relations/referenced/in.rb +214 -0
  101. data/lib/mongoid/relations/referenced/many.rb +358 -0
  102. data/lib/mongoid/relations/referenced/many_to_many.rb +379 -0
  103. data/lib/mongoid/relations/referenced/one.rb +204 -0
  104. data/lib/mongoid/relations/reflections.rb +45 -0
  105. data/lib/mongoid/safe.rb +11 -1
  106. data/lib/mongoid/safety.rb +122 -97
  107. data/lib/mongoid/scope.rb +14 -9
  108. data/lib/mongoid/state.rb +37 -3
  109. data/lib/mongoid/timestamps.rb +11 -0
  110. data/lib/mongoid/validations.rb +42 -3
  111. data/lib/mongoid/validations/associated.rb +8 -5
  112. data/lib/mongoid/validations/uniqueness.rb +23 -2
  113. data/lib/mongoid/version.rb +1 -1
  114. data/lib/mongoid/versioning.rb +25 -16
  115. data/lib/rails/generators/mongoid/model/templates/model.rb +3 -1
  116. metadata +95 -80
  117. data/lib/mongoid/associations.rb +0 -364
  118. data/lib/mongoid/associations/embedded_in.rb +0 -74
  119. data/lib/mongoid/associations/embeds_many.rb +0 -299
  120. data/lib/mongoid/associations/embeds_one.rb +0 -111
  121. data/lib/mongoid/associations/foreign_key.rb +0 -35
  122. data/lib/mongoid/associations/meta_data.rb +0 -38
  123. data/lib/mongoid/associations/options.rb +0 -78
  124. data/lib/mongoid/associations/proxy.rb +0 -60
  125. data/lib/mongoid/associations/referenced_in.rb +0 -70
  126. data/lib/mongoid/associations/references_many.rb +0 -254
  127. data/lib/mongoid/associations/references_many_as_array.rb +0 -128
  128. data/lib/mongoid/associations/references_one.rb +0 -104
  129. data/lib/mongoid/extensions/array/accessors.rb +0 -17
  130. data/lib/mongoid/extensions/array/assimilation.rb +0 -26
  131. data/lib/mongoid/extensions/hash/accessors.rb +0 -42
  132. data/lib/mongoid/extensions/hash/assimilation.rb +0 -40
  133. data/lib/mongoid/extensions/nil/assimilation.rb +0 -17
  134. data/lib/mongoid/memoization.rb +0 -33
@@ -1,111 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Associations #:nodoc:
4
- # Represents an association that is embedded in a parent document as a
5
- # one-to-one relationship.
6
- class EmbedsOne < Proxy
7
-
8
- # Build a new object for the association.
9
- def build(attrs = {}, type = nil)
10
- @target = attrs.assimilate(@parent, @options, type); self
11
- end
12
-
13
- # Replaces the target with a new object
14
- #
15
- # Returns the association proxy
16
- def replace(obj)
17
- @target = obj
18
- self
19
- end
20
-
21
- # Creates the new association by finding the attributes in
22
- # the parent document with its name, and instantiating a
23
- # new document for it.
24
- #
25
- # All method calls on this object will then be delegated
26
- # to the internal document itself.
27
- #
28
- # Options:
29
- #
30
- # document: The parent +Document+
31
- # attributes: The attributes of the target object.
32
- # options: The association options.
33
- #
34
- # Returns:
35
- #
36
- # A new +HashOne+ association proxy.
37
- def initialize(document, options, target = nil)
38
- @parent, @options = document, options
39
-
40
- if target
41
- replace(target)
42
- else
43
- attributes = document.raw_attributes[options.name]
44
- build(attributes) unless attributes.blank?
45
- end
46
-
47
- extends(options)
48
- end
49
-
50
- # Used for setting the association via a nested attributes setter on the
51
- # parent +Document+. Called when using accepts_nested_attributes_for.
52
- #
53
- # Options:
54
- #
55
- # attributes: The attributes for the new association
56
- #
57
- # Returns:
58
- #
59
- # A new target document.
60
- def nested_build(attributes, options = nil)
61
- options ||= {}
62
- _destroy = Boolean.set(attributes.delete('_destroy'))
63
- if options[:allow_destroy] && _destroy
64
- @target = nil
65
- elsif @target.present? || !options[:update_only]
66
- @target.write_attributes(attributes)
67
- end
68
- target
69
- end
70
-
71
- class << self
72
- # Returns the macro used to create the association.
73
- def macro
74
- :embeds_one
75
- end
76
-
77
- # Perform an update of the relationship of the parent and child. This
78
- # will assimilate the child +Document+ into the parent's object graph.
79
- #
80
- # Options:
81
- #
82
- # child: The child +Document+ or +Hash+.
83
- # parent: The parent +Document+ to update.
84
- # options: The association +Options+
85
- #
86
- # Example:
87
- #
88
- # <tt>EmbedsOne.update({:first_name => "Hank"}, person, options)</tt>
89
- #
90
- # Returns:
91
- #
92
- # A new +EmbedsOne+ association proxy.
93
- def update(child, parent, options)
94
- child.assimilate(parent, options)
95
- new(parent, options, child.is_a?(Hash) ? nil : child)
96
- end
97
-
98
- # Validate the options passed to the embeds one macro, to encapsulate
99
- # the behavior in this class instead of the associations module.
100
- #
101
- # Options:
102
- #
103
- # options: Thank you captain obvious.
104
- def validate_options(options = {})
105
- check_dependent_not_allowed!(options)
106
- check_inverse_not_allowed!(options)
107
- end
108
- end
109
- end
110
- end
111
- end
@@ -1,35 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Associations #:nodoc:
4
- module ForeignKey #:nodoc:
5
- extend ActiveSupport::Concern
6
-
7
- module ClassMethods #:nodoc:
8
- # Determine the value for the foreign key constriant field in the
9
- # database, based on the type of association or if the actual value was
10
- # supplied as an option.
11
- #
12
- # Example:
13
- #
14
- # <tt>contraint(:posts, {}, :references_one)</tt>
15
- #
16
- # Returns
17
- #
18
- # A +String+ for the foreign key field.
19
- def constraint(name, options, association)
20
- key = options[:foreign_key]
21
-
22
- # Always return the supplied foreign_key option if it was supplied -
23
- # the user should always be ble to override.
24
- return key.to_s if key
25
-
26
- case association
27
- when :one, :many then self.name.foreign_key
28
- when :many_as_array then "#{name.to_s.singularize}_ids"
29
- else name.to_s.foreign_key
30
- end
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,38 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Associations #:nodoc:
4
- # This class contains metadata about association proxies.
5
- class MetaData
6
-
7
- attr_reader :association, :options
8
-
9
- delegate :macro, :to => :association
10
-
11
- # Delegate all methods on +Options+ to the options instance.
12
- Associations::Options.public_instance_methods(false).each do |name|
13
- define_method(name) { |*args| @options.send(name) }
14
- end
15
-
16
- # Return true if this meta data is for an embedded association.
17
- #
18
- # Example:
19
- #
20
- # <tt>metadata.embedded?</tt>
21
- def embedded?
22
- [ EmbedsOne, EmbedsMany ].include?(association)
23
- end
24
-
25
- # Create the new associations MetaData object, which holds the type of
26
- # the association and its options, with convenience methods for getting
27
- # that information.
28
- #
29
- # Options:
30
- #
31
- # association: The association type as a class instance.
32
- # options: The association options
33
- def initialize(association, options)
34
- @association, @options = association, options
35
- end
36
- end
37
- end
38
- end
@@ -1,78 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Associations #:nodoc:
4
- class Options < Hash #:nodoc:
5
-
6
- # Create the new +Options+ object, which provides convenience methods for
7
- # accessing values out of an options +Hash+.
8
- def initialize(attributes = {})
9
- self.merge!(attributes)
10
- end
11
-
12
- # For relational associations we want to know if we cascade deletes or
13
- # destroys to associations.
14
- def dependent
15
- self[:dependent]
16
- end
17
-
18
- # Returns the extension if it exists, nil if not.
19
- def extension
20
- self[:extend]
21
- end
22
-
23
- # Returns true is the options have extensions.
24
- def extension?
25
- !extension.nil?
26
- end
27
-
28
- # Return the foreign key if it exists, otherwise inflect it from the
29
- # associated class name.
30
- def foreign_key
31
- key = self[:foreign_key] || klass.name.to_s.foreign_key
32
- key.to_s
33
- end
34
-
35
- # Returns whether the foreign key column is indexed.
36
- def index
37
- self[:index] || false
38
- end
39
-
40
- # Returns the name of the inverse_of association
41
- def inverse_of
42
- self[:inverse_of]
43
- end
44
-
45
- # Return a +Class+ for the options. See #class_name
46
- def klass
47
- class_name.constantize
48
- end
49
-
50
- # Return a +String+ representing the associated class_name. If a class_name
51
- # was provided, then the constantized class_name will be returned. If not,
52
- # a constant based on the association name will be returned.
53
- def class_name
54
- self[:class_name] || name.to_s.classify
55
- end
56
-
57
- # Returns the association name of the options.
58
- def name
59
- self[:name].to_s
60
- end
61
-
62
- # Returns whether or not this association is polymorphic.
63
- def polymorphic
64
- self[:polymorphic] == true
65
- end
66
-
67
- # Used with references_many to save as array of ids.
68
- def stored_as
69
- self[:stored_as]
70
- end
71
-
72
- # Used with references_many to define a default sorting order
73
- def default_order
74
- self[:default_order]
75
- end
76
- end
77
- end
78
- end
@@ -1,60 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc
3
- module Associations #:nodoc
4
- class Proxy #:nodoc
5
- instance_methods.each do |method|
6
- undef_method(method) unless method =~ /(^__|^send$|^object_id$|^extend$)/
7
- end
8
- attr_reader \
9
- :options,
10
- :target
11
-
12
- # Default behavior of method missing should be to delegate all calls
13
- # to the target of the proxy. This can be overridden in special cases.
14
- def method_missing(name, *args, &block)
15
- @target.send(name, *args, &block)
16
- end
17
-
18
- # If anonymous extensions are added this will take care of them.
19
- def extends(options)
20
- extend Module.new(&options.extension) if options.extension?
21
- end
22
-
23
- # Sets up the parent, klass, foreign_key, options
24
- def setup(document, options)
25
- @parent = document
26
- @klass = options.klass
27
- @options = options
28
- @foreign_key = options.foreign_key
29
- extends(options)
30
- end
31
-
32
- protected
33
- class << self
34
- def check_dependent_not_allowed!(options)
35
- if options.has_key?(:dependent)
36
- raise Errors::InvalidOptions.new(
37
- "dependent_only_references_one_or_many", {}
38
- )
39
- end
40
- end
41
-
42
- def check_inverse_not_allowed!(options)
43
- if options.has_key?(:inverse_of)
44
- raise Errors::InvalidOptions.new(
45
- "association_cant_have_inverse_of", {}
46
- )
47
- end
48
- end
49
-
50
- def check_inverse_must_be_defined!(options)
51
- unless options.has_key?(:inverse_of)
52
- raise Errors::InvalidOptions.new(
53
- "embedded_in_must_have_inverse_of", {}
54
- )
55
- end
56
- end
57
- end
58
- end
59
- end
60
- end
@@ -1,70 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Associations #:nodoc:
4
- # Represents a relational association to a "parent" object.
5
- class ReferencedIn < Proxy
6
-
7
- # Initializing a related association only requires looking up the object
8
- # by its id.
9
- #
10
- # Options:
11
- #
12
- # document: The +Document+ that contains the relationship.
13
- # options: The association +Options+.
14
- def initialize(document, options, target = nil)
15
- @options = options
16
-
17
- if target
18
- replace(target)
19
- else
20
- foreign_key = document.send(options.foreign_key)
21
- replace(options.klass.find(foreign_key)) unless foreign_key.blank?
22
- end
23
-
24
- extends(options)
25
- end
26
-
27
- # Replaces the target with a new object
28
- #
29
- # Returns the association proxy
30
- def replace(obj)
31
- @target = obj
32
- self
33
- end
34
-
35
- class << self
36
- # Returns the macro used to create the association.
37
- def macro
38
- :referenced_in
39
- end
40
-
41
- # Perform an update of the relationship of the parent and child. This
42
- # will assimilate the child +Document+ into the parent's object graph.
43
- #
44
- # Options:
45
- #
46
- # target: The target(parent) object
47
- # document: The +Document+ to update.
48
- # options: The association +Options+
49
- #
50
- # Example:
51
- #
52
- # <tt>ReferencedIn.update(person, game, options)</tt>
53
- def update(target, document, options)
54
- document.send("#{options.foreign_key}=", target ? target.id : nil)
55
- new(document, options, target)
56
- end
57
-
58
- # Validate the options passed to the referenced in macro, to encapsulate
59
- # the behavior in this class instead of the associations module.
60
- #
61
- # Options:
62
- #
63
- # options: Thank you captain obvious.
64
- def validate_options(options = {})
65
- check_dependent_not_allowed!(options)
66
- end
67
- end
68
- end
69
- end
70
- end
@@ -1,254 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Associations #:nodoc:
4
- # Represents an relational one-to-many association with an object in a
5
- # separate collection or database.
6
- class ReferencesMany < Proxy
7
-
8
- # Appends the object to the +Array+, setting its parent in
9
- # the process.
10
- def <<(*objects)
11
- load_target
12
- objects.flatten.each do |object|
13
- object.write_attribute(@foreign_key, @parent.id)
14
- @target << object
15
- object.save unless @parent.new_record?
16
- end
17
- end
18
-
19
- alias :concat :<<
20
- alias :push :<<
21
-
22
- # Builds a new Document and adds it to the association collection. The
23
- # document created will be of the same class as the others in the
24
- # association, and the attributes will be passed into the constructor.
25
- #
26
- # Returns the newly created object.
27
- def build(attributes = {},type = nil)
28
- load_target
29
- name = determine_name
30
- object = (type || @klass).instantiate(attributes)
31
- object.send("#{name}=", @parent)
32
- @target << object
33
- object
34
- end
35
-
36
- # Creates a new Document and adds it to the association collection. The
37
- # document created will be of the same class as the others in the
38
- # association, and the attributes will be passed into the constructor and
39
- # the new object will then be saved.
40
- #
41
- # Returns the newly created object.
42
- def create(attributes = nil,type = nil)
43
- build(attributes,type).tap(&:save)
44
- end
45
-
46
- # Creates a new Document and adds it to the association collection. If
47
- # validation fails an error is raised.
48
- #
49
- # Returns the newly created object.
50
- def create!(attributes = nil,type = nil)
51
- build(attributes,type).tap(&:save!)
52
- end
53
-
54
- # Delete all the associated objects.
55
- #
56
- # Example:
57
- #
58
- # <tt>person.posts.delete_all</tt>
59
- #
60
- # Returns:
61
- #
62
- # The number of objects deleted.
63
- def delete_all(conditions = {})
64
- remove(:delete_all, conditions[:conditions])
65
- end
66
-
67
- # Destroy all the associated objects.
68
- #
69
- # Example:
70
- #
71
- # <tt>person.posts.destroy_all</tt>
72
- #
73
- # Returns:
74
- #
75
- # The number of objects destroyed.
76
- def destroy_all(conditions = {})
77
- remove(:destroy_all, conditions[:conditions])
78
- end
79
-
80
- # Finds a document in this association.
81
- # If an id is passed, will return the document for that id.
82
- def find(id_or_type, options = {})
83
- return self.id_criteria(id_or_type) unless id_or_type.is_a?(Symbol)
84
- options[:conditions] = (options[:conditions] || {}).merge(@foreign_key.to_sym => @parent.id)
85
- @klass.find(id_or_type, options)
86
- end
87
-
88
- # Initializing a related association only requires looking up the objects
89
- # by their ids.
90
- #
91
- # Options:
92
- #
93
- # document: The +Document+ that contains the relationship.
94
- # options: The association +Options+.
95
- def initialize(document, options, target = nil)
96
- setup(document, options)
97
- @target = target || query.call
98
- end
99
-
100
- # Override the default behavior to allow the criteria to get reset on
101
- # each call into the association.
102
- #
103
- # Example:
104
- #
105
- # person.posts.where(:title => "New")
106
- # person.posts # resets the criteria
107
- #
108
- # Returns:
109
- #
110
- # A Criteria object or Array.
111
- def method_missing(name, *args, &block)
112
- @target = query.call unless @target.is_a?(Array)
113
- @target.send(name, *args, &block)
114
- end
115
-
116
- # Used for setting associations via a nested attributes setter from the
117
- # parent +Document+.
118
- #
119
- # Options:
120
- #
121
- # attributes: A +Hash+ of integer keys and +Hash+ values.
122
- #
123
- # Returns:
124
- #
125
- # The newly build target Document.
126
- def nested_build(attributes, options = {})
127
- attributes.each do |index, attrs|
128
- begin
129
- _destroy = Boolean.set(attrs.delete('_destroy'))
130
- document = find(attrs.delete("id"))
131
- if options && options[:allow_destroy] && _destroy
132
- @target.delete(document)
133
- document.destroy
134
- else
135
- document.update_attributes(attrs)
136
- end
137
- rescue Errors::DocumentNotFound
138
- create(attrs)
139
- end
140
- end
141
- end
142
-
143
- protected
144
- # Load the target entries if the parent document is new.
145
- def load_target
146
- @target = @target.entries if @parent.new_record?
147
- end
148
-
149
- def determine_name
150
- @proxy ||= class << self; self; end
151
- @proxy.send(:determine_name, @parent, @options)
152
- end
153
-
154
- # The default query used for retrieving the documents from the database.
155
- # In this case we use the common API between Mongoid, ActiveRecord, and
156
- # DataMapper so we can do one-to-many relationships with data in other
157
- # databases.
158
- #
159
- # Example:
160
- #
161
- # <tt>association.query</tt>
162
- #
163
- # Returns:
164
- #
165
- # A +Criteria+ if a Mongoid association.
166
- # An +Array+ of objects if an ActiveRecord association
167
- # A +Collection+ if a DataMapper association.
168
- def query
169
- @query ||= lambda {
170
- @klass.all(:conditions => { @foreign_key => @parent.id }).tap do |crit|
171
- crit.set_order_by(@options.default_order) if @options.default_order
172
- end
173
- }
174
- end
175
-
176
- # Remove the objects based on conditions.
177
- def remove(method, conditions)
178
- selector = { @foreign_key => @parent.id }.merge(conditions || {})
179
- removed = @klass.send(method, :conditions => selector)
180
- reset; removed
181
- end
182
-
183
- # Reset the memoized association on the parent. This will execute the
184
- # database query again.
185
- #
186
- # Example:
187
- #
188
- # <tt>association.reset</tt>
189
- #
190
- # Returns:
191
- #
192
- # See #query rdoc for return values.
193
- def reset
194
- @parent.send(:reset, @options.name) { query.call }
195
- end
196
-
197
- class << self
198
- # Preferred method for creating the new +ReferencesMany+ association.
199
- #
200
- # Options:
201
- #
202
- # document: The +Document+ that contains the relationship.
203
- # options: The association +Options+.
204
- def instantiate(document, options, target = nil)
205
- new(document, options, target)
206
- end
207
-
208
- # Returns the macro used to create the association.
209
- def macro
210
- :references_many
211
- end
212
-
213
- # Perform an update of the relationship of the parent and child. This
214
- # will assimilate the child +Document+ into the parent's object graph.
215
- #
216
- # Options:
217
- #
218
- # related: The related object
219
- # parent: The parent +Document+ to update.
220
- # options: The association +Options+
221
- #
222
- # Example:
223
- #
224
- # <tt>RelatesToOne.update(game, person, options)</tt>
225
- def update(target, document, options)
226
- name = determine_name(document, options)
227
- target.each { |child| child.send("#{name}=", document) }
228
- instantiate(document, options, target)
229
- end
230
-
231
- protected
232
- def determine_name(document, options)
233
- target = document.class
234
- if (inverse = options.inverse_of) && inverse.is_a?(Array)
235
- inverse = [*inverse].detect { |name| target.respond_to?(name) }
236
- end
237
- if !inverse
238
- association = detect_association(target, options, false)
239
- association = detect_association(target, options, true) if association.blank?
240
- inferred = association.name if association
241
- end
242
- inverse || inferred || target.to_s.underscore
243
- end
244
-
245
- def detect_association(target, options, with_class_name = false)
246
- association = options.klass.associations.values.detect do |metadata|
247
- metadata.options.klass == target &&
248
- (with_class_name ? true : metadata.options[:class_name].nil?)
249
- end
250
- end
251
- end
252
- end
253
- end
254
- end