mongo_mapper 0.7.6 → 0.8.0

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 +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