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,283 +1,41 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
3
  module Document
3
4
  extend Support::DescendantAppends
4
5
 
5
6
  def self.included(model)
6
7
  model.class_eval do
7
- include InstanceMethods
8
- extend Support::Find
9
- extend ClassMethods
10
8
  extend Plugins
11
9
 
10
+ plugin Plugins::Document
11
+ plugin Plugins::Querying # for now needs to be before associations (save_to_collection)
12
12
  plugin Plugins::Associations
13
+ plugin Plugins::Caching
13
14
  plugin Plugins::Clone
14
15
  plugin Plugins::Descendants
16
+ plugin Plugins::DynamicQuerying
15
17
  plugin Plugins::Equality
16
18
  plugin Plugins::Inspect
19
+ plugin Plugins::Indexes
17
20
  plugin Plugins::Keys
18
21
  plugin Plugins::Dirty # for now dirty needs to be after keys
19
22
  plugin Plugins::Logger
20
23
  plugin Plugins::Modifiers
21
24
  plugin Plugins::Pagination
22
25
  plugin Plugins::Persistence
26
+ plugin Plugins::Accessible
23
27
  plugin Plugins::Protected
24
28
  plugin Plugins::Rails
29
+ plugin Plugins::Safe # needs to be after querying (save_to_collection)
30
+ plugin Plugins::Sci
31
+ plugin Plugins::Scopes
25
32
  plugin Plugins::Serialization
26
33
  plugin Plugins::Timestamps
27
34
  plugin Plugins::Userstamps
28
35
  plugin Plugins::Validations
29
36
  plugin Plugins::Callbacks # for now callbacks needs to be after validations
30
-
31
- extend Plugins::Validations::DocumentMacros
32
37
  end
33
-
34
38
  super
35
39
  end
36
-
37
- module ClassMethods
38
- def inherited(subclass)
39
- subclass.set_collection_name(collection_name)
40
- super
41
- end
42
-
43
- def ensure_index(spec, options={})
44
- collection.create_index(spec, options)
45
- end
46
-
47
- def find(*args)
48
- options = args.extract_options!
49
- return nil if args.size == 0
50
-
51
- if args.first.is_a?(Array) || args.size > 1
52
- find_some(args, options)
53
- else
54
- query = query(options).update(:_id => args[0])
55
- find_one(query.to_hash)
56
- end
57
- end
58
-
59
- def find!(*args)
60
- options = args.extract_options!
61
- raise DocumentNotFound, "Couldn't find without an ID" if args.size == 0
62
-
63
- if args.first.is_a?(Array) || args.size > 1
64
- find_some!(args, options)
65
- else
66
- query = query(options).update(:_id => args[0])
67
- find_one(query.to_hash) || raise(DocumentNotFound, "Document match #{options.inspect} does not exist in #{collection.name} collection")
68
- end
69
- end
70
-
71
- def find_each(options={})
72
- query(options).find().each { |doc| yield load(doc) }
73
- end
74
-
75
- def find_by_id(id)
76
- find(id)
77
- end
78
-
79
- def first_or_create(args)
80
- first(args) || create(args.reject { |key, value| !key?(key) })
81
- end
82
-
83
- def first_or_new(args)
84
- first(args) || new(args.reject { |key, value| !key?(key) })
85
- end
86
-
87
- def first(options={})
88
- find_one(options)
89
- end
90
-
91
- def last(options={})
92
- raise ':order option must be provided when using last' if options[:order].blank?
93
- find_one(query(options).reverse.to_hash)
94
- end
95
-
96
- def all(options={})
97
- find_many(options)
98
- end
99
-
100
- def count(options={})
101
- query(options).count
102
- end
103
-
104
- def exists?(options={})
105
- !count(options).zero?
106
- end
107
-
108
- def create(*docs)
109
- initialize_each(*docs) { |doc| doc.save }
110
- end
111
-
112
- def create!(*docs)
113
- initialize_each(*docs) { |doc| doc.save! }
114
- end
115
-
116
- def update(*args)
117
- if args.length == 1
118
- update_multiple(args[0])
119
- else
120
- id, attributes = args
121
- update_single(id, attributes)
122
- end
123
- end
124
-
125
- def delete(*ids)
126
- query(:_id => ids.flatten).remove
127
- end
128
-
129
- def delete_all(options={})
130
- query(options).remove
131
- end
132
-
133
- def destroy(*ids)
134
- find_some!(ids.flatten).each(&:destroy)
135
- end
136
-
137
- def destroy_all(options={})
138
- find_each(options) { |document| document.destroy }
139
- end
140
-
141
- def embeddable?
142
- false
143
- end
144
-
145
- def single_collection_inherited?
146
- keys.key?(:_type) && single_collection_inherited_superclass?
147
- end
148
-
149
- def single_collection_inherited_superclass?
150
- superclass.respond_to?(:keys) && superclass.keys.key?(:_type)
151
- end
152
-
153
- # @api private for now
154
- def query(options={})
155
- Query.new(self, options)
156
- end
157
-
158
- private
159
- def initialize_each(*docs)
160
- instances = []
161
- docs = [{}] if docs.blank?
162
- docs.flatten.each do |attrs|
163
- doc = new(attrs)
164
- yield(doc)
165
- instances << doc
166
- end
167
- instances.size == 1 ? instances[0] : instances
168
- end
169
-
170
- def find_some(ids, options={})
171
- query = query(options).update(:_id => ids.flatten.compact.uniq)
172
- find_many(query.to_hash).compact
173
- end
174
-
175
- def find_some!(ids, options={})
176
- ids = ids.flatten.compact.uniq
177
- documents = find_some(ids, options)
178
-
179
- if ids.size == documents.size
180
- documents
181
- else
182
- raise DocumentNotFound, "Couldn't find all of the ids (#{ids.to_sentence}). Found #{documents.size}, but was expecting #{ids.size}"
183
- end
184
- end
185
-
186
- # All query methods that load documents pass through find_one or find_many
187
- def find_one(options={})
188
- load(query(options).first)
189
- end
190
-
191
- # All query methods that load documents pass through find_one or find_many
192
- def find_many(options)
193
- query(options).all().map { |doc| load(doc) }
194
- end
195
-
196
- def update_single(id, attrs)
197
- if id.blank? || attrs.blank? || !attrs.is_a?(Hash)
198
- raise ArgumentError, "Updating a single document requires an id and a hash of attributes"
199
- end
200
-
201
- find(id).tap do |doc|
202
- doc.update_attributes(attrs)
203
- end
204
- end
205
-
206
- def update_multiple(docs)
207
- unless docs.is_a?(Hash)
208
- raise ArgumentError, "Updating multiple documents takes 1 argument and it must be hash"
209
- end
210
-
211
- instances = []
212
- docs.each_pair { |id, attrs| instances << update(id, attrs) }
213
- instances
214
- end
215
- end
216
-
217
- module InstanceMethods
218
- def save(options={})
219
- options.assert_valid_keys(:validate, :safe)
220
- options.reverse_merge!(:validate => true)
221
- !options[:validate] || valid? ? create_or_update(options) : false
222
- end
223
-
224
- def save!(options={})
225
- options.assert_valid_keys(:safe)
226
- save(options) || raise(DocumentNotValid.new(self))
227
- end
228
-
229
- def destroy
230
- delete
231
- end
232
-
233
- def delete
234
- @_destroyed = true
235
- self.class.delete(id) unless new?
236
- end
237
-
238
- def new?
239
- @new
240
- end
241
-
242
- def destroyed?
243
- @_destroyed == true
244
- end
245
-
246
- def reload
247
- if doc = self.class.query(:_id => id).first
248
- self.class.associations.each { |name, assoc| send(name).reset if respond_to?(name) }
249
- self.attributes = doc
250
- self
251
- else
252
- raise DocumentNotFound, "Document match #{_id.inspect} does not exist in #{collection.name} collection"
253
- end
254
- end
255
-
256
- # Used by embedded docs to find root easily without if/respond_to? stuff.
257
- # Documents are always root documents.
258
- def _root_document
259
- self
260
- end
261
-
262
- private
263
- def create_or_update(options={})
264
- result = new? ? create(options) : update(options)
265
- result != false
266
- end
267
-
268
- def create(options={})
269
- save_to_collection(options)
270
- end
271
-
272
- def update(options={})
273
- save_to_collection(options)
274
- end
275
-
276
- def save_to_collection(options={})
277
- safe = options[:safe] || false
278
- @new = false
279
- collection.save(to_mongo, :safe => safe)
280
- end
281
- end
282
40
  end # Document
283
41
  end # MongoMapper
@@ -1,14 +1,15 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
3
  module EmbeddedDocument
3
4
  extend Support::DescendantAppends
4
5
 
5
6
  def self.included(model)
6
7
  model.class_eval do
7
- include InstanceMethods
8
- extend ClassMethods
9
8
  extend Plugins
10
9
 
10
+ plugin Plugins::EmbeddedDocument
11
11
  plugin Plugins::Associations
12
+ plugin Plugins::Caching
12
13
  plugin Plugins::Clone
13
14
  plugin Plugins::Descendants
14
15
  plugin Plugins::Equality
@@ -16,55 +17,15 @@ module MongoMapper
16
17
  plugin Plugins::Keys
17
18
  plugin Plugins::Logger
18
19
  plugin Plugins::Persistence
20
+ plugin Plugins::Accessible
19
21
  plugin Plugins::Protected
20
22
  plugin Plugins::Rails
23
+ plugin Plugins::Sci
21
24
  plugin Plugins::Serialization
22
25
  plugin Plugins::Validations
23
26
  plugin Plugins::Callbacks
24
-
25
- attr_reader :_root_document, :_parent_document
26
27
  end
27
-
28
28
  super
29
29
  end
30
-
31
- module ClassMethods
32
- def embeddable?
33
- true
34
- end
35
-
36
- def embedded_in(owner_name)
37
- define_method(owner_name) { _parent_document }
38
- end
39
- end
40
-
41
- module InstanceMethods
42
- def destroyed?
43
- !!_root_document.try(:destroyed?)
44
- end
45
-
46
- def new?
47
- _root_document.try(:new?) || @new
48
- end
49
-
50
- def save(options={})
51
- if result = _root_document.try(:save, options)
52
- @new = false
53
- end
54
- result
55
- end
56
-
57
- def save!(options={})
58
- if result = _root_document.try(:save!, options)
59
- @new = false
60
- end
61
- result
62
- end
63
-
64
- def _parent_document=(value)
65
- @_root_document = value._root_document
66
- @_parent_document = value
67
- end
68
- end # InstanceMethods
69
- end # EmbeddedDocument
70
- end # MongoMapper
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ # generic MM error
4
+ class Error < StandardError; end
5
+
6
+ # raised when key expected to exist but not found
7
+ class KeyNotFound < Error; end
8
+
9
+ # raised when document expected but not found
10
+ class DocumentNotFound < Error; end
11
+
12
+ # raised when trying to connect using uri with incorrect scheme
13
+ class InvalidScheme < Error; end
14
+
15
+ # raised when trying to do something not supported, mostly for edocs
16
+ class NotSupported < Error; end
17
+
18
+ # raised when document not valid and using !
19
+ class DocumentNotValid < Error
20
+ def initialize(document)
21
+ super("Validation failed: #{document.errors.full_messages.join(", ")}")
22
+ end
23
+ end
24
+
25
+ class AccessibleOrProtected < Error
26
+ def initialize(name)
27
+ super("Declare either attr_protected or attr_accessible for #{name}, but not both.")
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Extensions
4
+ module Array
5
+ def to_mongo(value)
6
+ value = value.respond_to?(:lines) ? value.lines : value
7
+ value.to_a
8
+ end
9
+
10
+ def from_mongo(value)
11
+ value || []
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ class Array
18
+ extend MongoMapper::Extensions::Array
19
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Extensions
4
+ module Binary
5
+ def to_mongo(value)
6
+ if value.is_a?(::BSON::Binary)
7
+ value
8
+ else
9
+ value.nil? ? nil : ::BSON::Binary.new(value)
10
+ end
11
+ end
12
+
13
+ def from_mongo(value)
14
+ value
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ class Binary
21
+ extend MongoMapper::Extensions::Binary
22
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Extensions
4
+ module Boolean
5
+ Mapping = {
6
+ true => true,
7
+ 'true' => true,
8
+ 'TRUE' => true,
9
+ 'True' => true,
10
+ 't' => true,
11
+ 'T' => true,
12
+ '1' => true,
13
+ 1 => true,
14
+ 1.0 => true,
15
+ false => false,
16
+ 'false' => false,
17
+ 'FALSE' => false,
18
+ 'False' => false,
19
+ 'f' => false,
20
+ 'F' => false,
21
+ '0' => false,
22
+ 0 => false,
23
+ 0.0 => false,
24
+ nil => nil
25
+ }
26
+
27
+ def to_mongo(value)
28
+ if value.is_a?(Boolean)
29
+ value
30
+ else
31
+ Mapping[value]
32
+ end
33
+ end
34
+
35
+ def from_mongo(value)
36
+ value.nil? ? nil : !!value
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ class Boolean
43
+ extend MongoMapper::Extensions::Boolean
44
+ end