mongo_mapper 0.7.6 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. data/README.rdoc +4 -8
  2. data/bin/mmconsole +1 -1
  3. data/examples/keys.rb +37 -0
  4. data/examples/plugins.rb +41 -0
  5. data/examples/querying.rb +35 -0
  6. data/examples/scopes.rb +52 -0
  7. data/lib/mongo_mapper.rb +77 -97
  8. data/lib/mongo_mapper/connection.rb +83 -0
  9. data/lib/mongo_mapper/document.rb +10 -252
  10. data/lib/mongo_mapper/embedded_document.rb +7 -46
  11. data/lib/mongo_mapper/exceptions.rb +30 -0
  12. data/lib/mongo_mapper/extensions/array.rb +19 -0
  13. data/lib/mongo_mapper/extensions/binary.rb +22 -0
  14. data/lib/mongo_mapper/extensions/boolean.rb +44 -0
  15. data/lib/mongo_mapper/extensions/date.rb +25 -0
  16. data/lib/mongo_mapper/extensions/float.rb +14 -0
  17. data/lib/mongo_mapper/extensions/hash.rb +14 -0
  18. data/lib/mongo_mapper/extensions/integer.rb +19 -0
  19. data/lib/mongo_mapper/extensions/kernel.rb +9 -0
  20. data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
  21. data/lib/mongo_mapper/extensions/object.rb +27 -0
  22. data/lib/mongo_mapper/extensions/object_id.rb +30 -0
  23. data/lib/mongo_mapper/extensions/set.rb +20 -0
  24. data/lib/mongo_mapper/extensions/string.rb +18 -0
  25. data/lib/mongo_mapper/extensions/time.rb +29 -0
  26. data/lib/mongo_mapper/plugins.rb +1 -21
  27. data/lib/mongo_mapper/plugins/accessible.rb +44 -0
  28. data/lib/mongo_mapper/plugins/associations.rb +7 -24
  29. data/lib/mongo_mapper/plugins/associations/base.rb +1 -0
  30. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +5 -6
  31. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +5 -6
  32. data/lib/mongo_mapper/plugins/associations/collection.rb +1 -0
  33. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +2 -1
  34. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +22 -39
  35. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +4 -4
  36. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +22 -23
  37. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +1 -0
  38. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +1 -0
  39. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +1 -0
  40. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +2 -3
  41. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +6 -7
  42. data/lib/mongo_mapper/plugins/associations/proxy.rb +8 -6
  43. data/lib/mongo_mapper/plugins/caching.rb +21 -0
  44. data/lib/mongo_mapper/plugins/callbacks.rb +4 -3
  45. data/lib/mongo_mapper/plugins/clone.rb +10 -4
  46. data/lib/mongo_mapper/plugins/descendants.rb +1 -0
  47. data/lib/mongo_mapper/plugins/dirty.rb +1 -0
  48. data/lib/mongo_mapper/plugins/document.rb +41 -0
  49. data/lib/mongo_mapper/plugins/dynamic_querying.rb +41 -0
  50. data/lib/mongo_mapper/{support/find.rb → plugins/dynamic_querying/dynamic_finder.rb} +3 -36
  51. data/lib/mongo_mapper/plugins/embedded_document.rb +49 -0
  52. data/lib/mongo_mapper/plugins/equality.rb +3 -9
  53. data/lib/mongo_mapper/plugins/identity_map.rb +8 -10
  54. data/lib/mongo_mapper/plugins/indexes.rb +12 -0
  55. data/lib/mongo_mapper/plugins/inspect.rb +1 -0
  56. data/lib/mongo_mapper/plugins/keys.rb +15 -27
  57. data/lib/mongo_mapper/plugins/keys/key.rb +14 -3
  58. data/lib/mongo_mapper/plugins/logger.rb +1 -0
  59. data/lib/mongo_mapper/plugins/modifiers.rb +3 -2
  60. data/lib/mongo_mapper/plugins/pagination.rb +5 -15
  61. data/lib/mongo_mapper/plugins/persistence.rb +12 -11
  62. data/lib/mongo_mapper/plugins/protected.rb +8 -0
  63. data/lib/mongo_mapper/plugins/querying.rb +236 -0
  64. data/lib/mongo_mapper/plugins/querying/decorator.rb +46 -0
  65. data/lib/mongo_mapper/plugins/rails.rb +1 -0
  66. data/lib/mongo_mapper/plugins/safe.rb +28 -0
  67. data/lib/mongo_mapper/plugins/sci.rb +32 -0
  68. data/lib/mongo_mapper/plugins/scopes.rb +21 -0
  69. data/lib/mongo_mapper/plugins/serialization.rb +1 -0
  70. data/lib/mongo_mapper/plugins/timestamps.rb +1 -0
  71. data/lib/mongo_mapper/plugins/userstamps.rb +1 -0
  72. data/lib/mongo_mapper/plugins/validations.rb +5 -1
  73. data/lib/mongo_mapper/support/descendant_appends.rb +5 -6
  74. data/lib/mongo_mapper/version.rb +2 -1
  75. data/test/NOTE_ON_TESTING +1 -0
  76. data/test/active_model_lint_test.rb +13 -0
  77. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
  78. data/test/functional/associations/test_belongs_to_proxy.rb +93 -0
  79. data/test/functional/associations/test_in_array_proxy.rb +319 -0
  80. data/test/functional/associations/test_many_documents_as_proxy.rb +229 -0
  81. data/test/functional/associations/test_many_documents_proxy.rb +536 -0
  82. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +176 -0
  83. data/test/functional/associations/test_many_embedded_proxy.rb +256 -0
  84. data/test/functional/associations/test_many_polymorphic_proxy.rb +302 -0
  85. data/test/functional/associations/test_one_embedded_proxy.rb +58 -0
  86. data/test/functional/associations/test_one_proxy.rb +182 -0
  87. data/test/functional/test_accessible.rb +168 -0
  88. data/test/functional/test_associations.rb +44 -0
  89. data/test/functional/test_binary.rb +27 -0
  90. data/test/functional/test_caching.rb +76 -0
  91. data/test/functional/test_callbacks.rb +151 -0
  92. data/test/functional/test_dirty.rb +163 -0
  93. data/test/functional/test_document.rb +253 -0
  94. data/test/functional/test_dynamic_querying.rb +75 -0
  95. data/test/functional/test_embedded_document.rb +210 -0
  96. data/test/functional/test_identity_map.rb +506 -0
  97. data/test/functional/test_indexes.rb +42 -0
  98. data/test/functional/test_logger.rb +20 -0
  99. data/test/functional/test_modifiers.rb +416 -0
  100. data/test/functional/test_pagination.rb +91 -0
  101. data/test/functional/test_protected.rb +175 -0
  102. data/test/functional/test_querying.rb +873 -0
  103. data/test/functional/test_safe.rb +76 -0
  104. data/test/functional/test_sci.rb +230 -0
  105. data/test/functional/test_scopes.rb +171 -0
  106. data/test/functional/test_string_id_compatibility.rb +67 -0
  107. data/test/functional/test_timestamps.rb +62 -0
  108. data/test/functional/test_userstamps.rb +27 -0
  109. data/test/functional/test_validations.rb +342 -0
  110. data/test/models.rb +227 -0
  111. data/test/test_helper.rb +98 -0
  112. data/test/unit/associations/test_base.rb +212 -0
  113. data/test/unit/associations/test_proxy.rb +105 -0
  114. data/test/unit/serializers/test_json_serializer.rb +202 -0
  115. data/test/unit/test_clone.rb +69 -0
  116. data/test/unit/test_descendant_appends.rb +71 -0
  117. data/test/unit/test_document.rb +213 -0
  118. data/test/unit/test_dynamic_finder.rb +125 -0
  119. data/test/unit/test_embedded_document.rb +644 -0
  120. data/test/unit/test_extensions.rb +380 -0
  121. data/test/unit/test_key.rb +185 -0
  122. data/test/unit/test_keys.rb +55 -0
  123. data/test/unit/test_mongo_mapper.rb +110 -0
  124. data/test/unit/test_pagination.rb +11 -0
  125. data/test/unit/test_plugins.rb +50 -0
  126. data/test/unit/test_rails.rb +181 -0
  127. data/test/unit/test_rails_compatibility.rb +52 -0
  128. data/test/unit/test_serialization.rb +51 -0
  129. data/test/unit/test_time_zones.rb +39 -0
  130. data/test/unit/test_validations.rb +544 -0
  131. metadata +113 -44
  132. data/lib/mongo_mapper/plugins/pagination/proxy.rb +0 -72
  133. data/lib/mongo_mapper/query.rb +0 -23
  134. data/lib/mongo_mapper/support.rb +0 -196
@@ -1,11 +1,17 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
3
  module Plugins
3
4
  module Clone
4
5
  module InstanceMethods
5
- def clone
6
- clone_attributes = self.attributes
7
- clone_attributes.delete("_id")
8
- self.class.new(clone_attributes)
6
+ def initialize_copy(other)
7
+ @_new = true
8
+ @_destroyed = false
9
+ default_id_value({})
10
+ self.attributes = other.attributes.clone.except(:_id).inject({}) do |hash, entry|
11
+ key, value = entry
12
+ hash[key] = value.duplicable? ? value.clone : value
13
+ hash
14
+ end
9
15
  end
10
16
  end
11
17
  end
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
3
  module Plugins
3
4
  module Descendants
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
3
  module Plugins
3
4
  module Dirty
@@ -0,0 +1,41 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Document
5
+ module ClassMethods
6
+ def embeddable?
7
+ false
8
+ end
9
+ end
10
+
11
+ module InstanceMethods
12
+ def new?
13
+ @_new
14
+ end
15
+
16
+ def destroyed?
17
+ @_destroyed == true
18
+ end
19
+
20
+ def reload
21
+ if doc = self.class.query.find_one(:_id => id)
22
+ tap do |instance|
23
+ instance.class.associations.each_key do |association_name|
24
+ send(association_name).reset if respond_to?(association_name)
25
+ end
26
+ instance.attributes = doc
27
+ end
28
+ else
29
+ raise DocumentNotFound, "Document match #{_id.inspect} does not exist in #{collection.name} collection"
30
+ end
31
+ end
32
+
33
+ # Used by embedded docs to find root easily without if/respond_to? stuff.
34
+ # Documents are always root documents.
35
+ def _root_document
36
+ self
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module DynamicQuerying
5
+ module ClassMethods
6
+ def dynamic_find(finder, args)
7
+ attributes = {}
8
+
9
+ finder.attributes.each_with_index do |attr, index|
10
+ attributes[attr] = args[index]
11
+ end
12
+
13
+ options = args.extract_options!.merge(attributes)
14
+
15
+ if result = send(finder.finder, options)
16
+ result
17
+ else
18
+ if finder.raise?
19
+ raise DocumentNotFound, "Couldn't find Document with #{attributes.inspect} in collection named #{collection.name}"
20
+ end
21
+
22
+ if finder.instantiator
23
+ self.send(finder.instantiator, attributes)
24
+ end
25
+ end
26
+ end
27
+
28
+ protected
29
+ def method_missing(method, *args, &block)
30
+ finder = DynamicFinder.new(method)
31
+
32
+ if finder.found?
33
+ dynamic_find(finder, args)
34
+ else
35
+ super
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,29 +1,7 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
- module Support
3
- # @api private
4
- module Find
5
- def dynamic_find(finder, args)
6
- attributes = {}
7
-
8
- finder.attributes.each_with_index do |attr, index|
9
- attributes[attr] = args[index]
10
- end
11
-
12
- options = args.extract_options!.merge(attributes)
13
-
14
- if result = send(finder.finder, options)
15
- result
16
- else
17
- if finder.raise?
18
- raise DocumentNotFound, "Couldn't find Document with #{attributes.inspect} in collection named #{collection.name}"
19
- end
20
-
21
- if finder.instantiator
22
- self.send(finder.instantiator, attributes)
23
- end
24
- end
25
- end
26
-
3
+ module Plugins
4
+ module DynamicQuerying
27
5
  class DynamicFinder
28
6
  attr_reader :method, :attributes, :finder, :bang, :instantiator
29
7
 
@@ -61,17 +39,6 @@ module MongoMapper
61
39
  @attributes = names && names.split('_and_')
62
40
  end
63
41
  end
64
-
65
- protected
66
- def method_missing(method, *args, &block)
67
- finder = DynamicFinder.new(method)
68
-
69
- if finder.found?
70
- dynamic_find(finder, args)
71
- else
72
- super
73
- end
74
- end
75
42
  end
76
43
  end
77
44
  end
@@ -0,0 +1,49 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module EmbeddedDocument
5
+ def self.configure(model)
6
+ model.class_eval do
7
+ attr_reader :_root_document, :_parent_document
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ def embeddable?
13
+ true
14
+ end
15
+
16
+ def embedded_in(owner_name)
17
+ define_method(owner_name) { _parent_document }
18
+ end
19
+ end
20
+
21
+ module InstanceMethods
22
+ def new?
23
+ _root_document.try(:new?) || @_new
24
+ end
25
+
26
+ def destroyed?
27
+ !!_root_document.try(:destroyed?)
28
+ end
29
+
30
+ def save(options={})
31
+ _root_document.try(:save, options).tap do |result|
32
+ @_new = false if result
33
+ end
34
+ end
35
+
36
+ def save!(options={})
37
+ _root_document.try(:save, options).tap do |result|
38
+ @_new = false if result
39
+ end
40
+ end
41
+
42
+ def _parent_document=(value)
43
+ @_root_document = value._root_document
44
+ @_parent_document = value
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -1,18 +1,12 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
3
  module Plugins
3
4
  module Equality
4
5
  module InstanceMethods
5
- def ==(other)
6
- other.is_a?(self.class) && _id == other._id
7
- end
8
-
9
6
  def eql?(other)
10
- self == other
11
- end
12
-
13
- def equal?(other)
14
- object_id === other.object_id
7
+ other.is_a?(self.class) && _id == other._id
15
8
  end
9
+ alias :== :eql?
16
10
 
17
11
  def hash
18
12
  _id.hash
@@ -1,3 +1,6 @@
1
+ # encoding: UTF-8
2
+ require 'set'
3
+
1
4
  module MongoMapper
2
5
  module Plugins
3
6
  module IdentityMap
@@ -29,20 +32,19 @@ module MongoMapper
29
32
 
30
33
  def find_one(options={})
31
34
  query = query(options)
32
- criteria = query.criteria.to_hash
33
-
34
- if simple_find?(criteria) && identity_map.key?(criteria[:_id])
35
- identity_map[criteria[:_id]]
35
+
36
+ if query.simple? && identity_map.key?(query[:_id])
37
+ identity_map[query[:_id]]
36
38
  else
37
39
  super.tap do |document|
38
- remove_documents_from_map(document) if selecting_fields?(query.options)
40
+ remove_documents_from_map(document) if query.fields?
39
41
  end
40
42
  end
41
43
  end
42
44
 
43
45
  def find_many(options)
44
46
  super.tap do |documents|
45
- remove_documents_from_map(documents) if selecting_fields?(query(options).options)
47
+ remove_documents_from_map(documents) if query(options).fields?
46
48
  end
47
49
  end
48
50
 
@@ -92,10 +94,6 @@ module MongoMapper
92
94
  end
93
95
  end
94
96
 
95
- def simple_find?(criteria)
96
- criteria.keys == [:_id] || criteria.keys.to_set == [:_id, :_type].to_set
97
- end
98
-
99
97
  def selecting_fields?(options)
100
98
  !options[:fields].nil?
101
99
  end
@@ -0,0 +1,12 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Indexes
5
+ module ClassMethods
6
+ def ensure_index(spec, options={})
7
+ collection.create_index(spec, options)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
3
  module Plugins
3
4
  module Inspect
@@ -1,15 +1,13 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
3
  module Plugins
3
4
  module Keys
4
- autoload :Key, 'mongo_mapper/plugins/keys/key'
5
-
6
5
  def self.configure(model)
7
6
  model.key :_id, ObjectId
8
7
  end
9
8
 
10
9
  module ClassMethods
11
10
  def inherited(descendant)
12
- key :_type, String unless keys.keys.include?(:_type)
13
11
  descendant.instance_variable_set(:@keys, keys.dup)
14
12
  super
15
13
  end
@@ -159,14 +157,12 @@ module MongoMapper
159
157
  default_id_value(attrs)
160
158
 
161
159
  if from_database
162
- @new = false
160
+ @_new = false
163
161
  load_from_database(attrs)
164
162
  else
165
- @new = true
163
+ @_new = true
166
164
  assign(attrs)
167
165
  end
168
-
169
- assign_type
170
166
  end
171
167
 
172
168
  def persisted?
@@ -186,24 +182,22 @@ module MongoMapper
186
182
  end
187
183
 
188
184
  def attributes
189
- attrs = HashWithIndifferentAccess.new
190
-
191
- keys.each_pair do |name, key|
192
- value = key.set(self[key.name])
193
- attrs[name] = value
194
- end
185
+ HashWithIndifferentAccess.new.tap do |attrs|
186
+ keys.each_pair do |name, key|
187
+ value = key.set(self[key.name])
188
+ attrs[name] = value
189
+ end
195
190
 
196
- embedded_associations.each do |association|
197
- if documents = instance_variable_get(association.ivar)
198
- if association.one?
199
- attrs[association.name] = documents.to_mongo
200
- else
201
- attrs[association.name] = documents.map { |document| document.to_mongo }
191
+ embedded_associations.each do |association|
192
+ if documents = instance_variable_get(association.ivar)
193
+ if association.one?
194
+ attrs[association.name] = documents.to_mongo
195
+ else
196
+ attrs[association.name] = documents.map { |document| document.to_mongo }
197
+ end
202
198
  end
203
199
  end
204
200
  end
205
-
206
- attrs
207
201
  end
208
202
  alias :to_mongo :attributes
209
203
 
@@ -279,10 +273,6 @@ module MongoMapper
279
273
  end
280
274
  end
281
275
 
282
- def assign_type
283
- self._type = self.class.name if respond_to?(:_type=)
284
- end
285
-
286
276
  def ensure_key_exists(name)
287
277
  self.class.key(name) unless respond_to?("#{name}=")
288
278
  end
@@ -314,8 +304,6 @@ module MongoMapper
314
304
  instance_variable_set :"@#{name}", key.set(value)
315
305
  end
316
306
  end
317
-
318
-
319
307
  end
320
308
  end
321
309
  end
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
3
  module Plugins
3
4
  module Keys
@@ -8,7 +9,7 @@ module MongoMapper
8
9
  options = args.extract_options!
9
10
  @name, @type = args.shift.to_s, args.shift
10
11
  self.options = (options || {}).symbolize_keys
11
- self.default_value = self.options.delete(:default)
12
+ self.default_value = self.options[:default]
12
13
  end
13
14
 
14
15
  def ==(other)
@@ -16,7 +17,8 @@ module MongoMapper
16
17
  end
17
18
 
18
19
  def embeddable?
19
- type.respond_to?(:embeddable?) && type.embeddable? ? true : false
20
+ return false unless type.respond_to?(:embeddable?)
21
+ type.embeddable?
20
22
  end
21
23
 
22
24
  def number?
@@ -36,8 +38,17 @@ module MongoMapper
36
38
  end
37
39
 
38
40
  def set(value)
39
- type.to_mongo(value)
41
+ type.to_mongo(value).tap do |values|
42
+ if options[:typecast].present?
43
+ values.map! { |v| typecast_class.to_mongo(v) }
44
+ end
45
+ end
40
46
  end
47
+
48
+ private
49
+ def typecast_class
50
+ @typecast_class ||= options[:typecast].constantize
51
+ end
41
52
  end
42
53
  end
43
54
  end
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
3
  module Plugins
3
4
  module Logger
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
3
  module Plugins
3
4
  module Modifiers
@@ -29,7 +30,7 @@ module MongoMapper
29
30
  criteria = {:id => ids}
30
31
  end
31
32
 
32
- criteria = query(criteria).criteria.to_hash
33
+ criteria = criteria_hash(criteria).to_hash
33
34
  modifiers = keys.inject({}) { |hash, key| hash[key] = 1; hash }
34
35
  collection.update(criteria, {'$unset' => modifiers}, :multi => true)
35
36
  end
@@ -68,7 +69,7 @@ module MongoMapper
68
69
  def criteria_and_keys_from_args(args)
69
70
  keys = args.pop
70
71
  criteria = args[0].is_a?(Hash) ? args[0] : {:id => args}
71
- [query(criteria).criteria.to_hash, keys]
72
+ [criteria_hash(criteria).to_hash, keys]
72
73
  end
73
74
  end
74
75