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,24 +1,14 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
3
  module Plugins
3
4
  module Pagination
4
5
  module ClassMethods
5
- def per_page
6
- 25
7
- end
8
-
9
- def paginate(options)
10
- per_page = options.delete(:per_page) || self.per_page
11
- page = options.delete(:page)
12
- total_entries = count(options)
13
- pagination = Pagination::Proxy.new(total_entries, page, per_page)
6
+ def per_page; 25 end
14
7
 
15
- options.update(:limit => pagination.limit, :skip => pagination.skip)
16
- pagination.subject = find_many(options)
17
- pagination
8
+ def paginate(opts={})
9
+ query.paginate({:per_page => per_page}.merge(opts))
18
10
  end
19
11
  end
20
12
  end
21
13
  end
22
- end
23
-
24
- require 'mongo_mapper/plugins/pagination/proxy'
14
+ end
@@ -1,11 +1,10 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
3
  module Plugins
3
4
  module Persistence
4
5
  module ClassMethods
5
- class Unsupported < MongoMapperError; end
6
-
7
6
  def connection(mongo_connection=nil)
8
- not_supported_by_embedded
7
+ assert_supported
9
8
  if mongo_connection.nil?
10
9
  @connection ||= MongoMapper.connection
11
10
  else
@@ -15,17 +14,17 @@ module MongoMapper
15
14
  end
16
15
 
17
16
  def set_database_name(name)
18
- not_supported_by_embedded
17
+ assert_supported
19
18
  @database_name = name
20
19
  end
21
20
 
22
21
  def database_name
23
- not_supported_by_embedded
22
+ assert_supported
24
23
  @database_name
25
24
  end
26
25
 
27
26
  def database
28
- not_supported_by_embedded
27
+ assert_supported
29
28
  if database_name.nil?
30
29
  MongoMapper.database
31
30
  else
@@ -34,23 +33,25 @@ module MongoMapper
34
33
  end
35
34
 
36
35
  def set_collection_name(name)
37
- not_supported_by_embedded
36
+ assert_supported
38
37
  @collection_name = name
39
38
  end
40
39
 
41
40
  def collection_name
42
- not_supported_by_embedded
41
+ assert_supported
43
42
  @collection_name ||= self.to_s.tableize.gsub(/\//, '.')
44
43
  end
45
44
 
46
45
  def collection
47
- not_supported_by_embedded
46
+ assert_supported
48
47
  database.collection(collection_name)
49
48
  end
50
49
 
51
50
  private
52
- def not_supported_by_embedded
53
- raise Unsupported.new('This is not supported for embeddable documents at this time.') if embeddable?
51
+ def assert_supported
52
+ if embeddable?
53
+ raise NotSupported.new('This is not supported for embeddable documents at this time.')
54
+ end
54
55
  end
55
56
  end
56
57
 
@@ -1,8 +1,12 @@
1
+ # encoding: UTF-8
2
+ require 'set'
3
+
1
4
  module MongoMapper
2
5
  module Plugins
3
6
  module Protected
4
7
  module ClassMethods
5
8
  def attr_protected(*attrs)
9
+ raise AccessibleOrProtected.new(name) if try(:accessible_attributes?)
6
10
  self.write_inheritable_attribute(:attr_protected, Set.new(attrs) + (protected_attributes || []))
7
11
  end
8
12
 
@@ -10,6 +14,10 @@ module MongoMapper
10
14
  self.read_inheritable_attribute(:attr_protected)
11
15
  end
12
16
 
17
+ def protected_attributes?
18
+ !protected_attributes.nil?
19
+ end
20
+
13
21
  def key(*args)
14
22
  key = super
15
23
  attr_protected key.name.to_sym if key.options[:protected]
@@ -0,0 +1,236 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Querying
5
+ module ClassMethods
6
+ def find(*args)
7
+ options = args.extract_options!
8
+ return nil if args.size == 0
9
+
10
+ if args.first.is_a?(Array) || args.size > 1
11
+ find_some(args, options)
12
+ else
13
+ find_one(options.merge(:_id => args[0]))
14
+ end
15
+ end
16
+
17
+ def find!(*args)
18
+ options = args.extract_options!
19
+ raise DocumentNotFound, "Couldn't find without an ID" if args.size == 0
20
+
21
+ if args.first.is_a?(Array) || args.size > 1
22
+ find_some!(args, options)
23
+ else
24
+ find_one(options.merge(:_id => args[0])) ||
25
+ raise(DocumentNotFound, "Document match #{options.inspect} does not exist
26
+ in #{collection.name} collection")
27
+ end
28
+ end
29
+
30
+ def find_each(options={})
31
+ query(options).find_each.each { |doc| yield load(doc) }
32
+ end
33
+
34
+ def find_by_id(id)
35
+ find_one(:_id => id)
36
+ end
37
+
38
+ def first(options={})
39
+ find_one(options)
40
+ end
41
+
42
+ # All bets are off an actual order if you provide none.
43
+ def last(options={})
44
+ find_one(query(options).reverse.to_hash)
45
+ end
46
+
47
+ def all(options={})
48
+ find_many(options)
49
+ end
50
+
51
+ def count(options={})
52
+ query(options).count
53
+ end
54
+
55
+ def exists?(options={})
56
+ !count(options).zero?
57
+ end
58
+
59
+ def first_or_create(args)
60
+ first(args) || create(args.reject { |key, value| !key?(key) })
61
+ end
62
+
63
+ def first_or_new(args)
64
+ first(args) || new(args.reject { |key, value| !key?(key) })
65
+ end
66
+
67
+ def create(*docs)
68
+ initialize_each(*docs) { |doc| doc.save }
69
+ end
70
+
71
+ def create!(*docs)
72
+ initialize_each(*docs) { |doc| doc.save! }
73
+ end
74
+
75
+ def update(*args)
76
+ if args.length == 1
77
+ update_multiple(args[0])
78
+ else
79
+ id, attributes = args
80
+ update_single(id, attributes)
81
+ end
82
+ end
83
+
84
+ def delete(*ids)
85
+ query(:_id => ids.flatten).remove
86
+ end
87
+
88
+ def delete_all(options={})
89
+ query(options).remove
90
+ end
91
+
92
+ def destroy(*ids)
93
+ find_some!(ids.flatten).each(&:destroy)
94
+ end
95
+
96
+ def destroy_all(options={})
97
+ find_each(options) { |document| document.destroy }
98
+ end
99
+
100
+ def where(options={})
101
+ query.where(options)
102
+ end
103
+
104
+ def fields(*args)
105
+ query.fields(*args)
106
+ end
107
+
108
+ def limit(*args)
109
+ query.limit(*args)
110
+ end
111
+
112
+ def skip(*args)
113
+ query.skip(*args)
114
+ end
115
+
116
+ def sort(*args)
117
+ query.sort(*args)
118
+ end
119
+
120
+ # @api private for now
121
+ def query(options={})
122
+ Plucky::Query.new(collection).tap do |query|
123
+ query.extend(MongoMapper::Plugins::Querying::Decorator)
124
+ query.object_ids(object_id_keys)
125
+ query.update(options)
126
+ query.model(self)
127
+ end
128
+ end
129
+
130
+ # @api private for now
131
+ def criteria_hash(criteria={})
132
+ Plucky::CriteriaHash.new(criteria, :object_ids => object_id_keys)
133
+ end
134
+
135
+ private
136
+ def find_some(ids, options={})
137
+ query = query(options).update(:_id => ids.flatten.compact.uniq)
138
+ find_many(query.to_hash).compact
139
+ end
140
+
141
+ def find_some!(ids, options={})
142
+ ids = ids.flatten.compact.uniq
143
+ docs = find_some(ids, options)
144
+
145
+ if ids.size != docs.size
146
+ raise DocumentNotFound, "Couldn't find all of the ids (#{ids.to_sentence}). Found #{docs.size}, but was expecting #{ids.size}"
147
+ end
148
+
149
+ docs
150
+ end
151
+
152
+ # All query methods that load documents pass through find_one or find_many
153
+ def find_one(options={})
154
+ query(options).first
155
+ end
156
+
157
+ # All query methods that load documents pass through find_one or find_many
158
+ def find_many(options)
159
+ query(options).all
160
+ end
161
+
162
+ def initialize_each(*docs)
163
+ instances = []
164
+ docs = [{}] if docs.blank?
165
+ docs.flatten.each do |attrs|
166
+ doc = new(attrs)
167
+ yield(doc)
168
+ instances << doc
169
+ end
170
+ instances.size == 1 ? instances[0] : instances
171
+ end
172
+
173
+ def update_single(id, attrs)
174
+ if id.blank? || attrs.blank? || !attrs.is_a?(Hash)
175
+ raise ArgumentError, "Updating a single document requires an id and a hash of attributes"
176
+ end
177
+
178
+ find(id).tap do |doc|
179
+ doc.update_attributes(attrs)
180
+ end
181
+ end
182
+
183
+ def update_multiple(docs)
184
+ unless docs.is_a?(Hash)
185
+ raise ArgumentError, "Updating multiple documents takes 1 argument and it must be hash"
186
+ end
187
+
188
+ instances = []
189
+ docs.each_pair { |id, attrs| instances << update(id, attrs) }
190
+ instances
191
+ end
192
+ end
193
+
194
+ module InstanceMethods
195
+ def save(options={})
196
+ options.assert_valid_keys(:validate, :safe)
197
+ options.reverse_merge!(:validate => true)
198
+ !options[:validate] || valid? ? create_or_update(options) : false
199
+ end
200
+
201
+ def save!(options={})
202
+ options.assert_valid_keys(:safe)
203
+ save(options) || raise(DocumentNotValid.new(self))
204
+ end
205
+
206
+ def destroy
207
+ delete
208
+ end
209
+
210
+ def delete
211
+ @_destroyed = true
212
+ self.class.delete(id) unless new?
213
+ end
214
+
215
+ private
216
+ def create_or_update(options={})
217
+ result = new? ? create(options) : update(options)
218
+ result != false
219
+ end
220
+
221
+ def create(options={})
222
+ save_to_collection(options)
223
+ end
224
+
225
+ def update(options={})
226
+ save_to_collection(options)
227
+ end
228
+
229
+ def save_to_collection(options={})
230
+ @_new = false
231
+ collection.save(to_mongo, :safe => options[:safe])
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Querying
5
+ module Decorator
6
+ include MongoMapper::Plugins::DynamicQuerying::ClassMethods
7
+
8
+ def model(model=nil)
9
+ return @model if model.nil?
10
+ @model = model
11
+ self
12
+ end
13
+
14
+ def find!(*ids)
15
+ raise DocumentNotFound, "Couldn't find without an ID" if ids.size == 0
16
+
17
+ find(*ids).tap do |result|
18
+ if result.nil? || ids.size != Array(result).size
19
+ raise DocumentNotFound, "Couldn't find all of the ids (#{ids.join(',')}). Found #{Array(result).size}, but was expecting #{ids.size}"
20
+ end
21
+ end
22
+ end
23
+
24
+ def all(opts={})
25
+ super.map { |doc| model.load(doc) }
26
+ end
27
+
28
+ def first(opts={})
29
+ model.load(super)
30
+ end
31
+
32
+ def last(opts={})
33
+ model.load(super)
34
+ end
35
+
36
+ private
37
+ def method_missing(method, *args, &block)
38
+ return super unless model.respond_to?(method)
39
+ result = model.send(method, *args, &block)
40
+ return super unless result.is_a?(Plucky::Query)
41
+ merge(result)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  module MongoMapper
2
3
  module Plugins
3
4
  module Rails
@@ -0,0 +1,28 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Safe
5
+ module ClassMethods
6
+ def inherited(subclass)
7
+ super
8
+ subclass.safe if safe?
9
+ end
10
+
11
+ def safe
12
+ @safe = true
13
+ end
14
+
15
+ def safe?
16
+ @safe == true
17
+ end
18
+ end
19
+
20
+ module InstanceMethods
21
+ def save_to_collection(options={})
22
+ options[:safe] = self.class.safe? unless options.key?(:safe)
23
+ super
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Sci
5
+ module ClassMethods
6
+ def inherited(subclass)
7
+ key :_type, String unless key?(:_type)
8
+ subclass.instance_variable_set("@single_collection_inherited", true)
9
+ subclass.set_collection_name(collection_name) unless subclass.embeddable?
10
+ super
11
+ end
12
+
13
+ def single_collection_inherited?
14
+ @single_collection_inherited == true
15
+ end
16
+
17
+ def query(options={})
18
+ super.tap do |query|
19
+ query[:_type] = name if single_collection_inherited?
20
+ end
21
+ end
22
+ end
23
+
24
+ module InstanceMethods
25
+ def initialize(*args)
26
+ super
27
+ write_key :_type, self.class.name if self.class.key?(:_type)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end