numon 0.0.1

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