wonkavision 0.5.1 → 0.5.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.
- data/CHANGELOG.rdoc +4 -0
- data/lib/wonkavision.rb +6 -1
- data/lib/wonkavision/event_context.rb +9 -0
- data/lib/wonkavision/event_handler.rb +1 -0
- data/lib/wonkavision/plugins.rb +1 -1
- data/lib/wonkavision/plugins/business_activity.rb +13 -12
- data/lib/wonkavision/plugins/callbacks.rb +182 -0
- data/lib/wonkavision/plugins/event_handling.rb +44 -15
- data/lib/wonkavision/plugins/timeline.rb +9 -9
- data/lib/wonkavision/version.rb +1 -1
- data/test/event_handler_test.rb +9 -0
- data/test/log/test.log +5277 -0
- data/test/test_activity_models.rb +14 -6
- data/wonkavision.gemspec +4 -2
- metadata +6 -4
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
== 0.5.2
|
2
|
+
* Added callbacks to event handler, before_event and after_event. Refactored event handling hierarchy to be a little
|
3
|
+
less functional and a little more object oriented.
|
4
|
+
|
1
5
|
== 0.5.1
|
2
6
|
* Ensure that subscriptions to an event namespace will only get notified once per incoming message
|
3
7
|
|
data/lib/wonkavision.rb
CHANGED
@@ -7,6 +7,7 @@ dir = File.dirname(__FILE__)
|
|
7
7
|
"plugins",
|
8
8
|
"event_path_segment",
|
9
9
|
"event",
|
10
|
+
"event_context",
|
10
11
|
"event_namespace",
|
11
12
|
"event_coordinator",
|
12
13
|
"event_binding",
|
@@ -14,6 +15,7 @@ dir = File.dirname(__FILE__)
|
|
14
15
|
"message_mapper/indifferent_access",
|
15
16
|
"message_mapper/map",
|
16
17
|
"message_mapper",
|
18
|
+
"plugins/callbacks",
|
17
19
|
"plugins/event_handling",
|
18
20
|
"plugins/business_activity",
|
19
21
|
"plugins/timeline",
|
@@ -43,7 +45,7 @@ module Wonkavision
|
|
43
45
|
|
44
46
|
def namespace_wildcard_character
|
45
47
|
@namespace_wildcard_character = "*"
|
46
|
-
end
|
48
|
+
end
|
47
49
|
|
48
50
|
def is_absolute_path(path)
|
49
51
|
path.to_s[0..0] == event_path_separator
|
@@ -63,6 +65,9 @@ module Wonkavision
|
|
63
65
|
end
|
64
66
|
end
|
65
67
|
|
68
|
+
class WonkavisionError < StandardError #:nodoc:
|
69
|
+
end
|
70
|
+
|
66
71
|
|
67
72
|
|
68
73
|
end
|
data/lib/wonkavision/plugins.rb
CHANGED
@@ -21,7 +21,7 @@ module Wonkavision
|
|
21
21
|
include mod::InstanceMethods if mod.const_defined?(:InstanceMethods)
|
22
22
|
extend mod::Fields if mod.const_defined?(:Fields)
|
23
23
|
include mod::Fields if mod.const_defined?(:Fields)
|
24
|
-
mod.configure(self,options)
|
24
|
+
mod.configure(self,options) if mod.respond_to?(:configure)
|
25
25
|
wonkavision_plugins << mod
|
26
26
|
end
|
27
27
|
alias use plug
|
@@ -17,22 +17,26 @@ module Wonkavision
|
|
17
17
|
end
|
18
18
|
|
19
19
|
module ClassMethods
|
20
|
+
def instantiate_handler(event_context)
|
21
|
+
correlation_id = event_context.data[event_correlation_id_key.to_s]
|
22
|
+
find_activity_instance(correlation_id_field,correlation_id)
|
23
|
+
end
|
20
24
|
|
21
25
|
def event(name,*args,&block)
|
22
|
-
handle(name,args) do
|
23
|
-
|
26
|
+
handle(name,args) do
|
27
|
+
ctx = @wonkavision_event_context
|
24
28
|
result = :ok
|
25
29
|
if (block_given?)
|
26
30
|
result = case block.arity
|
27
|
-
when 3 then yield
|
28
|
-
when 2 then yield
|
29
|
-
when 1 then yield
|
30
|
-
else
|
31
|
+
when 3 then yield ctx.data,ctx.path,self
|
32
|
+
when 2 then yield ctx.data, ctx.path
|
33
|
+
when 1 then yield ctx.data
|
34
|
+
else instance_eval &block
|
31
35
|
end
|
32
36
|
end
|
33
37
|
unless result == :handled
|
34
|
-
result = update_activity(
|
35
|
-
|
38
|
+
result = self.class.update_activity(self,ctx.data) unless result == :updated
|
39
|
+
save!
|
36
40
|
end
|
37
41
|
result
|
38
42
|
end
|
@@ -56,10 +60,7 @@ module Wonkavision
|
|
56
60
|
correlation_ids << {:model=>model_field.to_s, :event=>event_field.to_s}
|
57
61
|
end
|
58
62
|
|
59
|
-
|
60
|
-
correlation_id = event_data[event_correlation_id_key.to_s]
|
61
|
-
find_activity_instance(correlation_id_field,correlation_id)
|
62
|
-
end
|
63
|
+
|
63
64
|
|
64
65
|
end
|
65
66
|
|
@@ -0,0 +1,182 @@
|
|
1
|
+
# The note you see below, 'Almost all of this callback stuff...', along with all the code adapted from ActiveSupport,
|
2
|
+
# is in turn adapted from MongoMapper, and for the same reasons.
|
3
|
+
# encoding: UTF-8
|
4
|
+
# Almost all of this callback stuff is pulled directly from ActiveSupport
|
5
|
+
# in the interest of support rails 2 and 3 at the same time and is the
|
6
|
+
# same copyright as rails.
|
7
|
+
module Wonkavision
|
8
|
+
module Plugins
|
9
|
+
module Callbacks
|
10
|
+
def self.configure(handler,options)
|
11
|
+
handler.define_wonkavision_callbacks :before_event,
|
12
|
+
:after_event
|
13
|
+
|
14
|
+
handler.alias_method_chain :handle_event, :callbacks
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
#The ODM library may already have taken care of mixing in callback functioanlity,
|
19
|
+
#in which case we'll just use that
|
20
|
+
def define_wonkavision_callbacks(*callbacks)
|
21
|
+
callbacks.each do |callback|
|
22
|
+
class_eval <<-"end_eval"
|
23
|
+
def self.#{callback}(*methods, &block)
|
24
|
+
callbacks = CallbackChain.build(:#{callback}, *methods, &block)
|
25
|
+
@#{callback}_callbacks ||= CallbackChain.new
|
26
|
+
@#{callback}_callbacks.concat callbacks
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.#{callback}_callback_chain
|
30
|
+
@#{callback}_callbacks ||= CallbackChain.new
|
31
|
+
|
32
|
+
if superclass.respond_to?(:#{callback}_callback_chain)
|
33
|
+
CallbackChain.new(
|
34
|
+
superclass.#{callback}_callback_chain +
|
35
|
+
@#{callback}_callbacks
|
36
|
+
)
|
37
|
+
else
|
38
|
+
@#{callback}_callbacks
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end_eval
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module InstanceMethods
|
47
|
+
def handle_event_with_callbacks
|
48
|
+
ctx = @wonkavision_event_context
|
49
|
+
run_wonkavision_callbacks(:before_event)
|
50
|
+
handle_event_without_callbacks
|
51
|
+
run_wonkavision_callbacks(:after_event)
|
52
|
+
end
|
53
|
+
|
54
|
+
def run_wonkavision_callbacks(kind, options={}, &block)
|
55
|
+
callback_chain_method = "#{kind}_callback_chain"
|
56
|
+
return unless self.class.respond_to?(callback_chain_method)
|
57
|
+
self.class.send(callback_chain_method).run(self, options, &block)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class CallbackChain < Array
|
62
|
+
def self.build(kind, *methods, &block)
|
63
|
+
methods, options = extract_options(*methods, &block)
|
64
|
+
methods.map! { |method| Callback.new(kind, method, options) }
|
65
|
+
new(methods)
|
66
|
+
end
|
67
|
+
|
68
|
+
def run(object, options={}, &terminator)
|
69
|
+
enumerator = options[:enumerator] || :each
|
70
|
+
|
71
|
+
unless block_given?
|
72
|
+
send(enumerator) { |callback| callback.call(object) }
|
73
|
+
else
|
74
|
+
send(enumerator) do |callback|
|
75
|
+
result = callback.call(object)
|
76
|
+
break result if terminator.call(result, object)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def replace_or_append!(chain)
|
82
|
+
if index = index(chain)
|
83
|
+
self[index] = chain
|
84
|
+
else
|
85
|
+
self << chain
|
86
|
+
end
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
def find(callback, &block)
|
91
|
+
select { |c| c == callback && (!block_given? || yield(c)) }.first
|
92
|
+
end
|
93
|
+
|
94
|
+
def delete(callback)
|
95
|
+
super(callback.is_a?(Callback) ? callback : find(callback))
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
def self.extract_options(*methods, &block)
|
100
|
+
methods.flatten!
|
101
|
+
options = methods.extract_options!
|
102
|
+
methods << block if block_given?
|
103
|
+
return methods, options
|
104
|
+
end
|
105
|
+
|
106
|
+
def extract_options(*methods, &block)
|
107
|
+
self.class.extract_options(*methods, &block)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class Callback
|
112
|
+
attr_reader :kind, :method, :identifier, :options
|
113
|
+
|
114
|
+
def initialize(kind, method, options={})
|
115
|
+
@kind = kind
|
116
|
+
@method = method
|
117
|
+
@identifier = options[:identifier]
|
118
|
+
@options = options
|
119
|
+
end
|
120
|
+
|
121
|
+
def ==(other)
|
122
|
+
case other
|
123
|
+
when Callback
|
124
|
+
(self.identifier && self.identifier == other.identifier) || self.method == other.method
|
125
|
+
else
|
126
|
+
(self.identifier && self.identifier == other) || self.method == other
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def eql?(other)
|
131
|
+
self == other
|
132
|
+
end
|
133
|
+
|
134
|
+
def dup
|
135
|
+
self.class.new(@kind, @method, @options.dup)
|
136
|
+
end
|
137
|
+
|
138
|
+
def hash
|
139
|
+
if @identifier
|
140
|
+
@identifier.hash
|
141
|
+
else
|
142
|
+
@method.hash
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def call(*args, &block)
|
147
|
+
evaluate_method(method, *args, &block) if should_run_callback?(*args)
|
148
|
+
rescue LocalJumpError
|
149
|
+
raise ArgumentError,
|
150
|
+
"Cannot yield from a Proc type filter. The Proc must take two " +
|
151
|
+
"arguments and execute #call on the second argument."
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
def evaluate_method(method, *args, &block)
|
156
|
+
case method
|
157
|
+
when Symbol
|
158
|
+
object = args.shift
|
159
|
+
object.send(method, *args, &block)
|
160
|
+
when String
|
161
|
+
eval(method, args.first.instance_eval { binding })
|
162
|
+
when Proc, Method
|
163
|
+
method.call(*args, &block)
|
164
|
+
else
|
165
|
+
if method.respond_to?(kind)
|
166
|
+
method.send(kind, *args, &block)
|
167
|
+
else
|
168
|
+
raise ArgumentError,
|
169
|
+
"Callbacks must be a symbol denoting the method to call, a string to be evaluated, " +
|
170
|
+
"a block to be invoked, or an object responding to the callback method."
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def should_run_callback?(*args)
|
176
|
+
[options[:if]].flatten.compact.all? { |a| evaluate_method(a, *args) } &&
|
177
|
+
![options[:unless]].flatten.compact.any? { |a| evaluate_method(a, *args) }
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -11,6 +11,7 @@ module Wonkavision
|
|
11
11
|
|
12
12
|
handler.write_inheritable_attribute :maps, []
|
13
13
|
handler.class_inheritable_reader :maps
|
14
|
+
|
14
15
|
end
|
15
16
|
|
16
17
|
module ClassMethods
|
@@ -36,23 +37,53 @@ module Wonkavision
|
|
36
37
|
def handle(name,*args,&block)
|
37
38
|
binding = Wonkavision::EventBinding.new(name,self,*args)
|
38
39
|
binding.subscribe_to_events do |event_data,event_path|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
when 1 then yield event_data
|
44
|
-
else yield
|
45
|
-
end
|
46
|
-
end
|
40
|
+
ctx = Wonkavision::EventContext.new(event_data,event_path,binding,block)
|
41
|
+
handler = instantiate_handler(ctx)
|
42
|
+
handler.instance_variable_set(:@wonkavision_event_context, ctx)
|
43
|
+
handler.handle_event
|
47
44
|
end
|
48
45
|
bindings << binding
|
49
46
|
binding
|
50
47
|
end
|
48
|
+
|
49
|
+
def instantiate_handler(event_context)
|
50
|
+
self.new
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
module InstanceMethods
|
56
|
+
|
57
|
+
def handled?
|
58
|
+
@wonkavision_event_handled ||= false
|
59
|
+
end
|
60
|
+
|
61
|
+
def handled=(handled)
|
62
|
+
@wonkavision_event_handled = handled
|
63
|
+
end
|
64
|
+
|
65
|
+
def event_context
|
66
|
+
@wonkavision_event_context
|
67
|
+
end
|
51
68
|
|
52
|
-
|
69
|
+
def handle_event
|
70
|
+
ctx = @wonkavision_event_context
|
71
|
+
ctx.data = map(ctx.data,ctx.path)
|
72
|
+
handler = ctx.callback
|
53
73
|
|
54
|
-
|
55
|
-
|
74
|
+
if handler && handler.respond_to?(:call) && handler.respond_to?(:arity)
|
75
|
+
case handler.arity
|
76
|
+
when 3 then handler.call(ctx.data,ctx.path,self)
|
77
|
+
when 2 then handler.call(ctx.data,ctx.path)
|
78
|
+
when 1 then handler.call (ctx.data)
|
79
|
+
else instance_eval &handler
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
protected
|
85
|
+
def map(data,path)
|
86
|
+
self.class.maps.each do |map_def|
|
56
87
|
condition = map_def[0]
|
57
88
|
map_block = map_def[1]
|
58
89
|
return Wonkavision::MessageMapper.execute(map_block,data) if map?(condition,data,path)
|
@@ -61,18 +92,16 @@ module Wonkavision
|
|
61
92
|
end
|
62
93
|
|
63
94
|
def map?(condition,data,path)
|
64
|
-
return true unless condition && condition.to_s != 'all'
|
95
|
+
return true unless condition && condition.to_s != 'all' && condition.to_s != '*'
|
65
96
|
return path =~ condition if condition.is_a?(Regexp)
|
66
97
|
if (condition.is_a?(Proc))
|
67
98
|
return condition.call if condition.arity <= 0
|
68
99
|
return condition.call(path) if condition.arity == 1
|
69
100
|
return condition.call(path,data)
|
70
101
|
end
|
71
|
-
#default behavior
|
72
|
-
header.properties[:routing_key] == filter.to_s
|
73
102
|
end
|
74
103
|
end
|
75
|
-
|
104
|
+
|
76
105
|
end
|
77
106
|
end
|
78
107
|
end
|
@@ -27,20 +27,21 @@ module Wonkavision
|
|
27
27
|
module ClassMethods
|
28
28
|
|
29
29
|
def milestone(name,*args)
|
30
|
-
timeline_milestones << event(name,*args) do
|
31
|
-
|
32
|
-
|
30
|
+
timeline_milestones << event(name,*args) do
|
31
|
+
ctx = @wonkavision_event_context
|
32
|
+
event_time = self.class.extract_event_time(ctx.data,ctx.path)
|
33
|
+
prev_event_time = self[timeline_field][name]
|
33
34
|
unless prev_event_time
|
34
|
-
|
35
|
+
self[timeline_field][name] = event_time
|
35
36
|
#If the event being processed happened earlier than a previously
|
36
37
|
#recorded event, we don't want to overwrite state of the activity, as
|
37
38
|
#it is already more up to date than the incoming event.
|
38
|
-
latest_ms =
|
39
|
+
latest_ms = self[latest_milestone_field]
|
39
40
|
unless latest_ms &&
|
40
|
-
(last_event =
|
41
|
+
(last_event = self[timeline_field][latest_ms]) &&
|
41
42
|
last_event > event_time
|
42
|
-
update_activity(
|
43
|
-
|
43
|
+
self.class.update_activity(self,ctx.data)
|
44
|
+
self[latest_milestone_field] = name
|
44
45
|
end
|
45
46
|
:updated
|
46
47
|
else
|
@@ -56,7 +57,6 @@ module Wonkavision
|
|
56
57
|
time ? time.to_time : nil
|
57
58
|
end
|
58
59
|
|
59
|
-
private
|
60
60
|
def extract_event_time(event_data,event_path)
|
61
61
|
convert_time(event_data.delete(event_time_key.to_s)) || Time.now.utc
|
62
62
|
end
|
data/lib/wonkavision/version.rb
CHANGED
data/test/event_handler_test.rb
CHANGED
@@ -55,6 +55,15 @@ class EventHandlerTest < ActiveSupport::TestCase
|
|
55
55
|
Wonkavision.event_coordinator.receive_event("vermicious/oompa",1);
|
56
56
|
assert_equal 1, TestEventHandler.knids.length
|
57
57
|
end
|
58
|
+
|
59
|
+
should "process any defined callbacks" do
|
60
|
+
TestEventHandler.reset
|
61
|
+
|
62
|
+
Wonkavision.event_coordinator.receive_event("vermicious/knid",1)
|
63
|
+
#3 execs of each kind of callback, one for event subscription, one for namespace sub and global sub
|
64
|
+
assert_equal 3 * 2, TestEventHandler.callbacks.length
|
65
|
+
end
|
66
|
+
|
58
67
|
end
|
59
68
|
|
60
69
|
end
|