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,3 +1,8 @@
1
+ = PLEASE READ (03 JAN 2011)
2
+
3
+ USERS CURRENTLY POINTED AT master, PLEASE MOVE TO safe_master UNTIL
4
+ FURTHER NOTICE.
5
+
1
6
  = Overview
2
7
 
3
8
  == About Mongoid
@@ -18,6 +23,9 @@ Mongoid is developed against Ruby 1.8.7, 1.9.1, 1.9.2, and REE.
18
23
  Please see the new Mongoid website for up-to-date documentation:
19
24
  {mongoid.org}[http://mongoid.org]
20
25
 
26
+ = Donating
27
+ * {Support Mongoid at Pledgie}[http://www.pledgie.com/campaigns/7757]
28
+
21
29
  = License
22
30
 
23
31
  Copyright (c) 2009, 2010 Durran Jordan
@@ -0,0 +1,51 @@
1
+ require "bundler"
2
+ Bundler.setup
3
+
4
+ require "rake"
5
+ require "rake/rdoctask"
6
+ require "rspec"
7
+ require "rspec/core/rake_task"
8
+
9
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
10
+ require "mongoid/version"
11
+
12
+ task :gem => :build
13
+ task :build do
14
+ system "gem build mongoid.gemspec"
15
+ end
16
+
17
+ task :install => :build do
18
+ system "sudo gem install mongoid-#{Mongoid::VERSION}.gem"
19
+ end
20
+
21
+ task :release => :build do
22
+ system "git tag -a #{Mongoid::VERSION} -m 'Tagging #{Mongoid::VERSION}'"
23
+ system "git push --tags"
24
+ system "gem push mongoid-#{Mongoid::VERSION}.gem"
25
+ end
26
+
27
+ Rspec::Core::RakeTask.new(:spec) do |spec|
28
+ spec.pattern = "spec/**/*_spec.rb"
29
+ end
30
+
31
+ Rspec::Core::RakeTask.new("spec:unit") do |spec|
32
+ spec.pattern = "spec/unit/**/*_spec.rb"
33
+ end
34
+
35
+ Rspec::Core::RakeTask.new("spec:integration") do |spec|
36
+ spec.pattern = "spec/integration/**/*_spec.rb"
37
+ end
38
+
39
+ Rspec::Core::RakeTask.new('spec:progress') do |spec|
40
+ spec.rspec_opts = %w(--format progress)
41
+ spec.pattern = "spec/**/*_spec.rb"
42
+ end
43
+
44
+ Rake::RDocTask.new do |rdoc|
45
+ rdoc.rdoc_dir = "rdoc"
46
+ rdoc.title = "mongoid #{Mongoid::VERSION}"
47
+ rdoc.rdoc_files.include("README*")
48
+ rdoc.rdoc_files.include("lib/**/*.rb")
49
+ end
50
+
51
+ task :default => :spec
@@ -0,0 +1,39 @@
1
+ nl:
2
+ activemodel:
3
+ errors:
4
+ messages:
5
+ taken: al in gebruik
6
+
7
+ mongoid:
8
+ errors:
9
+ messages:
10
+ document_not_found:
11
+ Document niet gevonden voor class %{klass} met de id(s) %{identifiers}.
12
+ invalid_database:
13
+ Database moet een Mongo::DB zijn, niet een %{name}.
14
+ invalid_type:
15
+ Veld was gedefinieerd als een %{klass}, maar ontvangen als %{other} met
16
+ de waarde %{value}.
17
+ unsupported_version:
18
+ MongoDB %{version} wordt niet ondersteund, upgrade naar versie %{mongo_version}.
19
+ validations:
20
+ Gefaalde validatie - %{errors}.
21
+ invalid_collection:
22
+ Toegang tot de collectie voor %{klass} is niet toegestaan aangezien het
23
+ een embedded document is, benader de collectie van een root document.
24
+ invalid_field:
25
+ Het is niet toegestaan om een veld genaamd %{name} te definiëren.
26
+ Definieer geen velden die conflicteren met de Mongoid interne attributen of methode namen.
27
+ Gerbuik Document#instance_methods om welke namen dit gaat.
28
+ too_many_nested_attribute_records:
29
+ Het accepteren van nested attributes voor %{association} is gelimiteerd tot %{limit} records.
30
+ embedded_in_must_have_inverse_of:
31
+ Opties voor embedded_in association moet gebruik maken van de inverse_of optie.
32
+ dependent_only_references_one_or_many:
33
+ De dependent => destroy|delete optie die was meegegeven is alleen geldig
34
+ op references_one en references_many associations.
35
+ association_cant_have_inverse_of:
36
+ Het definieren van een inverse_of op deze association is niet toegestaan. Gebruik
37
+ alleen deze optie met embedded_in en references_many as array.
38
+ calling_document_find_with_nil_is_invalid:
39
+ Het is niet toegestaan om Document#find aan te roepen met de waarde nil.
@@ -1,4 +1,4 @@
1
- en:
1
+ ro:
2
2
  activemodel:
3
3
  errors:
4
4
  messages:
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+
2
3
  # Copyright (c) 2009, 2010 Durran Jordan
3
4
  #
4
5
  # Permission is hereby granted, free of charge, to any person obtaining
@@ -19,9 +20,7 @@
19
20
  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
-
23
23
  require "delegate"
24
- require "singleton"
25
24
  require "time"
26
25
  require "ostruct"
27
26
  require "active_support/core_ext"
@@ -43,7 +42,6 @@ require "mongo"
43
42
  require "mongoid/errors"
44
43
  require "mongoid/extensions"
45
44
  require "mongoid/safe"
46
- require "mongoid/associations"
47
45
  require "mongoid/atomicity"
48
46
  require "mongoid/attributes"
49
47
  require "mongoid/callbacks"
@@ -51,9 +49,11 @@ require "mongoid/collection"
51
49
  require "mongoid/collections"
52
50
  require "mongoid/config"
53
51
  require "mongoid/contexts"
52
+ require "mongoid/copyable"
54
53
  require "mongoid/criteria"
55
54
  require "mongoid/cursor"
56
55
  require "mongoid/deprecation"
56
+ require "mongoid/default_scope"
57
57
  require "mongoid/dirty"
58
58
  require "mongoid/extras"
59
59
  require "mongoid/factory"
@@ -63,17 +63,20 @@ require "mongoid/finders"
63
63
  require "mongoid/hierarchy"
64
64
  require "mongoid/identity"
65
65
  require "mongoid/indexes"
66
+ require "mongoid/inspection"
66
67
  require "mongoid/javascript"
67
68
  require "mongoid/json"
68
69
  require "mongoid/keys"
69
70
  require "mongoid/logger"
70
71
  require "mongoid/matchers"
71
- require "mongoid/memoization"
72
72
  require "mongoid/modifiers"
73
73
  require "mongoid/multi_parameter_attributes"
74
+ require "mongoid/multi_database"
74
75
  require "mongoid/named_scope"
76
+ require "mongoid/nested_attributes"
75
77
  require "mongoid/paths"
76
78
  require "mongoid/persistence"
79
+ require "mongoid/relations"
77
80
  require "mongoid/safety"
78
81
  require "mongoid/scope"
79
82
  require "mongoid/state"
@@ -100,7 +103,7 @@ module Mongoid #:nodoc
100
103
 
101
104
  # Sets the Mongoid configuration options. Best used by passing a block.
102
105
  #
103
- # Example:
106
+ # @example Set up configuration options.
104
107
  #
105
108
  # Mongoid.configure do |config|
106
109
  # name = "mongoid_test"
@@ -113,33 +116,30 @@ module Mongoid #:nodoc
113
116
  # ]
114
117
  # end
115
118
  #
116
- # Returns:
117
- #
118
- # The Mongoid +Config+ singleton instance.
119
+ # @return [ Config ] The configuration obejct.
119
120
  def configure
120
- config = Mongoid::Config.instance
121
+ config = Mongoid::Config
121
122
  block_given? ? yield(config) : config
122
123
  end
124
+ alias :config :configure
123
125
 
124
126
  # Easy convenience method for generating an alert from the
125
127
  # deprecation module.
126
128
  #
127
- # Example:
129
+ # @example Alert a deprecation.
130
+ # Mongoid.deprecate("Method no longer used")
128
131
  #
129
- # <tt>Mongoid.deprecate("Method no longer used")</tt>
132
+ # @param [ String ] message The message to print.
130
133
  def deprecate(message)
131
- Mongoid::Deprecation.instance.alert(message)
134
+ Mongoid::Deprecation.alert(message)
132
135
  end
133
-
134
- alias :config :configure
135
136
  end
136
137
 
137
138
  # Take all the public instance methods from the Config singleton and allow
138
139
  # them to be accessed through the Mongoid module directly.
139
140
  #
140
- # Example:
141
- #
142
- # <tt>Mongoid.database = Mongo::Connection.new.db("test")</tt>
141
+ # @example Delegate the configuration methods.
142
+ # Mongoid.database = Mongo::Connection.new.db("test")
143
143
  Mongoid::Config.public_instance_methods(false).each do |name|
144
144
  (class << self; self; end).class_eval <<-EOT
145
145
  def #{name}(*args)
@@ -1,46 +1,52 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
- module Atomicity #:nodoc:
3
+
4
+ # This module contains the logic for supporting atomic operations against the
5
+ # database.
6
+ #
7
+ # @todo Durran: Refactor class out into separate objects for each type of
8
+ # update.
9
+ module Atomicity
4
10
  extend ActiveSupport::Concern
5
11
 
6
12
  # Get all the atomic updates that need to happen for the current
7
13
  # +Document+. This includes all changes that need to happen in the
8
14
  # entire hierarchy that exists below where the save call was made.
9
15
  #
10
- # Example:
11
- #
12
- # <tt>person.save</tt> # Saves entire tree
16
+ # @note
17
+ # MongoDB does not allow "conflicting modifications" to be
18
+ # performed in a single operation. Conflicting modifications are
19
+ # detected by the 'haveConflictingMod' function in MongoDB.
20
+ # Examination of the code suggests that two modifications (a $set
21
+ # and a $pushAll, for example) conflict if:
22
+ # (1) the key paths being modified are equal.
23
+ # (2) one key path is a prefix of the other.
24
+ # So a $set of 'addresses.0.street' will conflict with a $pushAll
25
+ # to 'addresses', and we will need to split our update into two
26
+ # pieces. We do not, however, attempt to match MongoDB's logic
27
+ # exactly. Instead, we assume that two updates conflict if the
28
+ # first component of the two key paths matches.
13
29
  #
14
- # Returns:
30
+ # @example Get the updates that need to occur.
31
+ # person._updates
15
32
  #
16
- # A +Hash+ of all atomic updates that need to occur.
33
+ # @return [ Hash ] The updates and their modifiers.
17
34
  def _updates
18
35
  processed = {}
19
-
36
+
20
37
  _children.inject({ "$set" => _sets, "$pushAll" => {}, :other => {} }) do |updates, child|
21
38
  changes = child._sets
22
39
  updates["$set"].update(changes)
23
40
  unless changes.empty?
24
41
  processed[child._conficting_modification_key] = true
25
42
  end
26
-
27
- # MongoDB does not allow "conflicting modifications" to be
28
- # performed in a single operation. Conflicting modifications are
29
- # detected by the 'haveConflictingMod' function in MongoDB.
30
- # Examination of the code suggests that two modifications (a $set
31
- # and a $pushAll, for example) conflict if (1) the key paths being
32
- # modified are equal or (2) one key path is a prefix of the other.
33
- # So a $set of 'addresses.0.street' will conflict with a $pushAll
34
- # to 'addresses', and we will need to split our update into two
35
- # pieces. We do not, however, attempt to match MongoDB's logic
36
- # exactly. Instead, we assume that two updates conflict if the
37
- # first component of the two key paths matches.
43
+
38
44
  if processed.has_key?(child._conficting_modification_key)
39
45
  target = :other
40
46
  else
41
47
  target = "$pushAll"
42
48
  end
43
-
49
+
44
50
  child._pushes.each do |attr, val|
45
51
  if updates[target].has_key?(attr)
46
52
  updates[target][attr] << val
@@ -55,24 +61,50 @@ module Mongoid #:nodoc:
55
61
  end
56
62
 
57
63
  protected
64
+
58
65
  # Get the key used to check for conflicting modifications. For now, we
59
66
  # just use the first component of _path, and discard the first period
60
67
  # and everything that follows.
68
+ #
69
+ # @example Get the key.
70
+ # person._conflicting_modification_key
71
+ #
72
+ # @return [ String ] The conflicting key.
61
73
  def _conficting_modification_key
62
74
  _path.sub(/\..*/, '')
63
75
  end
64
76
 
65
77
  # Get all the push attributes that need to occur.
78
+ #
79
+ # @example Get the pushes.
80
+ # person._pushes
81
+ #
82
+ # @return [ Hash ] The $pushAll operations.
66
83
  def _pushes
67
- (new_record? && embedded_many? && !_parent.new_record?) ? { _path => raw_attributes } : {}
84
+ pushable? ? { _path => to_hash } : {}
85
+ end
86
+
87
+ # Determine if the document can be pushed.
88
+ #
89
+ # @example Is this pushable?
90
+ # person.pushable?
91
+ #
92
+ # @return [ true, false ] Is the document new and embedded?
93
+ def pushable?
94
+ new_record? && embedded_many? && _parent.persisted?
68
95
  end
69
96
 
70
97
  # Get all the attributes that need to be set.
98
+ #
99
+ # @example Get the sets.
100
+ # person._sets
101
+ #
102
+ # @return [ Hash ] The $set operations.
71
103
  def _sets
72
104
  if changed? && !new_record?
73
105
  setters
74
106
  else
75
- embedded_one? && new_record? ? { _path => raw_attributes } : {}
107
+ embedded_one? && new_record? ? { _path => to_hash } : {}
76
108
  end
77
109
  end
78
110
  end
@@ -1,23 +1,75 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
+
4
+ # This module contains the logic for handling the internal attributes hash,
5
+ # and how to get and set values.
3
6
  module Attributes
4
- extend ActiveSupport::Concern
7
+
8
+ # Returns the object type. This corresponds to the name of the class that
9
+ # this document is, which is used in determining the class to
10
+ # instantiate in various cases.
11
+ #
12
+ # @example Get the type.
13
+ # person._type
14
+ #
15
+ # @return [ String ] The name of the class the document is.
16
+ def _type
17
+ @attributes["_type"]
18
+ end
19
+
20
+ # Set the type of the document. This should be the name of the class.
21
+ #
22
+ # @example Set the type
23
+ # person._type = "Person"
24
+ #
25
+ # @param [ String ] new_type The name of the class.
26
+ #
27
+ # @return [ String ] the new type.
28
+ def _type=(new_type)
29
+ @attributes["_type"] = new_type
30
+ end
31
+
32
+ # Determine if an attribute is present.
33
+ #
34
+ # @example Is the attribute present?
35
+ # person.attribute_present?("title")
36
+ #
37
+ # @param [ String, Symbol ] name The name of the attribute.
38
+ #
39
+ # @return [ true, false ] True if present, false if not.
40
+ def attribute_present?(name)
41
+ !read_attribute(name).blank?
42
+ end
5
43
 
6
44
  # Get the id associated with this object. This will pull the _id value out
7
- # of the attributes +Hash+.
45
+ # of the attributes.
46
+ #
47
+ # @example Get the id.
48
+ # person.id
49
+ #
50
+ # @return [ BSON::ObjectId, String ] The id of the document.
8
51
  def id
9
52
  @attributes["_id"]
10
53
  end
54
+ alias :_id :id
11
55
 
12
- # Set the id of the +Document+ to a new one.
56
+ # Set the id of the document to a new one.
57
+ #
58
+ # @example Set the id.
59
+ # person.id = BSON::ObjectId.new
60
+ #
61
+ # @param [ BSON::ObjectId, String ] new_id The new id.
62
+ #
63
+ # @return [ BSON::ObjectId, String ] The new id.
13
64
  def id=(new_id)
14
65
  @attributes["_id"] = new_id
15
66
  end
16
-
17
- alias :_id :id
18
67
  alias :_id= :id=
19
68
 
20
69
  # Used for allowing accessor methods for dynamic attributes.
70
+ #
71
+ # @param [ String, Symbol ] name The name of the method.
72
+ # @param [ Array ] *args The arguments to the method.
21
73
  def method_missing(name, *args)
22
74
  attr = name.to_s
23
75
  return super unless @attributes.has_key?(attr.reader)
@@ -29,44 +81,42 @@ module Mongoid #:nodoc:
29
81
  end
30
82
  end
31
83
 
32
- # Override respond_to? so it responds properly for dynamic attributes
33
- def respond_to?(*args)
34
- (Mongoid.allow_dynamic_fields && @attributes && @attributes.has_key?(args.first.to_s)) || super
35
- end
36
-
37
84
  # Process the provided attributes casting them to their proper values if a
38
- # field exists for them on the +Document+. This will be limited to only the
85
+ # field exists for them on the document. This will be limited to only the
39
86
  # attributes provided in the suppied +Hash+ so that no extra nil values get
40
87
  # put into the document's attributes.
88
+ #
89
+ # @example Process the attributes.
90
+ # person.process(:title => "sir", :age => 40)
91
+ #
92
+ # @param [ Hash ] attrs The attributes to set.
41
93
  def process(attrs = nil)
94
+ pending = {}
42
95
  sanitize_for_mass_assignment(attrs || {}).each_pair do |key, value|
43
96
  if set_allowed?(key)
44
97
  write_attribute(key, value)
45
98
  else
46
- if associations.include?(key.to_s) and associations[key.to_s].embedded? and value.is_a?(Hash)
47
- if association = send(key)
48
- association.nested_build(value)
49
- else
50
- send("build_#{key}", value)
51
- end
52
- else
53
- send("#{key}=", value)
54
- end
99
+ pending[key.to_s] = value and next if relations.has_key?(key.to_s)
100
+ send("#{key}=", value)
55
101
  end
56
102
  end
103
+ yield self if block_given?
104
+ process_relations(pending)
57
105
  setup_modifications
58
106
  end
59
107
 
60
- # Read a value from the +Document+ attributes. If the value does not exist
108
+ # Read a value from the document attributes. If the value does not exist
61
109
  # it will return nil.
62
110
  #
63
- # Options:
111
+ # @example Read an attribute.
112
+ # person.read_attribute(:title)
64
113
  #
65
- # name: The name of the attribute to get.
114
+ # @example Read an attribute (alternate syntax.)
115
+ # person[:title]
66
116
  #
67
- # Example:
117
+ # @param [ String, Symbol ] name The name of the attribute to get.
68
118
  #
69
- # <tt>person.read_attribute(:title)</tt>
119
+ # @return [ Object ] The value of the attribute.
70
120
  def read_attribute(name)
71
121
  access = name.to_s
72
122
  value = @attributes[access]
@@ -78,93 +128,75 @@ module Mongoid #:nodoc:
78
128
  # Remove a value from the +Document+ attributes. If the value does not exist
79
129
  # it will fail gracefully.
80
130
  #
81
- # Options:
82
- #
83
- # name: The name of the attribute to remove.
131
+ # @example Remove the attribute.
132
+ # person.remove_attribute(:title)
84
133
  #
85
- # Example:
86
- #
87
- # <tt>person.remove_attribute(:title)</tt>
134
+ # @param [ String, Symbol ] name The name of the attribute to remove.
88
135
  def remove_attribute(name)
89
136
  access = name.to_s
90
137
  modify(access, @attributes.delete(name.to_s), nil)
91
138
  end
92
139
 
93
- # Returns true when attribute is present.
140
+ # Override respond_to? so it responds properly for dynamic attributes.
94
141
  #
95
- # Options:
142
+ # @example Does this object respond to the method?
143
+ # person.respond_to?(:title)
96
144
  #
97
- # name: The name of the attribute to request presence on.
98
- def attribute_present?(name)
99
- value = read_attribute(name)
100
- !value.blank?
101
- end
102
-
103
- # Returns the object type. This corresponds to the name of the class that
104
- # this +Document+ is, which is used in determining the class to
105
- # instantiate in various cases.
106
- def _type
107
- @attributes["_type"]
108
- end
109
-
110
- # Set the type of the +Document+. This should be the name of the class.
111
- def _type=(new_type)
112
- @attributes["_type"] = new_type
145
+ # @param [ Array ] *args The name of the method.
146
+ #
147
+ # @return [ true, false ] True if it does, false if not.
148
+ def respond_to?(*args)
149
+ (Mongoid.allow_dynamic_fields &&
150
+ @attributes &&
151
+ @attributes.has_key?(args.first.to_s)
152
+ ) || super
113
153
  end
114
154
 
115
- # Write a single attribute to the +Document+ attribute +Hash+. This will
155
+ # Write a single attribute to the document attribute hash. This will
116
156
  # also fire the before and after update callbacks, and perform any
117
157
  # necessary typecasting.
118
158
  #
119
- # Options:
120
- #
121
- # name: The name of the attribute to update.
122
- # value: The value to set for the attribute.
123
- #
124
- # Example:
159
+ # @example Write the attribute.
160
+ # person.write_attribute(:title, "Mr.")
125
161
  #
126
- # <tt>person.write_attribute(:title, "Mr.")</tt>
162
+ # @example Write the attribute (alternate syntax.)
163
+ # person[:title] = "Mr."
127
164
  #
128
- # This will also cause the observing +Document+ to notify it's parent if
129
- # there is any.
165
+ # @param [ String, Symbol ] name The name of the attribute to update.
166
+ # @param [ Object ] value The value to set for the attribute.
130
167
  def write_attribute(name, value)
131
168
  access = name.to_s
132
169
  modify(access, @attributes[access], typed_value_for(access, value))
133
- notify if !id.blank? && new_record?
134
170
  end
135
171
  alias :[]= :write_attribute
136
172
 
137
- # Writes the supplied attributes +Hash+ to the +Document+. This will only
173
+ # Writes the supplied attributes hash to the document. This will only
138
174
  # overwrite existing attributes if they are present in the new +Hash+, all
139
175
  # others will be preserved.
140
176
  #
141
- # Options:
177
+ # @example Write the attributes.
178
+ # person.write_attributes(:title => "Mr.")
142
179
  #
143
- # attrs: The +Hash+ of new attributes to set on the +Document+
180
+ # @example Write the attributes (alternate syntax.)
181
+ # person.attributes = { :title => "Mr." }
144
182
  #
145
- # Example:
146
- #
147
- # <tt>person.write_attributes(:title => "Mr.")</tt>
148
- #
149
- # This will also cause the observing +Document+ to notify it's parent if
150
- # there is any.
183
+ # @param [ Hash ] attrs The new attributes to set.
151
184
  def write_attributes(attrs = nil)
152
185
  process(attrs || {})
153
- identified = !id.blank?
154
- if new_record? && !identified
155
- identify; notify
186
+ if new_record? && id.blank?
187
+ identify
156
188
  end
157
189
  end
158
190
  alias :attributes= :write_attributes
159
191
 
160
192
  protected
161
193
 
162
- # Return the typecast value for a field.
163
- def typed_value_for(key, value)
164
- fields.has_key?(key) ? fields[key].set(value) : value
165
- end
166
-
167
- # apply default values to attributes - calling procs as required
194
+ # Get the default values for the attributes.
195
+ #
196
+ # @example Get the defaults.
197
+ # person.default_attributes
198
+ #
199
+ # @return [ Hash ] The default values for each field.
168
200
  def default_attributes
169
201
  default_values = defaults
170
202
  default_values.each_pair do |key, val|
@@ -173,59 +205,47 @@ module Mongoid #:nodoc:
173
205
  default_values || {}
174
206
  end
175
207
 
176
- # Return true if dynamic field setting is enabled.
177
- def set_allowed?(key)
178
- Mongoid.allow_dynamic_fields && !respond_to?("#{key}=")
179
- end
180
-
181
- # Used when supplying a :reject_if block as an option to
182
- # accepts_nested_attributes_for
183
- def reject(attributes, options)
184
- rejector = options[:reject_if]
185
- if rejector
186
- attributes.delete_if do |key, value|
187
- rejector.call(value)
208
+ # Process all the pending relations that needed to wait until ids were set
209
+ # to fire off.
210
+ #
211
+ # @example Process the relations.
212
+ # document.process_relations({ "addressable" => person })
213
+ #
214
+ # @param [ Hash ] pending The pending relation values.
215
+ def process_relations(pending)
216
+ pending.each_pair do |name, value|
217
+ metadata = relations[name]
218
+ if value.is_a?(Hash)
219
+ metadata.nested_builder(value, {}).build(self)
220
+ else
221
+ send("#{name}=", value)
188
222
  end
189
223
  end
190
224
  end
191
225
 
192
- # Used when supplying a :limit as an option to accepts_nested_attributes_for
193
- def limit(attributes, name, options)
194
- if options[:limit] && attributes.size > options[:limit]
195
- raise Mongoid::Errors::TooManyNestedAttributeRecords.new(name, options[:limit])
196
- end
226
+ # Return true if dynamic field setting is enabled.
227
+ #
228
+ # @example Is a set allowed for this name?
229
+ # person.set_allowed?(:title)
230
+ #
231
+ # @param [ String, Symbol ] key The name of the field.
232
+ #
233
+ # @return [ true, false ] True if allowed, false if not.
234
+ def set_allowed?(key)
235
+ Mongoid.allow_dynamic_fields && !respond_to?("#{key}=")
197
236
  end
198
237
 
199
- module ClassMethods
200
- # Defines attribute setters for the associations specified by the names.
201
- # This will work for a has one or has many association.
202
- #
203
- # Example:
204
- #
205
- # class Person
206
- # include Mongoid::Document
207
- # embeds_one :name
208
- # embeds_many :addresses
209
- #
210
- # accepts_nested_attributes_for :name, :addresses
211
- # end
212
- def accepts_nested_attributes_for(*args)
213
- associations = args.flatten
214
- options = associations.last.is_a?(Hash) ? associations.pop : {}
215
- associations.each do |name|
216
- define_method("#{name}_attributes=") do |attrs|
217
- reject(attrs, options)
218
- limit(attrs, name, options)
219
- association = send(name)
220
- if association
221
- # observe(association, true)
222
- association.nested_build(attrs, options)
223
- else
224
- send("build_#{name}", attrs, options)
225
- end
226
- end
227
- end
228
- end
238
+ # Return the typecasted value for a field.
239
+ #
240
+ # @example Get the value typecasted.
241
+ # person.typed_value_for(:title, :sir)
242
+ #
243
+ # @param [ String, Symbol ] key The field name.
244
+ # @param [ Object ] value The uncast value.
245
+ #
246
+ # @return [ Object ] The cast value.
247
+ def typed_value_for(key, value)
248
+ fields.has_key?(key) ? fields[key].set(value) : value
229
249
  end
230
250
  end
231
251
  end