wonkavision 0.5.4 → 0.5.9
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +28 -16
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -21
- data/Rakefile +47 -47
- data/lib/wonkavision.rb +75 -74
- data/lib/wonkavision/acts_as_oompa_loompa.rb +22 -22
- data/lib/wonkavision/event_binding.rb +21 -21
- data/lib/wonkavision/event_context.rb +9 -9
- data/lib/wonkavision/event_coordinator.rb +75 -75
- data/lib/wonkavision/event_handler.rb +15 -15
- data/lib/wonkavision/event_namespace.rb +79 -79
- data/lib/wonkavision/event_path_segment.rb +35 -35
- data/lib/wonkavision/message_mapper.rb +30 -30
- data/lib/wonkavision/message_mapper/indifferent_access.rb +30 -26
- data/lib/wonkavision/message_mapper/map.rb +241 -153
- data/lib/wonkavision/persistence/mongo_mapper_adapter.rb +32 -32
- data/lib/wonkavision/persistence/mongoid_adapter.rb +32 -0
- data/lib/wonkavision/plugins.rb +30 -30
- data/lib/wonkavision/plugins/business_activity.rb +92 -92
- data/lib/wonkavision/plugins/business_activity/event_binding.rb +15 -15
- data/lib/wonkavision/plugins/callbacks.rb +182 -182
- data/lib/wonkavision/plugins/event_handling.rb +111 -111
- data/lib/wonkavision/plugins/timeline.rb +79 -79
- data/lib/wonkavision/version.rb +3 -3
- data/test/business_activity_test.rb +31 -31
- data/test/event_handler_test.rb +68 -69
- data/test/event_namespace_test.rb +108 -108
- data/test/event_path_segment_test.rb +41 -41
- data/test/log/test.log +817 -18354
- data/test/map_test.rb +315 -201
- data/test/message_mapper_test.rb +20 -20
- data/test/test_activity_models.rb +72 -72
- data/test/test_helper.rb +70 -63
- data/test/timeline_test.rb +55 -61
- data/test/wonkavision_test.rb +9 -9
- metadata +72 -12
- data/wonkavision.gemspec +0 -97
@@ -1,75 +1,75 @@
|
|
1
|
-
module Wonkavision
|
2
|
-
class EventCoordinator
|
3
|
-
|
4
|
-
attr_reader :root_namespace
|
5
|
-
|
6
|
-
def initialize
|
7
|
-
@root_namespace = Wonkavision::EventNamespace.new
|
8
|
-
@lock = Mutex.new
|
9
|
-
#@event_cache = {}
|
10
|
-
@incoming_event_filters = []
|
11
|
-
end
|
12
|
-
|
13
|
-
def before_receive_event(&block)
|
14
|
-
@incoming_event_filters << block
|
15
|
-
end
|
16
|
-
|
17
|
-
def clear_filters
|
18
|
-
@incoming_event_filters = []
|
19
|
-
end
|
20
|
-
|
21
|
-
def configure(&block)
|
22
|
-
self.instance_eval(&block)
|
23
|
-
end
|
24
|
-
|
25
|
-
def map ()
|
26
|
-
yield root_namespace if block_given?
|
27
|
-
end
|
28
|
-
|
29
|
-
def subscribe(event_path,final_segment_type=nil,&block)
|
30
|
-
event_path, final_segment_type = *detect_final_segment(event_path) unless final_segment_type
|
31
|
-
segment = (event_path.blank? ? root_namespace : root_namespace.find_or_create(event_path,final_segment_type))
|
32
|
-
segment.subscribe(&block)
|
33
|
-
end
|
34
|
-
|
35
|
-
def receive_event(event_path, event_data)
|
36
|
-
@lock.synchronize do
|
37
|
-
#If process_incoming_event returns nil or false, it means a filter chose to abort
|
38
|
-
#the event processing, in which case we'll break for lunch.
|
39
|
-
return unless event_data = process_incoming_event(event_path,event_data)
|
40
|
-
|
41
|
-
event_path = Wonkavision.normalize_event_path(event_path)
|
42
|
-
targets = root_namespace.find_matching_segments(event_path).values
|
43
|
-
#If the event wasn't matched, maybe someone is subscribing to '/*' ?
|
44
|
-
targets = [root_namespace] if targets.blank?
|
45
|
-
targets.each{|target|target.notify_subscribers(event_data,event_path)}
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
protected
|
50
|
-
def process_incoming_event(event_path,event_data)
|
51
|
-
return nil unless event_data
|
52
|
-
@incoming_event_filters.each do |filter_block|
|
53
|
-
if (filter_block.arity < 1)
|
54
|
-
event_data = event_data.instance_eval(&filter_block)
|
55
|
-
elsif filter_block.arity == 1
|
56
|
-
event_data = filter_block.call(event_data)
|
57
|
-
else
|
58
|
-
event_data = filter_block.call(event_data,event_path)
|
59
|
-
end
|
60
|
-
return nil unless event_data
|
61
|
-
end
|
62
|
-
event_data
|
63
|
-
end
|
64
|
-
|
65
|
-
def detect_final_segment(event_path)
|
66
|
-
parts = Wonkavision.split(event_path)
|
67
|
-
if parts[-1] == Wonkavision.namespace_wildcard_character
|
68
|
-
[Wonkavision.join(parts.slice(0..-2)),:namespace]
|
69
|
-
else
|
70
|
-
[event_path,:event]
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
end
|
75
|
-
end
|
1
|
+
module Wonkavision
|
2
|
+
class EventCoordinator
|
3
|
+
|
4
|
+
attr_reader :root_namespace
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@root_namespace = Wonkavision::EventNamespace.new
|
8
|
+
@lock = Mutex.new
|
9
|
+
#@event_cache = {}
|
10
|
+
@incoming_event_filters = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def before_receive_event(&block)
|
14
|
+
@incoming_event_filters << block
|
15
|
+
end
|
16
|
+
|
17
|
+
def clear_filters
|
18
|
+
@incoming_event_filters = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def configure(&block)
|
22
|
+
self.instance_eval(&block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def map ()
|
26
|
+
yield root_namespace if block_given?
|
27
|
+
end
|
28
|
+
|
29
|
+
def subscribe(event_path,final_segment_type=nil,&block)
|
30
|
+
event_path, final_segment_type = *detect_final_segment(event_path) unless final_segment_type
|
31
|
+
segment = (event_path.blank? ? root_namespace : root_namespace.find_or_create(event_path,final_segment_type))
|
32
|
+
segment.subscribe(&block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def receive_event(event_path, event_data)
|
36
|
+
@lock.synchronize do
|
37
|
+
#If process_incoming_event returns nil or false, it means a filter chose to abort
|
38
|
+
#the event processing, in which case we'll break for lunch.
|
39
|
+
return unless event_data = process_incoming_event(event_path,event_data)
|
40
|
+
|
41
|
+
event_path = Wonkavision.normalize_event_path(event_path)
|
42
|
+
targets = root_namespace.find_matching_segments(event_path).values
|
43
|
+
#If the event wasn't matched, maybe someone is subscribing to '/*' ?
|
44
|
+
targets = [root_namespace] if targets.blank?
|
45
|
+
targets.each{|target|target.notify_subscribers(event_data,event_path)}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
def process_incoming_event(event_path,event_data)
|
51
|
+
return nil unless event_data
|
52
|
+
@incoming_event_filters.each do |filter_block|
|
53
|
+
if (filter_block.arity < 1)
|
54
|
+
event_data = event_data.instance_eval(&filter_block)
|
55
|
+
elsif filter_block.arity == 1
|
56
|
+
event_data = filter_block.call(event_data)
|
57
|
+
else
|
58
|
+
event_data = filter_block.call(event_data,event_path)
|
59
|
+
end
|
60
|
+
return nil unless event_data
|
61
|
+
end
|
62
|
+
event_data
|
63
|
+
end
|
64
|
+
|
65
|
+
def detect_final_segment(event_path)
|
66
|
+
parts = Wonkavision.split(event_path)
|
67
|
+
if parts[-1] == Wonkavision.namespace_wildcard_character
|
68
|
+
[Wonkavision.join(parts.slice(0..-2)),:namespace]
|
69
|
+
else
|
70
|
+
[event_path,:event]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -1,15 +1,15 @@
|
|
1
|
-
module Wonkavision
|
2
|
-
|
3
|
-
module EventHandler
|
4
|
-
|
5
|
-
def self.included(handler)
|
6
|
-
handler.class_eval do
|
7
|
-
extend Plugins
|
8
|
-
use Plugins::EventHandling
|
9
|
-
use Plugins::Callbacks
|
10
|
-
end
|
11
|
-
|
12
|
-
super
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
1
|
+
module Wonkavision
|
2
|
+
|
3
|
+
module EventHandler
|
4
|
+
|
5
|
+
def self.included(handler)
|
6
|
+
handler.class_eval do
|
7
|
+
extend Plugins
|
8
|
+
use Plugins::EventHandling
|
9
|
+
use Plugins::Callbacks
|
10
|
+
end
|
11
|
+
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,79 +1,79 @@
|
|
1
|
-
module Wonkavision
|
2
|
-
|
3
|
-
class EventNamespace < EventPathSegment
|
4
|
-
|
5
|
-
attr_reader :children
|
6
|
-
|
7
|
-
def initialize(name=nil,namespace = nil,opts={})
|
8
|
-
super name, namespace,opts
|
9
|
-
@children=HashWithIndifferentAccess.new
|
10
|
-
end
|
11
|
-
|
12
|
-
def find_or_create (path, final_segment_type = :event)
|
13
|
-
if path.is_a?(Array)
|
14
|
-
child_name = path.shift
|
15
|
-
segment_type = path.blank? ? final_segment_type : :namespace
|
16
|
-
child = @children[child_name] ||= self.send(segment_type,child_name)
|
17
|
-
return child if path.blank?
|
18
|
-
raise "Events cannot have children. The path you requested is not valid" if child.is_a?(Wonkavision::Event)
|
19
|
-
child.find_or_create(path,final_segment_type)
|
20
|
-
else
|
21
|
-
path = Wonkavision.normalize_event_path(path)
|
22
|
-
source_ns = self
|
23
|
-
if (Wonkavision.is_absolute_path(path)) #indicates an absolute path, because it begins with a '/'
|
24
|
-
source_ns = root_namespace
|
25
|
-
path = path[1..-1]
|
26
|
-
end
|
27
|
-
source_ns.find_or_create(path.split(Wonkavision.event_path_separator), final_segment_type)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def find_matching_segments (event_path)
|
32
|
-
selected = {}
|
33
|
-
@children.each_value do |child|
|
34
|
-
if (child.is_a?(Wonkavision::Event))
|
35
|
-
select_segment(child,selected) if child.matches(event_path)
|
36
|
-
elsif (child.is_a?(Wonkavision::EventNamespace))
|
37
|
-
selected.merge!(child.find_matching_segments(event_path))
|
38
|
-
else
|
39
|
-
raise "An unexpected child type was encountered in find_matching_events #{child.class}"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
#If no child was found, and the event matches this namespace, we should add ourselves to the list.
|
43
|
-
#This is not necessary if any child event was located, because in that case event notifications
|
44
|
-
#would bubble up to us anyway. If no event was found, there's nobody to blow the bubble but us.
|
45
|
-
select_segment(self,selected) if selected.blank? && matches_event(event_path)
|
46
|
-
selected
|
47
|
-
end
|
48
|
-
|
49
|
-
def matches_event(event_path)
|
50
|
-
Wonkavision.split(path) == Wonkavision.split(event_path).slice(0..-2)
|
51
|
-
end
|
52
|
-
|
53
|
-
#dsl
|
54
|
-
def namespace(*args)
|
55
|
-
return super if args.blank?
|
56
|
-
name, opts = args.shift, (args.shift || {})
|
57
|
-
ns = @children[name] || Wonkavision::EventNamespace.new(name,self,opts)
|
58
|
-
yield ns if block_given?
|
59
|
-
@children[ns.name] = ns
|
60
|
-
ns
|
61
|
-
end
|
62
|
-
|
63
|
-
def event(*args)
|
64
|
-
name, opts = args.shift, args.extract_options!
|
65
|
-
opts[:source_events] = (opts[:source_events] || []).concat(args) unless args.blank?
|
66
|
-
evt = @children[name] || Wonkavision::Event.new(name,self,opts)
|
67
|
-
yield evt if block_given?
|
68
|
-
@children[evt.name] = evt
|
69
|
-
evt
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
def select_segment(segment,selected)
|
74
|
-
selected[segment.name] = segment
|
75
|
-
select_segment(segment.namespace,selected) if segment.namespace
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|
79
|
-
end
|
1
|
+
module Wonkavision
|
2
|
+
|
3
|
+
class EventNamespace < EventPathSegment
|
4
|
+
|
5
|
+
attr_reader :children
|
6
|
+
|
7
|
+
def initialize(name=nil,namespace = nil,opts={})
|
8
|
+
super name, namespace,opts
|
9
|
+
@children=HashWithIndifferentAccess.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_or_create (path, final_segment_type = :event)
|
13
|
+
if path.is_a?(Array)
|
14
|
+
child_name = path.shift
|
15
|
+
segment_type = path.blank? ? final_segment_type : :namespace
|
16
|
+
child = @children[child_name] ||= self.send(segment_type,child_name)
|
17
|
+
return child if path.blank?
|
18
|
+
raise "Events cannot have children. The path you requested is not valid" if child.is_a?(Wonkavision::Event)
|
19
|
+
child.find_or_create(path,final_segment_type)
|
20
|
+
else
|
21
|
+
path = Wonkavision.normalize_event_path(path)
|
22
|
+
source_ns = self
|
23
|
+
if (Wonkavision.is_absolute_path(path)) #indicates an absolute path, because it begins with a '/'
|
24
|
+
source_ns = root_namespace
|
25
|
+
path = path[1..-1]
|
26
|
+
end
|
27
|
+
source_ns.find_or_create(path.split(Wonkavision.event_path_separator), final_segment_type)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_matching_segments (event_path)
|
32
|
+
selected = {}
|
33
|
+
@children.each_value do |child|
|
34
|
+
if (child.is_a?(Wonkavision::Event))
|
35
|
+
select_segment(child,selected) if child.matches(event_path)
|
36
|
+
elsif (child.is_a?(Wonkavision::EventNamespace))
|
37
|
+
selected.merge!(child.find_matching_segments(event_path))
|
38
|
+
else
|
39
|
+
raise "An unexpected child type was encountered in find_matching_events #{child.class}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
#If no child was found, and the event matches this namespace, we should add ourselves to the list.
|
43
|
+
#This is not necessary if any child event was located, because in that case event notifications
|
44
|
+
#would bubble up to us anyway. If no event was found, there's nobody to blow the bubble but us.
|
45
|
+
select_segment(self,selected) if selected.blank? && matches_event(event_path)
|
46
|
+
selected
|
47
|
+
end
|
48
|
+
|
49
|
+
def matches_event(event_path)
|
50
|
+
Wonkavision.split(path) == Wonkavision.split(event_path).slice(0..-2)
|
51
|
+
end
|
52
|
+
|
53
|
+
#dsl
|
54
|
+
def namespace(*args)
|
55
|
+
return super if args.blank?
|
56
|
+
name, opts = args.shift, (args.shift || {})
|
57
|
+
ns = @children[name] || Wonkavision::EventNamespace.new(name,self,opts)
|
58
|
+
yield ns if block_given?
|
59
|
+
@children[ns.name] = ns
|
60
|
+
ns
|
61
|
+
end
|
62
|
+
|
63
|
+
def event(*args)
|
64
|
+
name, opts = args.shift, args.extract_options!
|
65
|
+
opts[:source_events] = (opts[:source_events] || []).concat(args) unless args.blank?
|
66
|
+
evt = @children[name] || Wonkavision::Event.new(name,self,opts)
|
67
|
+
yield evt if block_given?
|
68
|
+
@children[evt.name] = evt
|
69
|
+
evt
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
def select_segment(segment,selected)
|
74
|
+
selected[segment.name] = segment
|
75
|
+
select_segment(segment.namespace,selected) if segment.namespace
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
@@ -1,36 +1,36 @@
|
|
1
|
-
module Wonkavision
|
2
|
-
class EventPathSegment
|
3
|
-
attr_reader :name, :namespace, :options, :subscribers
|
4
|
-
|
5
|
-
def initialize(name = nil, namespace = nil, opts={})
|
6
|
-
@name = name
|
7
|
-
@namespace = namespace
|
8
|
-
@options = opts
|
9
|
-
@subscribers = []
|
10
|
-
end
|
11
|
-
|
12
|
-
def path
|
13
|
-
Wonkavision.join(namespace.blank? ? nil : namespace.path,name)
|
14
|
-
end
|
15
|
-
|
16
|
-
def subscribe(&block)
|
17
|
-
@subscribers << block
|
18
|
-
self
|
19
|
-
end
|
20
|
-
|
21
|
-
def notify_subscribers(event_data, event_path=self.path)
|
22
|
-
@subscribers.each do |sub|
|
23
|
-
sub.call(event_data,event_path)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def root_namespace
|
28
|
-
cur_namespace = self
|
29
|
-
while (cur_namespace.namespace)
|
30
|
-
cur_namespace=cur_namespace.namespace
|
31
|
-
end
|
32
|
-
cur_namespace
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
1
|
+
module Wonkavision
|
2
|
+
class EventPathSegment
|
3
|
+
attr_reader :name, :namespace, :options, :subscribers
|
4
|
+
|
5
|
+
def initialize(name = nil, namespace = nil, opts={})
|
6
|
+
@name = name
|
7
|
+
@namespace = namespace
|
8
|
+
@options = opts
|
9
|
+
@subscribers = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def path
|
13
|
+
Wonkavision.join(namespace.blank? ? nil : namespace.path,name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def subscribe(&block)
|
17
|
+
@subscribers << block
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def notify_subscribers(event_data, event_path=self.path)
|
22
|
+
@subscribers.each do |sub|
|
23
|
+
sub.call(event_data,event_path)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def root_namespace
|
28
|
+
cur_namespace = self
|
29
|
+
while (cur_namespace.namespace)
|
30
|
+
cur_namespace=cur_namespace.namespace
|
31
|
+
end
|
32
|
+
cur_namespace
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
36
|
end
|
@@ -1,30 +1,30 @@
|
|
1
|
-
module Wonkavision
|
2
|
-
|
3
|
-
module MessageMapper
|
4
|
-
|
5
|
-
class << self
|
6
|
-
def maps
|
7
|
-
@maps ||={}
|
8
|
-
end
|
9
|
-
|
10
|
-
def register(map_name,&block)
|
11
|
-
MessageMapper.maps[map_name] = block
|
12
|
-
end
|
13
|
-
|
14
|
-
def execute(map,data_source)
|
15
|
-
map_block = map.kind_of?(Proc) ? map : MessageMapper.maps[map]
|
16
|
-
|
17
|
-
raise "#{map} not found" unless map_block
|
18
|
-
message = MessageMapper::Map.new(data_source)
|
19
|
-
message.instance_eval(&map_block)
|
20
|
-
message
|
21
|
-
end
|
22
|
-
|
23
|
-
def register_map_directory(directory_path, recursive=true)
|
24
|
-
searcher = "#{recursive ? "*" : "**/*"}.rb"
|
25
|
-
Dir[File.join(directory_path,searcher)].each {|map| require map}
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
end
|
1
|
+
module Wonkavision
|
2
|
+
|
3
|
+
module MessageMapper
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def maps
|
7
|
+
@maps ||={}
|
8
|
+
end
|
9
|
+
|
10
|
+
def register(map_name,&block)
|
11
|
+
MessageMapper.maps[map_name] = block
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute(map,data_source)
|
15
|
+
map_block = map.kind_of?(Proc) ? map : MessageMapper.maps[map]
|
16
|
+
|
17
|
+
raise "#{map} not found" unless map_block
|
18
|
+
message = MessageMapper::Map.new(data_source)
|
19
|
+
message.instance_eval(&map_block)
|
20
|
+
message
|
21
|
+
end
|
22
|
+
|
23
|
+
def register_map_directory(directory_path, recursive=true)
|
24
|
+
searcher = "#{recursive ? "*" : "**/*"}.rb"
|
25
|
+
Dir[File.join(directory_path,searcher)].each {|map| require map}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|