mongomatic 0.6.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -201,6 +201,35 @@ Mongomatic doesn't have any kind of special support for relationship management
201
201
  end
202
202
  end
203
203
 
204
+ == Observers
205
+
206
+ Mongomatic does have one thing in common with ActiveRecord and that is observers. You can add observers to your class to decouple distinct functionality in callbacks. To add observation to your model you must include the Mongomatic::Observable module. All observers inherit from Mongomatic::Observer.
207
+
208
+ Unlike ActiveRecord the existence of a CollectionNameObserver will automatically add the observer the CollectionName class. Instead you must use the observer macro or CollectionName.add_observer
209
+
210
+ You can add your own observers to CollectionName using CollectionName.add_observer(klass).
211
+
212
+ class MyCustomCallbacks < Mongomatic::Observer
213
+ def after_insert_or_update
214
+ puts "after insert or update"
215
+ end
216
+ end
217
+
218
+ class Person < Mongomatic::Base
219
+ include Mongomatic::Observable
220
+ observer :PersonObserver
221
+ observer MyCustomCallbacks
222
+ end
223
+
224
+ # this observer is automatically added to the Person class
225
+ class PersonObserver < Mongomatic::Observer
226
+ def after_insert(instance)
227
+ puts "new person inserted"
228
+ end
229
+ end
230
+
231
+ It is worth noting that you should be careful the operations you perform on the instance of your class passed to the observer callbacks. Calling operations that invoke callbacks can result in an infinite loop if improperly structured. To avoid this future implementations may only pass the document (instance of Mongomatic::MHash) to observers.
232
+
204
233
  == Note on Patches/Pull Requests
205
234
 
206
235
  * Fork the project.
@@ -147,7 +147,18 @@ module Mongomatic
147
147
  field, hash = hash_for_field(key.to_s, true)
148
148
  hash[field]
149
149
  end
150
-
150
+
151
+ ##
152
+ # Same as Hash#delete
153
+ #
154
+ # mydoc.delete("name")
155
+ # => "Ben"
156
+ # mydoc.has_hey?("name")
157
+ # => false
158
+ def delete(key)
159
+ @doc.delete(key)
160
+ end
161
+
151
162
  # Fetch a field (just like a hash):
152
163
  # mydoc["name"]
153
164
  # => "Ben"
@@ -297,6 +308,8 @@ module Mongomatic
297
308
  end
298
309
 
299
310
  def do_callback(meth)
311
+ notify(meth) if self.class.included_modules.include?(Mongomatic::Observable) # TODO entire block is smelly, doesnt belong here
312
+
300
313
  return false unless respond_to?(meth, true)
301
314
  send(meth)
302
315
  end
@@ -32,6 +32,14 @@ module Mongomatic
32
32
  !(@errors.any? { |k,v| v && !v.empty? })
33
33
  end
34
34
 
35
+ def any?
36
+ !empty?
37
+ end
38
+
39
+ def count
40
+ @errors.values.inject(0) { |sum, errors| sum += errors.size }
41
+ end
42
+
35
43
  def full_messages
36
44
  full_messages = []
37
45
  @errors.each do |field, messages|
@@ -0,0 +1,46 @@
1
+ module Mongomatic
2
+ module Observable
3
+ def self.included(base)
4
+ base.send(:extend, ClassMethods)
5
+ end
6
+
7
+ def notify(meth, opts = {})
8
+ self.class.observers.each do |observer|
9
+ @observer_cache ||= {}
10
+ unless observer_klass = @observer_cache[observer]
11
+ @observer_cache[observer] = observer_klass = Object.const_get(observer) if Module.const_defined?(observer)
12
+ end
13
+
14
+ if observer_klass
15
+ instance = observer_klass.new
16
+ instance.send(meth, self, opts) if instance.respond_to?(meth)
17
+ end
18
+ end
19
+ end
20
+
21
+ module ClassMethods
22
+ def observers
23
+ @observers ||= []
24
+ end
25
+
26
+ def add_observer(klass)
27
+ @observers ||= []
28
+ @observers << klass.to_s.to_sym unless @observers.include?(klass.to_s.to_sym)
29
+ end
30
+ alias :observer :add_observer
31
+
32
+ def has_observer?(klass_or_sym)
33
+ case klass_or_sym
34
+ when Symbol
35
+ @observers.include?(klass)
36
+ else
37
+ @observers.include?(klass.to_s.to_sym)
38
+ end
39
+ end
40
+
41
+ def remove_observers
42
+ @observers = []
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,4 @@
1
+ module Mongomatic
2
+ class Observer
3
+ end
4
+ end
data/lib/mongomatic.rb CHANGED
@@ -5,11 +5,14 @@ gem "activesupport", ">= 2.3.5"
5
5
  require "bson"
6
6
  require "mongo"
7
7
 
8
- begin
8
+ require 'active_support/version'
9
+
10
+ if ActiveSupport::VERSION::MAJOR == 3
11
+ gem 'i18n', '>= 0.4.2'
9
12
  require 'active_support/core_ext/object/blank' # newer versions of active_support (>= 3.0)
10
13
  require 'active_support/core_ext/hash' # newer versions of active_support (>= 3.0)
11
- rescue LoadError => e
12
- require 'active_support/all' # support older versions of active_support (<= 2.3.5)
14
+ else
15
+ require 'active_support'
13
16
  end
14
17
 
15
18
  module Mongomatic
@@ -29,6 +32,8 @@ module Mongomatic
29
32
  end
30
33
  end
31
34
 
35
+ require "#{File.dirname(__FILE__)}/mongomatic/observer"
36
+ require "#{File.dirname(__FILE__)}/mongomatic/observable"
32
37
  require "#{File.dirname(__FILE__)}/mongomatic/exceptions"
33
38
  require "#{File.dirname(__FILE__)}/mongomatic/util"
34
39
  require "#{File.dirname(__FILE__)}/mongomatic/m_hash"
data/test/helper.rb CHANGED
@@ -99,13 +99,21 @@ class Thing < Mongomatic::Base
99
99
  end
100
100
 
101
101
  class Foobar < Mongomatic::Base
102
+ include Mongomatic::Observable
103
+ observer :FoobarObserver
104
+
102
105
  def validate
103
106
  errors << ["color", "must not be blank"] if self["color"].blank?
104
107
  errors << "missing style" if self["style"].blank?
105
108
  end
106
109
  end
107
110
 
111
+ class RigObserver < Mongomatic::Observer
112
+ end
113
+
108
114
  class Rig < Mongomatic::Base
115
+ include Mongomatic::Observable
116
+
109
117
  # :cast => true, :raise => false is the default
110
118
  typed_field "age", :type => :fixnum, :cast => true
111
119
  typed_field "manufacturer.name", :type => :string, :cast => true
@@ -0,0 +1,200 @@
1
+ require 'helper'
2
+ require 'minitest/autorun'
3
+
4
+ class MyCoolCallbacks < Mongomatic::Observer
5
+ end
6
+
7
+ class ThingObserver < Mongomatic::Observer
8
+ end
9
+
10
+ class FoobarObserver < Mongomatic::Observer
11
+ class << self
12
+ attr_accessor :observable_instance, :observer_opts
13
+
14
+ def observer_tests
15
+ @observer_tests || []
16
+ end
17
+
18
+ def add_observer_test_val(val)
19
+ @observer_tests ||= []
20
+ @observer_tests << val
21
+ end
22
+ end
23
+
24
+ def do_something(instance, opts)
25
+ self.class.observer_opts = opts
26
+ self.class.add_observer_test_val(:do_something)
27
+ end
28
+
29
+ def before_validate(instance, opts)
30
+ self.class.observable_instance = instance
31
+ self.class.add_observer_test_val(:before_validate)
32
+ end
33
+
34
+ def after_validate(instance, opts)
35
+ self.class.add_observer_test_val(:after_validate)
36
+ end
37
+
38
+ def before_insert(instance, opts)
39
+ self.class.add_observer_test_val(:before_insert)
40
+ end
41
+
42
+ def before_insert_or_update(instance, opts)
43
+ self.class.add_observer_test_val(:before_insert_or_update)
44
+ end
45
+
46
+ def after_insert_or_update(instance, opts)
47
+ self.class.add_observer_test_val(:after_insert_or_update)
48
+ end
49
+
50
+ def after_insert(instance, opts)
51
+ self.class.add_observer_test_val(:after_insert)
52
+ end
53
+
54
+ def before_update(instance, opts)
55
+ self.class.add_observer_test_val(:before_update)
56
+ end
57
+
58
+ def after_update(instance, opts)
59
+ self.class.add_observer_test_val(:after_update)
60
+ end
61
+
62
+ def before_remove(instance, opts)
63
+ self.class.add_observer_test_val(:before_remove)
64
+ end
65
+
66
+ def after_remove(instance, opts)
67
+ self.class.add_observer_test_val(:after_remove)
68
+ end
69
+ end
70
+
71
+ class TestObservable < MiniTest::Unit::TestCase
72
+ def test_add_observer_to_class
73
+ Person.send(:include, Mongomatic::Observable)
74
+ Person.remove_observers
75
+
76
+ assert_equal [], Person.observers
77
+
78
+ Person.add_observer(MyCoolCallbacks)
79
+ assert_equal Person.observers, [:MyCoolCallbacks]
80
+ end
81
+
82
+ def test_add_observer_to_class_as_symbol
83
+ Person.send(:include, Mongomatic::Observable)
84
+ Person.remove_observers
85
+
86
+ assert_equal [], Person.observers
87
+ Person.add_observer(:MyCoolCallbacks)
88
+
89
+ assert_equal Person.observers, [:MyCoolCallbacks]
90
+ end
91
+
92
+ def test_add_observer_using_directive
93
+ assert_equal Foobar.observers, [:FoobarObserver]
94
+ end
95
+
96
+ def test_does_not_bomb_with_nonexistent_observer
97
+ Person.send(:include, Mongomatic::Observable)
98
+ Person.remove_observers
99
+
100
+ Person.add_observer(:DoesNotExist)
101
+ p = Person.new
102
+
103
+ p.valid?
104
+ end
105
+
106
+ def test_passes_instance_to_observer
107
+ f = Foobar.new
108
+ f.valid?
109
+
110
+ assert_equal f, FoobarObserver.observable_instance
111
+ end
112
+
113
+ def test_before_validate
114
+ f = Foobar.new
115
+ f.valid?
116
+
117
+ assert FoobarObserver.observer_tests.include?(:before_validate)
118
+ end
119
+
120
+ def test_after_validate
121
+ f = Foobar.new
122
+ f.valid?
123
+
124
+ assert FoobarObserver.observer_tests.include?(:after_validate)
125
+ end
126
+
127
+ def test_before_insert
128
+ f = Foobar.new('style' => 'cool', 'color' => 'green')
129
+ f.insert
130
+
131
+ assert FoobarObserver.observer_tests.include?(:before_insert)
132
+ end
133
+
134
+ def test_after_insert
135
+ f = Foobar.new('style' => 'cool', 'color' => 'green')
136
+ f.insert
137
+
138
+ assert FoobarObserver.observer_tests.include?(:after_insert)
139
+ end
140
+
141
+ def test_before_insert_or_update
142
+ f = Foobar.new('style' => 'cool', 'color' => 'green')
143
+ f.insert
144
+
145
+ assert FoobarObserver.observer_tests.include?(:before_insert_or_update)
146
+ end
147
+
148
+ def test_after_insert_or_update
149
+ f = Foobar.new('style' => 'cool', 'color' => 'green')
150
+ f.insert
151
+
152
+ assert FoobarObserver.observer_tests.include?(:after_insert_or_update)
153
+ end
154
+
155
+ def test_before_update
156
+ f = Foobar.new('style' => 'cool', 'color' => 'green')
157
+ f.insert
158
+ f.update
159
+
160
+ assert FoobarObserver.observer_tests.include?(:before_update)
161
+ end
162
+
163
+ def test_after_update
164
+ f = Foobar.new('style' => 'cool', 'color' => 'green')
165
+ f.insert
166
+ f.update
167
+
168
+ assert FoobarObserver.observer_tests.include?(:after_update)
169
+ end
170
+
171
+ def test_before_remove
172
+ f = Foobar.new('style' => 'cool', 'color' => 'green')
173
+ f.insert
174
+ f.remove
175
+
176
+ assert FoobarObserver.observer_tests.include?(:before_remove)
177
+ end
178
+
179
+ def test_after_remove
180
+ f = Foobar.new('style' => 'cool', 'color' => 'green')
181
+ f.insert
182
+ f.remove
183
+
184
+ assert FoobarObserver.observer_tests.include?(:after_remove)
185
+ end
186
+
187
+ def test_custom_callback
188
+ opts = {:a => 1234}
189
+ f = Foobar.new('style' => 'cool', 'color' => 'green')
190
+ class << f
191
+ def do_something
192
+ notify(:do_something, {:a => 1234})
193
+ end
194
+ end
195
+ f.do_something
196
+
197
+ assert FoobarObserver.observer_tests.include?(:do_something)
198
+ assert_equal opts, FoobarObserver.observer_opts
199
+ end
200
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 6
8
- - 5
9
- version: 0.6.5
7
+ - 7
8
+ - 0
9
+ version: 0.7.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ben Myles
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-11-16 00:00:00 -08:00
17
+ date: 2010-11-18 00:00:00 -08:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -99,6 +99,8 @@ files:
99
99
  - lib/mongomatic/expectations/present.rb
100
100
  - lib/mongomatic/m_hash.rb
101
101
  - lib/mongomatic/modifiers.rb
102
+ - lib/mongomatic/observable.rb
103
+ - lib/mongomatic/observer.rb
102
104
  - lib/mongomatic/type_converters.rb
103
105
  - lib/mongomatic/typed_fields.rb
104
106
  - lib/mongomatic/util.rb
@@ -109,6 +111,7 @@ files:
109
111
  - test/test_find.rb
110
112
  - test/test_misc.rb
111
113
  - test/test_modifiers.rb
114
+ - test/test_observable.rb
112
115
  - test/test_persistence.rb
113
116
  - test/test_typed_fields.rb
114
117
  - test/test_validations.rb
@@ -117,8 +120,8 @@ homepage: http://mongomatic.com/
117
120
  licenses: []
118
121
 
119
122
  post_install_message:
120
- rdoc_options:
121
- - --charset=UTF-8
123
+ rdoc_options: []
124
+
122
125
  require_paths:
123
126
  - lib
124
127
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -150,6 +153,7 @@ test_files:
150
153
  - test/test_find.rb
151
154
  - test/test_misc.rb
152
155
  - test/test_modifiers.rb
156
+ - test/test_observable.rb
153
157
  - test/test_persistence.rb
154
158
  - test/test_typed_fields.rb
155
159
  - test/test_validations.rb