thorsson-mongo_mapper 0.8.2

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/LICENSE +20 -0
  2. data/README.rdoc +27 -0
  3. data/bin/mmconsole +60 -0
  4. data/examples/keys.rb +40 -0
  5. data/examples/modifiers/set.rb +25 -0
  6. data/examples/plugins.rb +41 -0
  7. data/examples/querying.rb +35 -0
  8. data/examples/scopes.rb +52 -0
  9. data/lib/mongo_mapper.rb +79 -0
  10. data/lib/mongo_mapper/connection.rb +83 -0
  11. data/lib/mongo_mapper/document.rb +41 -0
  12. data/lib/mongo_mapper/embedded_document.rb +31 -0
  13. data/lib/mongo_mapper/exceptions.rb +27 -0
  14. data/lib/mongo_mapper/extensions/array.rb +19 -0
  15. data/lib/mongo_mapper/extensions/binary.rb +22 -0
  16. data/lib/mongo_mapper/extensions/boolean.rb +44 -0
  17. data/lib/mongo_mapper/extensions/date.rb +25 -0
  18. data/lib/mongo_mapper/extensions/float.rb +14 -0
  19. data/lib/mongo_mapper/extensions/hash.rb +14 -0
  20. data/lib/mongo_mapper/extensions/integer.rb +19 -0
  21. data/lib/mongo_mapper/extensions/kernel.rb +9 -0
  22. data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
  23. data/lib/mongo_mapper/extensions/object.rb +27 -0
  24. data/lib/mongo_mapper/extensions/object_id.rb +30 -0
  25. data/lib/mongo_mapper/extensions/set.rb +20 -0
  26. data/lib/mongo_mapper/extensions/string.rb +18 -0
  27. data/lib/mongo_mapper/extensions/time.rb +29 -0
  28. data/lib/mongo_mapper/plugins.rb +16 -0
  29. data/lib/mongo_mapper/plugins/accessible.rb +44 -0
  30. data/lib/mongo_mapper/plugins/associations.rb +97 -0
  31. data/lib/mongo_mapper/plugins/associations/base.rb +124 -0
  32. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +29 -0
  33. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +24 -0
  34. data/lib/mongo_mapper/plugins/associations/collection.rb +22 -0
  35. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +40 -0
  36. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +127 -0
  37. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  38. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +110 -0
  39. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
  40. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
  41. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
  42. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +40 -0
  43. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  44. data/lib/mongo_mapper/plugins/associations/proxy.rb +126 -0
  45. data/lib/mongo_mapper/plugins/caching.rb +21 -0
  46. data/lib/mongo_mapper/plugins/callbacks.rb +241 -0
  47. data/lib/mongo_mapper/plugins/clone.rb +19 -0
  48. data/lib/mongo_mapper/plugins/descendants.rb +17 -0
  49. data/lib/mongo_mapper/plugins/dirty.rb +120 -0
  50. data/lib/mongo_mapper/plugins/document.rb +41 -0
  51. data/lib/mongo_mapper/plugins/dynamic_querying.rb +43 -0
  52. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
  53. data/lib/mongo_mapper/plugins/embedded_document.rb +49 -0
  54. data/lib/mongo_mapper/plugins/equality.rb +17 -0
  55. data/lib/mongo_mapper/plugins/identity_map.rb +128 -0
  56. data/lib/mongo_mapper/plugins/indexes.rb +12 -0
  57. data/lib/mongo_mapper/plugins/inspect.rb +15 -0
  58. data/lib/mongo_mapper/plugins/keys.rb +309 -0
  59. data/lib/mongo_mapper/plugins/keys/key.rb +55 -0
  60. data/lib/mongo_mapper/plugins/logger.rb +18 -0
  61. data/lib/mongo_mapper/plugins/modifiers.rb +112 -0
  62. data/lib/mongo_mapper/plugins/pagination.rb +14 -0
  63. data/lib/mongo_mapper/plugins/persistence.rb +69 -0
  64. data/lib/mongo_mapper/plugins/protected.rb +53 -0
  65. data/lib/mongo_mapper/plugins/querying.rb +176 -0
  66. data/lib/mongo_mapper/plugins/querying/decorator.rb +46 -0
  67. data/lib/mongo_mapper/plugins/querying/plucky_methods.rb +15 -0
  68. data/lib/mongo_mapper/plugins/rails.rb +58 -0
  69. data/lib/mongo_mapper/plugins/safe.rb +28 -0
  70. data/lib/mongo_mapper/plugins/sci.rb +32 -0
  71. data/lib/mongo_mapper/plugins/scopes.rb +21 -0
  72. data/lib/mongo_mapper/plugins/serialization.rb +76 -0
  73. data/lib/mongo_mapper/plugins/timestamps.rb +22 -0
  74. data/lib/mongo_mapper/plugins/userstamps.rb +15 -0
  75. data/lib/mongo_mapper/plugins/validations.rb +50 -0
  76. data/lib/mongo_mapper/support/descendant_appends.rb +45 -0
  77. data/lib/mongo_mapper/version.rb +4 -0
  78. data/test/_NOTE_ON_TESTING +1 -0
  79. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
  80. data/test/functional/associations/test_belongs_to_proxy.rb +93 -0
  81. data/test/functional/associations/test_in_array_proxy.rb +319 -0
  82. data/test/functional/associations/test_many_documents_as_proxy.rb +229 -0
  83. data/test/functional/associations/test_many_documents_proxy.rb +575 -0
  84. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +176 -0
  85. data/test/functional/associations/test_many_embedded_proxy.rb +256 -0
  86. data/test/functional/associations/test_many_polymorphic_proxy.rb +302 -0
  87. data/test/functional/associations/test_one_embedded_proxy.rb +67 -0
  88. data/test/functional/associations/test_one_proxy.rb +182 -0
  89. data/test/functional/test_accessible.rb +168 -0
  90. data/test/functional/test_associations.rb +44 -0
  91. data/test/functional/test_binary.rb +27 -0
  92. data/test/functional/test_caching.rb +76 -0
  93. data/test/functional/test_callbacks.rb +151 -0
  94. data/test/functional/test_dirty.rb +163 -0
  95. data/test/functional/test_document.rb +253 -0
  96. data/test/functional/test_dynamic_querying.rb +75 -0
  97. data/test/functional/test_embedded_document.rb +210 -0
  98. data/test/functional/test_identity_map.rb +506 -0
  99. data/test/functional/test_indexes.rb +42 -0
  100. data/test/functional/test_logger.rb +20 -0
  101. data/test/functional/test_modifiers.rb +416 -0
  102. data/test/functional/test_pagination.rb +91 -0
  103. data/test/functional/test_protected.rb +175 -0
  104. data/test/functional/test_querying.rb +873 -0
  105. data/test/functional/test_safe.rb +76 -0
  106. data/test/functional/test_sci.rb +230 -0
  107. data/test/functional/test_scopes.rb +171 -0
  108. data/test/functional/test_string_id_compatibility.rb +67 -0
  109. data/test/functional/test_timestamps.rb +62 -0
  110. data/test/functional/test_userstamps.rb +27 -0
  111. data/test/functional/test_validations.rb +342 -0
  112. data/test/models.rb +233 -0
  113. data/test/test_active_model_lint.rb +13 -0
  114. data/test/test_helper.rb +104 -0
  115. data/test/unit/associations/test_base.rb +212 -0
  116. data/test/unit/associations/test_proxy.rb +105 -0
  117. data/test/unit/serializers/test_json_serializer.rb +202 -0
  118. data/test/unit/test_clone.rb +69 -0
  119. data/test/unit/test_descendant_appends.rb +71 -0
  120. data/test/unit/test_document.rb +213 -0
  121. data/test/unit/test_dynamic_finder.rb +125 -0
  122. data/test/unit/test_embedded_document.rb +644 -0
  123. data/test/unit/test_extensions.rb +380 -0
  124. data/test/unit/test_key.rb +185 -0
  125. data/test/unit/test_keys.rb +55 -0
  126. data/test/unit/test_mongo_mapper.rb +110 -0
  127. data/test/unit/test_pagination.rb +11 -0
  128. data/test/unit/test_plugins.rb +50 -0
  129. data/test/unit/test_rails.rb +181 -0
  130. data/test/unit/test_rails_compatibility.rb +52 -0
  131. data/test/unit/test_serialization.rb +51 -0
  132. data/test/unit/test_time_zones.rb +39 -0
  133. data/test/unit/test_validations.rb +544 -0
  134. metadata +316 -0
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Caching
5
+ module InstanceMethods
6
+ def cache_key(*suffixes)
7
+ cache_key = case
8
+ when new?
9
+ "#{self.class.name}/new"
10
+ when timestamp = self[:updated_at]
11
+ "#{self.class.name}/#{id}-#{timestamp.to_s(:number)}"
12
+ else
13
+ "#{self.class.name}/#{id}"
14
+ end
15
+ cache_key += "/#{suffixes.join('/')}" unless suffixes.empty?
16
+ cache_key
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,241 @@
1
+ # encoding: UTF-8
2
+ # Almost all of this callback stuff is pulled directly from ActiveSupport
3
+ # in the interest of support rails 2 and 3 at the same time and is the
4
+ # same copyright as rails.
5
+ module MongoMapper
6
+ module Plugins
7
+ module Callbacks
8
+ def self.configure(model)
9
+ model.class_eval do
10
+ define_callbacks(
11
+ :before_save, :after_save,
12
+ :before_create, :after_create,
13
+ :before_update, :after_update,
14
+ :before_validation, :after_validation,
15
+ :before_validation_on_create, :after_validation_on_create,
16
+ :before_validation_on_update, :after_validation_on_update,
17
+ :before_destroy, :after_destroy,
18
+ :validate_on_create, :validate_on_update,
19
+ :validate
20
+ )
21
+ end
22
+ end
23
+
24
+ module ClassMethods
25
+ def define_callbacks(*callbacks)
26
+ callbacks.each do |callback|
27
+ class_eval <<-"end_eval"
28
+ def self.#{callback}(*methods, &block)
29
+ callbacks = CallbackChain.build(:#{callback}, *methods, &block)
30
+ @#{callback}_callbacks ||= CallbackChain.new
31
+ @#{callback}_callbacks.concat callbacks
32
+ end
33
+
34
+ def self.#{callback}_callback_chain
35
+ @#{callback}_callbacks ||= CallbackChain.new
36
+
37
+ if superclass.respond_to?(:#{callback}_callback_chain)
38
+ CallbackChain.new(
39
+ superclass.#{callback}_callback_chain +
40
+ @#{callback}_callbacks
41
+ )
42
+ else
43
+ @#{callback}_callbacks
44
+ end
45
+ end
46
+ end_eval
47
+ end
48
+ end
49
+ end
50
+
51
+ module InstanceMethods
52
+ def valid?
53
+ action = new? ? 'create' : 'update'
54
+ run_callbacks(:before_validation)
55
+ run_callbacks("before_validation_on_#{action}".to_sym)
56
+ result = super
57
+ run_callbacks("after_validation_on_#{action}".to_sym)
58
+ run_callbacks(:after_validation)
59
+ result
60
+ end
61
+
62
+ # Overriding validatable's valid_for_group? to integrate validation callbacks
63
+ def valid_for_group?(group) #:nodoc:
64
+ errors.clear
65
+ run_before_validations
66
+ run_callbacks(:validate)
67
+ new? ? run_callbacks(:validate_on_create) : run_callbacks(:validate_on_update)
68
+ self.class.validate_children(self, group)
69
+ self.validate_group(group)
70
+ errors.empty?
71
+ end
72
+
73
+ def destroy
74
+ run_callbacks(:before_destroy)
75
+ result = super
76
+ run_callbacks(:after_destroy)
77
+ result
78
+ end
79
+
80
+ def run_callbacks(kind, options={}, &block)
81
+ callback_chain_method = "#{kind}_callback_chain"
82
+ return unless self.class.respond_to?(callback_chain_method)
83
+ self.class.send(callback_chain_method).run(self, options, &block)
84
+ self.embedded_associations.each do |association|
85
+ if association.one?
86
+ self.send(association.name).run_callbacks(kind, options, &block)
87
+ else
88
+ self.send(association.name).each do |document|
89
+ document.run_callbacks(kind, options, &block)
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ private
96
+ def create_or_update(*args)
97
+ run_callbacks(:before_save)
98
+ if result = super
99
+ run_callbacks(:after_save)
100
+ end
101
+ result
102
+ end
103
+
104
+ def create(*args)
105
+ run_callbacks(:before_create)
106
+ result = super
107
+ run_callbacks(:after_create)
108
+ result
109
+ end
110
+
111
+ def update(*args)
112
+ run_callbacks(:before_update)
113
+ result = super
114
+ run_callbacks(:after_update)
115
+ result
116
+ end
117
+ end
118
+
119
+ class CallbackChain < Array
120
+ def self.build(kind, *methods, &block)
121
+ methods, options = extract_options(*methods, &block)
122
+ methods.map! { |method| Callback.new(kind, method, options) }
123
+ new(methods)
124
+ end
125
+
126
+ def run(object, options={}, &terminator)
127
+ enumerator = options[:enumerator] || :each
128
+
129
+ unless block_given?
130
+ send(enumerator) { |callback| callback.call(object) }
131
+ else
132
+ send(enumerator) do |callback|
133
+ result = callback.call(object)
134
+ break result if terminator.call(result, object)
135
+ end
136
+ end
137
+ end
138
+
139
+ # TODO: Decompose into more Array like behavior
140
+ def replace_or_append!(chain)
141
+ if index = index(chain)
142
+ self[index] = chain
143
+ else
144
+ self << chain
145
+ end
146
+ self
147
+ end
148
+
149
+ def find(callback, &block)
150
+ select { |c| c == callback && (!block_given? || yield(c)) }.first
151
+ end
152
+
153
+ def delete(callback)
154
+ super(callback.is_a?(Callback) ? callback : find(callback))
155
+ end
156
+
157
+ private
158
+ def self.extract_options(*methods, &block)
159
+ methods.flatten!
160
+ options = methods.extract_options!
161
+ methods << block if block_given?
162
+ return methods, options
163
+ end
164
+
165
+ def extract_options(*methods, &block)
166
+ self.class.extract_options(*methods, &block)
167
+ end
168
+ end
169
+
170
+ class Callback
171
+ attr_reader :kind, :method, :identifier, :options
172
+
173
+ def initialize(kind, method, options={})
174
+ @kind = kind
175
+ @method = method
176
+ @identifier = options[:identifier]
177
+ @options = options
178
+ end
179
+
180
+ def ==(other)
181
+ case other
182
+ when Callback
183
+ (self.identifier && self.identifier == other.identifier) || self.method == other.method
184
+ else
185
+ (self.identifier && self.identifier == other) || self.method == other
186
+ end
187
+ end
188
+
189
+ def eql?(other)
190
+ self == other
191
+ end
192
+
193
+ def dup
194
+ self.class.new(@kind, @method, @options.dup)
195
+ end
196
+
197
+ def hash
198
+ if @identifier
199
+ @identifier.hash
200
+ else
201
+ @method.hash
202
+ end
203
+ end
204
+
205
+ def call(*args, &block)
206
+ evaluate_method(method, *args, &block) if should_run_callback?(*args)
207
+ rescue LocalJumpError
208
+ raise ArgumentError,
209
+ "Cannot yield from a Proc type filter. The Proc must take two " +
210
+ "arguments and execute #call on the second argument."
211
+ end
212
+
213
+ private
214
+ def evaluate_method(method, *args, &block)
215
+ case method
216
+ when Symbol
217
+ object = args.shift
218
+ object.send(method, *args, &block)
219
+ when String
220
+ eval(method, args.first.instance_eval { binding })
221
+ when Proc, Method
222
+ method.call(*args, &block)
223
+ else
224
+ if method.respond_to?(kind)
225
+ method.send(kind, *args, &block)
226
+ else
227
+ raise ArgumentError,
228
+ "Callbacks must be a symbol denoting the method to call, a string to be evaluated, " +
229
+ "a block to be invoked, or an object responding to the callback method."
230
+ end
231
+ end
232
+ end
233
+
234
+ def should_run_callback?(*args)
235
+ [options[:if]].flatten.compact.all? { |a| evaluate_method(a, *args) } &&
236
+ ![options[:unless]].flatten.compact.any? { |a| evaluate_method(a, *args) }
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Clone
5
+ module InstanceMethods
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
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Descendants
5
+ module ClassMethods
6
+ def inherited(descendant)
7
+ descendants << descendant
8
+ super
9
+ end
10
+
11
+ def descendants
12
+ @descendants ||= []
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,120 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Dirty
5
+ module InstanceMethods
6
+ def method_missing(method, *args, &block)
7
+ if method.to_s =~ /(_changed\?|_change|_will_change!|_was)$/
8
+ method_suffix = $1
9
+ key = method.to_s.gsub(method_suffix, '')
10
+
11
+ if key_names.include?(key)
12
+ case method_suffix
13
+ when '_changed?'
14
+ key_changed?(key)
15
+ when '_change'
16
+ key_change(key)
17
+ when '_will_change!'
18
+ key_will_change!(key)
19
+ when '_was'
20
+ key_was(key)
21
+ end
22
+ else
23
+ super
24
+ end
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ def changed?
31
+ !changed_keys.empty?
32
+ end
33
+
34
+ def changed
35
+ changed_keys.keys
36
+ end
37
+
38
+ def changes
39
+ changed.inject({}) { |h, key| h[key] = key_change(key); h }
40
+ end
41
+
42
+ def initialize(*args)
43
+ super
44
+ changed_keys.clear if args.first.blank? || !new?
45
+ end
46
+
47
+ def save(*args)
48
+ if status = super
49
+ changed_keys.clear
50
+ end
51
+ status
52
+ end
53
+
54
+ def save!(*args)
55
+ status = super
56
+ changed_keys.clear
57
+ status
58
+ end
59
+
60
+ def reload(*args)
61
+ document = super
62
+ changed_keys.clear
63
+ document
64
+ end
65
+
66
+ private
67
+ def clone_key_value(key)
68
+ value = read_key(key)
69
+ value.duplicable? ? value.clone : value
70
+ rescue TypeError, NoMethodError
71
+ value
72
+ end
73
+
74
+ def changed_keys
75
+ @changed_keys ||= {}
76
+ end
77
+
78
+ def key_changed?(key)
79
+ changed_keys.include?(key)
80
+ end
81
+
82
+ def key_change(key)
83
+ [changed_keys[key], __send__(key)] if key_changed?(key)
84
+ end
85
+
86
+ def key_was(key)
87
+ key_changed?(key) ? changed_keys[key] : __send__(key)
88
+ end
89
+
90
+ def key_will_change!(key)
91
+ changed_keys[key] = clone_key_value(key)
92
+ end
93
+
94
+ def write_key(key, value)
95
+ key = key.to_s
96
+
97
+ if changed_keys.include?(key)
98
+ old = changed_keys[key]
99
+ changed_keys.delete(key) unless value_changed?(key, old, value)
100
+ else
101
+ old = clone_key_value(key)
102
+ changed_keys[key] = old if value_changed?(key, old, value)
103
+ end
104
+
105
+ super(key, value)
106
+ end
107
+
108
+ def value_changed?(key_name, old, value)
109
+ key = keys[key_name]
110
+
111
+ if key.number? && value.blank?
112
+ value = nil
113
+ end
114
+
115
+ old != value
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -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 = collection.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