wires 0.3.8 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/wires/channel.rb +71 -98
- data/lib/wires/convenience.rb +13 -35
- data/lib/wires/core_ext.rb +59 -43
- data/lib/wires/event.rb +68 -143
- data/lib/wires/hub.rb +36 -158
- data/lib/wires/router.rb +42 -0
- data/lib/wires/time.rb +46 -129
- data/lib/wires.rb +10 -14
- metadata +7 -35
- data/lib/wires/clean.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3c79a4bb96ed3a79e94be39171fda18a4846025
|
4
|
+
data.tar.gz: 7dd1087d9f74885e1bbbf4a25d70e07ae98919e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61b8a2c4881a8fdec36257a7ea4f5bfb2d4831a0157e1a9fbe53897c3d0d83d7746ba655025fbd23903a39af623d9bbf672f798ed5d15344972fa707bedb47bb
|
7
|
+
data.tar.gz: 962a6bca1dfa83c565920750b46bc9699915603a1e8a5c7bf00a7984477dad2a6cea79ba5323c06ae1686362f28a45b0b5a45db969b9f44bf12369b2991840a6
|
data/lib/wires/channel.rb
CHANGED
@@ -5,44 +5,29 @@ module Wires
|
|
5
5
|
|
6
6
|
attr_reader :name
|
7
7
|
attr_reader :target_list
|
8
|
-
|
8
|
+
attr_accessor :not_firable
|
9
9
|
|
10
|
-
def inspect; "
|
10
|
+
def inspect; "#{self.class}(#{name.inspect})"; end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
@hub = Hub
|
13
|
+
@router = Router
|
14
|
+
@new_lock = Mutex.new
|
15
|
+
@@aim_lock = Mutex.new
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# Ensure that there is only one instance of Channel per name
|
22
|
-
@@new_lock = Mutex.new
|
23
|
-
def self.new(*args, &block)
|
24
|
-
(args.include? :recursion_guard) ?
|
25
|
-
(args.delete :recursion_guard) :
|
26
|
-
(@@channel_star ||= self.new('*', :recursion_guard))
|
17
|
+
class << self
|
18
|
+
attr_accessor :hub
|
19
|
+
attr_accessor :router
|
27
20
|
|
28
|
-
|
29
|
-
|
30
|
-
|
21
|
+
def new(*args)
|
22
|
+
channel = @new_lock.synchronize do
|
23
|
+
router.get_channel(self, *args) { |name| super(name) } end
|
31
24
|
end
|
25
|
+
alias_method :[], :new
|
32
26
|
end
|
33
27
|
|
34
28
|
def initialize(name)
|
35
29
|
@name = name
|
36
|
-
@target_list =
|
37
|
-
unless @@channel_hash.empty?
|
38
|
-
_relevant_init
|
39
|
-
@@channel_hash.values.each do |c|
|
40
|
-
c.send(:_test_relevance, self)
|
41
|
-
_test_relevance c
|
42
|
-
end
|
43
|
-
else
|
44
|
-
@relevant_channels = []
|
45
|
-
end
|
30
|
+
@target_list = []
|
46
31
|
end
|
47
32
|
|
48
33
|
# Register a proc to be triggered by an event on this channel
|
@@ -50,18 +35,27 @@ module Wires
|
|
50
35
|
def register(*events, &proc)
|
51
36
|
if not proc.is_a?(Proc) then raise SyntaxError, \
|
52
37
|
"No Proc given to execute on event: #{events}" end
|
53
|
-
|
54
|
-
|
38
|
+
events = Event.new_from(*events)
|
39
|
+
|
40
|
+
@@aim_lock.synchronize do
|
41
|
+
@target_list << [events, proc] \
|
42
|
+
unless @target_list.include? [events, proc]
|
43
|
+
end
|
44
|
+
|
55
45
|
proc
|
56
46
|
end
|
57
47
|
|
58
48
|
# Unregister a proc from the target list of this channel
|
59
49
|
# Return true if at least one matching target was unregistered, else false
|
60
50
|
def unregister(*events, &proc)
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
51
|
+
events = events.empty? ? [] : Event.new_from(*events)
|
52
|
+
|
53
|
+
@@aim_lock.synchronize do
|
54
|
+
!!(@target_list.reject! do |es,pr|
|
55
|
+
(proc and proc==pr) and \
|
56
|
+
(events.map{|event| es.map{|e| event=~e}.any?}.all?)
|
57
|
+
end)
|
58
|
+
end
|
65
59
|
end
|
66
60
|
|
67
61
|
# Add hook methods
|
@@ -78,27 +72,47 @@ module Wires
|
|
78
72
|
end
|
79
73
|
|
80
74
|
# Fire an event on this channel
|
81
|
-
def fire(
|
75
|
+
def fire(input, blocking:false)
|
82
76
|
|
83
77
|
raise *@not_firable if @not_firable
|
84
78
|
|
85
79
|
backtrace = caller
|
86
80
|
|
87
|
-
|
88
|
-
|
81
|
+
event = Event.new_from(*input)
|
82
|
+
|
83
|
+
case event.count
|
84
|
+
when 0
|
85
|
+
raise ArgumentError,"Can't create an event from input: #{input.inspect}"
|
86
|
+
when 1
|
87
|
+
event = event.first
|
88
|
+
else
|
89
|
+
raise ArgumentError,"Can't fire on multiple events: #{event.inspect}"
|
90
|
+
end
|
89
91
|
|
90
92
|
self.class.run_hooks(:@before_fire, event, self)
|
91
93
|
|
92
|
-
#
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
94
|
+
# Select appropriate targets
|
95
|
+
procs = []
|
96
|
+
@@aim_lock.synchronize do
|
97
|
+
self.class.router
|
98
|
+
.get_receivers(self).each do |chan|
|
99
|
+
chan.target_list.each do |elist, pr|
|
100
|
+
elist.each do |e|
|
101
|
+
procs << pr if e =~ event
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Fire to selected targets
|
108
|
+
procs.uniq.each do |pr|
|
109
|
+
self.class.hub.spawn \
|
110
|
+
event, # fired event object event
|
111
|
+
self.name, # name of channel fired from
|
112
|
+
pr, # proc to execute
|
113
|
+
blocking, # boolean from blocking kwarg
|
114
|
+
backtrace # captured backtrace
|
115
|
+
end
|
102
116
|
|
103
117
|
self.class.run_hooks(:@after_fire, event, self)
|
104
118
|
|
@@ -109,61 +123,20 @@ module Wires
|
|
109
123
|
self.fire(event, blocking:true)
|
110
124
|
end
|
111
125
|
|
112
|
-
#
|
113
|
-
|
114
|
-
events = [events] unless events.is_a? Array
|
115
|
-
events.flatten!
|
116
|
-
events.map! { |e| (e.is_a?(Class) ? e.codestring : e.to_s) }
|
117
|
-
events.uniq!
|
118
|
-
end
|
119
|
-
|
120
|
-
def _relevant_init
|
121
|
-
@relevant_channels = [@@channel_star]
|
122
|
-
@my_names = (self.name.is_a? Array) ? self.name : [self.name]
|
123
|
-
@my_names.map {|o| (o.respond_to? :channel_name) ? o.channel_name : o.to_s}
|
124
|
-
.flatten(1)
|
125
|
-
_test_relevance self
|
126
|
-
end
|
127
|
-
|
128
|
-
def _test_relevance(other_chan)
|
129
|
-
if self==@@channel_star
|
130
|
-
@relevant_channels << other_chan
|
131
|
-
return
|
132
|
-
end
|
133
|
-
|
134
|
-
for my_name in @my_names
|
135
|
-
|
136
|
-
if my_name.is_a?(Regexp) then
|
137
|
-
@not_firable = [TypeError,
|
138
|
-
"Cannot fire on Regexp channel: #{self.name}."\
|
139
|
-
" Regexp channels can only used in event handlers."]
|
140
|
-
return
|
141
|
-
end
|
142
|
-
|
143
|
-
other_name = other_chan.name
|
144
|
-
other_name = (other_name.respond_to? :channel_name) ? \
|
145
|
-
other_name.channel_name : other_name
|
146
|
-
|
147
|
-
@relevant_channels << other_chan if \
|
148
|
-
!@relevant_channels.include?(other_chan) and \
|
149
|
-
if other_name.is_a?(Regexp)
|
150
|
-
my_name =~ other_name
|
151
|
-
else
|
152
|
-
my_name.to_s == other_name.to_s
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
# Compare matching with another Channel
|
126
|
+
# Returns true if listening on 'self' would hear a firing on 'other'
|
127
|
+
# (not commutative)
|
158
128
|
def =~(other)
|
159
|
-
(other.is_a? Channel) ?
|
129
|
+
(other.is_a? Channel) ?
|
130
|
+
(self.class.router.get_receivers(other).include? self) :
|
131
|
+
super
|
160
132
|
end
|
161
133
|
|
162
|
-
|
163
|
-
self.
|
164
|
-
self.clear_hooks(:@after_fire)
|
134
|
+
def receivers
|
135
|
+
self.class.router.get_receivers self
|
165
136
|
end
|
166
137
|
|
138
|
+
router.clear_channels
|
139
|
+
|
167
140
|
end
|
168
141
|
|
169
142
|
end
|
data/lib/wires/convenience.rb
CHANGED
@@ -3,25 +3,25 @@ module Wires
|
|
3
3
|
|
4
4
|
module Convenience
|
5
5
|
|
6
|
-
def
|
7
|
-
|
8
|
-
def on(events, channels='*', &codeblock)
|
9
|
-
channels = [channels] unless channels.is_a? Array
|
10
|
-
for channel in channels
|
6
|
+
def on(events, channels=self, &codeblock)
|
7
|
+
[*channels].each do |channel|
|
11
8
|
channel=Channel.new(channel) unless channel.is_a? Channel
|
9
|
+
|
12
10
|
channel.register(*events, &codeblock)
|
13
11
|
end
|
14
12
|
codeblock
|
15
13
|
end
|
16
14
|
|
17
|
-
def fire(event,
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
15
|
+
def fire(event, channels=self, **kwargs)
|
16
|
+
[*channels].each do |channel|
|
17
|
+
channel = Channel.new(channel) unless channel.is_a? Channel
|
18
|
+
|
19
|
+
if kwargs[:time] or (kwargs[:count] and kwargs[:count]!=1)
|
20
|
+
time = kwargs.delete(:time) or Time.now
|
21
|
+
TimeScheduler.add(time, event, channel, **kwargs)
|
22
|
+
else
|
23
|
+
channel.fire(event, **kwargs)
|
24
|
+
end
|
25
25
|
end
|
26
26
|
nil end
|
27
27
|
|
@@ -30,28 +30,6 @@ module Wires
|
|
30
30
|
fire(*args, **kwargs)
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
class << self
|
36
|
-
def prefix_methods(prefix)
|
37
|
-
|
38
|
-
return unless prefix
|
39
|
-
prefix = prefix.to_s
|
40
|
-
|
41
|
-
instance_methods.each do |thing|
|
42
|
-
thing = thing.to_s
|
43
|
-
f2 = (prefix+'_'+thing)
|
44
|
-
f2 = (thing[0]=~/[[:lower:]]/) ? f2.underscore : f2.camelcase
|
45
|
-
f2 = f2.to_sym; thing = thing.to_sym
|
46
|
-
alias_method f2, thing
|
47
|
-
remove_method thing
|
48
|
-
end
|
49
|
-
|
50
|
-
# remove_method :prefix_methods
|
51
|
-
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
33
|
end
|
56
34
|
|
57
35
|
end
|
data/lib/wires/core_ext.rb
CHANGED
@@ -1,49 +1,65 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
|
14
|
-
# Reopen ActiveSupport::Duration to enable nifty syntax like:
|
15
|
-
# 32.minutes.from_now do some_stuff end
|
16
|
-
class ::ActiveSupport::Duration
|
17
|
-
|
18
|
-
unless instance_methods.include? :__original_since
|
19
|
-
alias_method :__original_since, :since
|
20
|
-
def since(*args, &block)
|
2
|
+
module Wires
|
3
|
+
module Convenience
|
4
|
+
|
5
|
+
@core_ext = true
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
# Set this attribute to false to disable core_ext on include
|
10
|
+
attr_accessor :core_ext
|
21
11
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
nil
|
27
|
-
else
|
28
|
-
__original_since(*args)
|
12
|
+
# Call extend_core on include unless attribute is set to false
|
13
|
+
def included(*args)
|
14
|
+
super
|
15
|
+
self.extend_core if @core_ext
|
29
16
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
17
|
+
|
18
|
+
# Add methods to ::Time and ::Numeric
|
19
|
+
def extend_core
|
20
|
+
# Add Time#fire for timed firing of events
|
21
|
+
::Time.class_eval do
|
22
|
+
def fire(event, channel='*', **kwargs)
|
23
|
+
Wires::TimeScheduler.add(self, event, channel, **kwargs)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Add Numeric => Numeric time-factor converters
|
28
|
+
{
|
29
|
+
[:second, :seconds] => '1',
|
30
|
+
[:minute, :minutes] => '60',
|
31
|
+
[:hour, :hours] => '3600',
|
32
|
+
[:day, :days] => '24.hours',
|
33
|
+
[:week, :weeks] => '7.days',
|
34
|
+
[:fortnight, :fortnights] => '2.weeks',
|
35
|
+
}.each_pair do |k,v|
|
36
|
+
::Numeric.class_eval <<-CODE
|
37
|
+
def #{k.last}
|
38
|
+
self * #{v}
|
39
|
+
end
|
40
|
+
alias #{k.first.inspect} #{k.last.inspect}
|
41
|
+
CODE
|
42
|
+
end
|
43
|
+
|
44
|
+
# Add Numeric => Time converters with implicit anonymous fire
|
45
|
+
{
|
46
|
+
[:from_now, :since] => '+',
|
47
|
+
[:until, :ago] => '-',
|
48
|
+
}.each_pair do |k,v|
|
49
|
+
::Numeric.class_eval <<-CODE
|
50
|
+
def #{k.last}(time = ::Time.now, &block)
|
51
|
+
if block
|
52
|
+
Channel[block.object_id].register :time_scheduler_anon, &block
|
53
|
+
self.#{k.last}(time).fire(:time_scheduler_anon, block.object_id)
|
54
|
+
end
|
55
|
+
time #{v} self
|
56
|
+
end
|
57
|
+
alias #{k.first.inspect} #{k.last.inspect}
|
58
|
+
CODE
|
59
|
+
end
|
44
60
|
end
|
61
|
+
|
45
62
|
end
|
46
|
-
|
63
|
+
|
47
64
|
end
|
48
|
-
|
49
|
-
end
|
65
|
+
end
|
data/lib/wires/event.rb
CHANGED
@@ -2,166 +2,91 @@
|
|
2
2
|
module Wires
|
3
3
|
|
4
4
|
class Event
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
@@registry.uniq!
|
13
|
-
end
|
5
|
+
attr_accessor :event_type
|
6
|
+
|
7
|
+
# Return a friendly output upon inspection
|
8
|
+
def inspect
|
9
|
+
list = [*args, **kwargs].map(&:inspect).join ', '
|
10
|
+
type = event_type ? event_type.inspect : ''
|
11
|
+
"#{self.class}#{type}(#{list})"
|
14
12
|
end
|
15
|
-
event_registry_create
|
16
|
-
end
|
17
|
-
|
18
|
-
# All Event classes should inherit from this one
|
19
|
-
class Event
|
20
13
|
|
21
|
-
#
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
# Be sure codestring doesn't conflict
|
26
|
-
existing = _from_codestring(subcls.codestring)
|
27
|
-
if existing then raise NameError, \
|
28
|
-
"New Event subclass '#{subcls}' conflicts with"\
|
29
|
-
" existing Event subclass '#{existing}'."\
|
30
|
-
" The generated codestring '#{subcls.codestring}'"\
|
31
|
-
" must be unique for each Event subclass." end
|
32
|
-
|
33
|
-
super
|
34
|
-
event_registry_register(subcls)
|
35
|
-
end
|
36
|
-
|
37
|
-
# List of class inheritance lineage back to but excluding Object
|
38
|
-
def ancestry(cls=self)
|
39
|
-
_next = cls.superclass
|
40
|
-
[cls==Object ? [] : [cls, ancestry(_next)]].flatten
|
41
|
-
end
|
42
|
-
|
43
|
-
# Convert class <ClassNameEvent> to string "class_name"
|
44
|
-
def codestring(cls=self)
|
45
|
-
@codestring ||= \
|
46
|
-
File.basename cls.to_s
|
47
|
-
.underscore
|
48
|
-
.gsub(/_event$/, "")
|
49
|
-
end
|
50
|
-
|
51
|
-
# List of codestrings associated with this event and ancestors
|
52
|
-
def codestrings
|
53
|
-
x = ancestry
|
54
|
-
.map {|cls| cls.codestring}
|
55
|
-
end
|
56
|
-
|
57
|
-
# Pull class from registry by codestring
|
58
|
-
# (more reliable than crafting a reverse regexp)
|
59
|
-
def _from_codestring(str)
|
60
|
-
return @@registry.select{|e| e.codestring==str}[0]
|
61
|
-
end; private :_from_codestring
|
62
|
-
|
63
|
-
def from_codestring(str)
|
64
|
-
cls = _from_codestring(str.to_s)
|
65
|
-
if not cls then raise NameError,
|
66
|
-
"No known Event subclass with codestring: '#{str}'" end
|
67
|
-
cls
|
68
|
-
end
|
69
|
-
|
70
|
-
# Convert an event from 'array notation' to an Event subclass instance
|
71
|
-
# TODO: List acceptable input forms here for documentation
|
72
|
-
def new_from(input)
|
73
|
-
|
74
|
-
# Standardize to array and pull out arguments if they exist
|
75
|
-
input = [input] unless input.is_a? Array
|
76
|
-
input, *args = input
|
77
|
-
|
78
|
-
# Create event object from event as an object, class, or symbol/string
|
79
|
-
event = case input
|
80
|
-
when Event
|
81
|
-
input
|
82
|
-
when Class
|
83
|
-
input.new(*args) if input <= Event
|
84
|
-
else
|
85
|
-
Event.from_codestring(input.to_s).new(*args)
|
86
|
-
end
|
87
|
-
end
|
14
|
+
# Internalize all *args and **kwargs and &block to be accessed later
|
15
|
+
def initialize(*args, **kwargs, &block)
|
16
|
+
@ignore = []
|
17
|
+
@kwargs = kwargs.dup
|
88
18
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
kwargs = args[-1].is_a?(Hash) ? args.pop.dup : Hash.new
|
96
|
-
kwargs[:kwargs] = kwargs.dup.freeze
|
97
|
-
kwargs[:args] = args.dup.freeze
|
98
|
-
kwargs[:codeblock] = block if block
|
99
|
-
for key in kwargs.keys
|
100
|
-
att = key.to_s
|
101
|
-
obj.instance_variable_set("@#{att}", kwargs[key])
|
102
|
-
class_eval { attr_reader att }
|
103
|
-
# class_eval { attr_writer att }
|
104
|
-
end
|
105
|
-
|
106
|
-
obj
|
107
|
-
end
|
108
|
-
|
19
|
+
(@kwargs[:args] = args.freeze; @ignore<<:args) \
|
20
|
+
unless @kwargs.has_key? :args
|
21
|
+
(@kwargs[:codeblock] = block; @ignore<<:codeblock) \
|
22
|
+
unless @kwargs.has_key? :codeblock
|
23
|
+
@kwargs.freeze
|
109
24
|
end
|
110
25
|
|
111
|
-
#
|
112
|
-
def
|
26
|
+
# Directly access contents of @kwargs by key
|
27
|
+
def [](key); @kwargs[key]; end
|
113
28
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
# Reopen Event and add comparison functions
|
121
|
-
class Event
|
29
|
+
# Used to fake a sort of read-only openstruct from contents of @kwargs
|
30
|
+
def method_missing(sym, *args, &block)
|
31
|
+
args.empty? and @kwargs.has_key?(sym) ?
|
32
|
+
@kwargs[sym] :
|
33
|
+
(sym==:kwargs ? @kwargs.reject{|k| @ignore.include? k} : super)
|
34
|
+
end
|
122
35
|
|
36
|
+
# Returns true if listening for 'self' would hear a firing of 'other'
|
37
|
+
# (not commutative)
|
123
38
|
def =~(other)
|
124
39
|
(other.is_a? Event) ?
|
125
40
|
((self.class >= other.class) \
|
41
|
+
and (self.event_type.nil? or self.event_type==other.event_type \
|
42
|
+
or (self.event_type.is_a? Class and other.event_type.is_a? Class \
|
43
|
+
and self.event_type >= other.event_type)) \
|
126
44
|
and (not self.kwargs.each_pair.detect{|k,v| other.kwargs[k]!=v}) \
|
127
45
|
and (not self.args.each_with_index.detect{|a,i| other.args[i]!=a})) :
|
128
46
|
super
|
129
47
|
end
|
130
48
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
other.is_a?(Class) ?
|
142
|
-
super : (self<=other and not self==other)
|
143
|
-
end
|
144
|
-
def >=(other)
|
145
|
-
other.is_a?(Class) ?
|
146
|
-
super : Event.from_codestring(other.to_s)<=self
|
49
|
+
# Return an array of Event instance objects generated from
|
50
|
+
# specially formatted input (see spec/event_spec.rb).
|
51
|
+
def self.new_from(*args)
|
52
|
+
args.flatten!
|
53
|
+
list = []
|
54
|
+
|
55
|
+
args.each do |x|
|
56
|
+
(x.is_a? Hash) ?
|
57
|
+
(x.each_pair { |x,y| list << [x,y] }) :
|
58
|
+
(list << [x,[]])
|
147
59
|
end
|
148
|
-
|
149
|
-
|
150
|
-
|
60
|
+
|
61
|
+
list.map! do |type, args|
|
62
|
+
case type
|
63
|
+
when Event; obj = type
|
64
|
+
when Class;
|
65
|
+
if type<=Event
|
66
|
+
obj = type.new(*args)
|
67
|
+
obj.event_type = type unless type==Wires::Event
|
68
|
+
end
|
69
|
+
when Symbol
|
70
|
+
obj = self.new(*args)
|
71
|
+
obj.event_type = type
|
72
|
+
obj if self==Wires::Event
|
73
|
+
end
|
74
|
+
obj
|
75
|
+
end.tap do |x|
|
76
|
+
raise ArgumentError,
|
77
|
+
"Invalid event creation input: #{args} \noutput: #{x}" \
|
78
|
+
if x.empty? or !x.all?
|
151
79
|
end
|
152
80
|
end
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
cls.class_eval(
|
161
|
-
"def #{op}(other)\n"\
|
162
|
-
" (other.is_a?(Class) and other<=Event) ? \n"\
|
163
|
-
" (other#{opinv}self) : super\n"\
|
164
|
-
"end\n")
|
81
|
+
|
82
|
+
# Ensure that self.new_from is not inherited
|
83
|
+
def self.inherited(subcls)
|
84
|
+
super
|
85
|
+
class << subcls
|
86
|
+
undef_method :new_from
|
87
|
+
end if self == Wires::Event
|
165
88
|
end
|
89
|
+
|
166
90
|
end
|
167
|
-
|
91
|
+
|
92
|
+
end
|
data/lib/wires/hub.rb
CHANGED
@@ -3,11 +3,10 @@ module Wires
|
|
3
3
|
# An Event Hub. Event/proc associations come in, and the procs
|
4
4
|
# get called in new threads in the order received
|
5
5
|
class self::Hub
|
6
|
-
# Operate on the metaclass as a type of singleton pattern
|
7
6
|
class << self
|
8
7
|
|
9
8
|
# Allow user to get/set limit to number of child threads
|
10
|
-
attr_accessor :
|
9
|
+
attr_accessor :max_children
|
11
10
|
|
12
11
|
private
|
13
12
|
|
@@ -17,85 +16,21 @@ module Wires
|
|
17
16
|
# Moved to a dedicated method for subclass' sake
|
18
17
|
def class_init
|
19
18
|
# @queue = Queue.new
|
20
|
-
@
|
21
|
-
@
|
22
|
-
@
|
23
|
-
@neglected
|
24
|
-
@
|
25
|
-
@spawning_count = 0
|
26
|
-
@spawning_count_lock = Monitor.new
|
19
|
+
@max_children = nil
|
20
|
+
@children = Array.new
|
21
|
+
@children .extend MonitorMixin
|
22
|
+
@neglected = Array.new
|
23
|
+
@neglected.extend MonitorMixin
|
27
24
|
|
28
|
-
@
|
29
|
-
@after_runs = Queue.new
|
30
|
-
@before_kills = Queue.new
|
31
|
-
@after_kills = Queue.new
|
32
|
-
@before_fires = []
|
33
|
-
@after_fires = []
|
34
|
-
|
35
|
-
@please_finish_all = false
|
25
|
+
@hold_lock = Monitor.new
|
36
26
|
|
37
27
|
reset_neglect_procs
|
38
28
|
reset_handler_exception_proc
|
39
29
|
|
40
|
-
# at_exit { (sleep 0.05 until dead?) unless $! }
|
41
|
-
|
42
|
-
state_machine_init
|
43
|
-
|
44
30
|
nil end
|
45
31
|
|
46
32
|
public
|
47
33
|
|
48
|
-
def dead?; state==:dead end
|
49
|
-
def alive?; state==:alive end
|
50
|
-
|
51
|
-
##
|
52
|
-
# Start the Hub to allow task spawning.
|
53
|
-
#
|
54
|
-
def run(*flags)
|
55
|
-
sleep 0 until @spawning_count <= 0
|
56
|
-
@spawning_count_lock.synchronize do
|
57
|
-
sleep 0 until request_state :alive
|
58
|
-
end
|
59
|
-
spawn_neglected_task_threads
|
60
|
-
join_children
|
61
|
-
nil end
|
62
|
-
|
63
|
-
##
|
64
|
-
# Kill the Hub event loop (optional flags change thread behavior)
|
65
|
-
#
|
66
|
-
# valid flags:
|
67
|
-
# [+:nonblocking+]
|
68
|
-
# Without this flag, calling thread will be blocked
|
69
|
-
# until Hub thread is done
|
70
|
-
# [+:purge_tasks+]
|
71
|
-
# Without this flag, Hub thread won't be done
|
72
|
-
# until all child threads are done
|
73
|
-
def kill(*flags)
|
74
|
-
sleep 0 until @spawning_count <= 0
|
75
|
-
@please_finish_all = (not flags.include? :purge_tasks)
|
76
|
-
sleep 0 until request_state :dead unless (flags.include? :nonblocking)
|
77
|
-
nil end
|
78
|
-
|
79
|
-
# Add hook methods
|
80
|
-
include Hooks
|
81
|
-
|
82
|
-
def before_run(*args, &proc)
|
83
|
-
add_hook(:@before_run, *args, &proc)
|
84
|
-
end
|
85
|
-
|
86
|
-
def after_run(*args, &proc)
|
87
|
-
add_hook(:@after_run, *args, &proc)
|
88
|
-
end
|
89
|
-
|
90
|
-
def before_kill(*args, &proc)
|
91
|
-
add_hook(:@before_kill, *args, &proc)
|
92
|
-
end
|
93
|
-
|
94
|
-
def after_kill(*args, &proc)
|
95
|
-
add_hook(:@after_kill, *args, &proc)
|
96
|
-
end
|
97
|
-
|
98
|
-
|
99
34
|
def on_neglect(&block)
|
100
35
|
@on_neglect=block
|
101
36
|
nil end
|
@@ -104,7 +39,7 @@ module Wires
|
|
104
39
|
nil end
|
105
40
|
def on_handler_exception(&block)
|
106
41
|
@on_handler_exception=block
|
107
|
-
end
|
42
|
+
nil end
|
108
43
|
|
109
44
|
def reset_neglect_procs
|
110
45
|
@on_neglect = Proc.new do |args|
|
@@ -117,18 +52,23 @@ module Wires
|
|
117
52
|
|
118
53
|
def reset_handler_exception_proc
|
119
54
|
@on_handler_exception = Proc.new { raise }
|
55
|
+
nil end
|
56
|
+
|
57
|
+
# Execute a block while neglecting all child threads
|
58
|
+
def hold
|
59
|
+
@hold_lock.synchronize { yield }
|
60
|
+
spawn_neglected_task_threads
|
120
61
|
end
|
121
62
|
|
122
63
|
# Spawn a task
|
123
|
-
def spawn(*args) # :args: event,
|
124
|
-
|
125
|
-
@spawning_count_lock.synchronize { @spawning_count += 1 }
|
64
|
+
def spawn(*args) # :args: event, chan, proc, blocking, fire_bt
|
126
65
|
|
127
|
-
return neglect(*args)
|
66
|
+
return neglect(*args) \
|
67
|
+
if @hold_lock.instance_variable_get(:@mon_mutex).locked?
|
128
68
|
|
129
|
-
event,
|
130
|
-
*proc_args = event,
|
131
|
-
*exc_args = event,
|
69
|
+
event, chan, proc, blocking, fire_bt = *args
|
70
|
+
*proc_args = event, chan
|
71
|
+
*exc_args = event, chan, fire_bt
|
132
72
|
|
133
73
|
# If blocking, run the proc in this thread
|
134
74
|
if blocking
|
@@ -142,78 +82,59 @@ module Wires
|
|
142
82
|
end
|
143
83
|
|
144
84
|
# If not blocking, clear old threads and spawn a new thread
|
145
|
-
|
146
|
-
|
147
|
-
@child_threads_lock.synchronize do
|
148
|
-
|
149
|
-
# Clear out dead threads
|
150
|
-
@child_threads.select!{|t| t.status}
|
151
|
-
|
85
|
+
Thread.exclusive do
|
152
86
|
begin
|
153
87
|
# Raise ThreadError for user-set thread limit to mimic OS limit
|
154
|
-
raise ThreadError if (@
|
155
|
-
(@
|
88
|
+
raise ThreadError if (@max_children) and \
|
89
|
+
(@max_children <= @children.count)
|
156
90
|
# Start the new child thread; follow with chain of neglected tasks
|
157
|
-
|
91
|
+
@children << Thread.new do
|
158
92
|
begin
|
159
93
|
proc.call(*proc_args)
|
160
94
|
rescue Exception => exc
|
161
95
|
unhandled_exception(exc, *exc_args)
|
162
96
|
ensure
|
163
97
|
spawn_neglected_task_chain
|
98
|
+
@children.synchronize { @children.delete Thread.current }
|
164
99
|
end
|
165
100
|
end
|
166
101
|
|
167
102
|
# Capture ThreadError from either OS or user-set limitation
|
168
103
|
rescue ThreadError; return neglect(*args) end
|
169
104
|
|
170
|
-
@
|
171
|
-
return new_thread
|
105
|
+
return @children.last
|
172
106
|
end
|
173
107
|
|
174
|
-
ensure
|
175
|
-
@spawning_count_lock.synchronize { @spawning_count -= 1 }
|
176
108
|
end
|
177
109
|
|
178
|
-
def
|
179
|
-
|
180
|
-
@neglected.clear
|
181
|
-
end
|
182
|
-
nil end
|
183
|
-
|
184
|
-
def number_neglected
|
185
|
-
@neglected_lock.synchronize do
|
186
|
-
@neglected.size
|
187
|
-
end
|
188
|
-
end
|
110
|
+
def clear_neglected; @neglected.synchronize { @neglected.clear; nil } end
|
111
|
+
def count_neglected; @neglected.synchronize { @neglected.count } end
|
189
112
|
|
190
113
|
# Join child threads, one by one, allowing more children to appear
|
191
114
|
def join_children
|
192
115
|
a_thread = Thread.new{nil}
|
193
116
|
while a_thread
|
194
|
-
@
|
195
|
-
a_thread = @
|
117
|
+
@children.synchronize do
|
118
|
+
a_thread = @children.shift
|
196
119
|
end
|
197
120
|
a_thread.join if ((a_thread) and (a_thread!=Thread.current))
|
198
|
-
|
121
|
+
Thread.pass
|
199
122
|
end
|
200
123
|
nil end
|
201
124
|
|
202
125
|
private
|
203
126
|
|
204
127
|
# Send relevant data to a custom exception handler
|
205
|
-
def unhandled_exception(exception, event,
|
206
|
-
|
128
|
+
def unhandled_exception(exception, event, chan, fire_bt)
|
207
129
|
class << exception; attr_reader :fire_backtrace; end
|
208
130
|
exception.instance_variable_set(:@fire_backtrace, fire_bt.dup)
|
209
131
|
|
210
|
-
@on_handler_exception.call(exception, event,
|
211
|
-
|
132
|
+
@on_handler_exception.call(exception, event, chan)
|
212
133
|
end
|
213
134
|
|
214
135
|
# Temporarily neglect a task until resources are available to run it
|
215
136
|
def neglect(*args)
|
216
|
-
@
|
137
|
+
@neglected.synchronize do
|
217
138
|
@on_neglect.call(*args)
|
218
139
|
@neglected << args
|
219
140
|
end
|
@@ -221,7 +142,7 @@ module Wires
|
|
221
142
|
|
222
143
|
# Run a chain of @neglected tasks in place until no more are waiting
|
223
144
|
def spawn_neglected_task_chain
|
224
|
-
args = @
|
145
|
+
args = @neglected.synchronize do
|
225
146
|
return nil if @neglected.empty?
|
226
147
|
((@neglected.shift)[0...-1]<<true) # Call with blocking
|
227
148
|
end
|
@@ -233,7 +154,7 @@ module Wires
|
|
233
154
|
# Flush @neglected task queue, each in a new thread
|
234
155
|
def spawn_neglected_task_threads
|
235
156
|
until (cease||=false)
|
236
|
-
args = @
|
157
|
+
args = @neglected.synchronize do
|
237
158
|
break if (cease = @neglected.empty?)
|
238
159
|
((@neglected.shift)[0...-1]<<false) # Call without blocking
|
239
160
|
end
|
@@ -245,49 +166,6 @@ module Wires
|
|
245
166
|
|
246
167
|
end
|
247
168
|
|
248
|
-
|
249
|
-
#***
|
250
|
-
# Initialize state machine properties
|
251
|
-
#***
|
252
|
-
class << self
|
253
|
-
include Hegemon
|
254
|
-
|
255
|
-
# Protect Hub users methods that could cause deadlock
|
256
|
-
# if called from inside an event
|
257
|
-
private :state_obj,
|
258
|
-
:state_objs,
|
259
|
-
:request_state,
|
260
|
-
:update_state,
|
261
|
-
:do_state_tasks,
|
262
|
-
:iter_hegemon_auto_loop,
|
263
|
-
:start_hegemon_auto_thread,
|
264
|
-
:join_hegemon_auto_thread,
|
265
|
-
:end_hegemon_auto_thread
|
266
|
-
|
267
|
-
def state_machine_init
|
268
|
-
|
269
|
-
impose_state :dead
|
270
|
-
|
271
|
-
declare_state :dead do
|
272
|
-
transition_to :alive do
|
273
|
-
before { flush_hooks :@before_run }
|
274
|
-
after { flush_hooks :@after_run }
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
declare_state :alive do
|
279
|
-
transition_to :dead do
|
280
|
-
before { flush_hooks :@before_kill }
|
281
|
-
before { purge_neglected }
|
282
|
-
before { join_children if @please_finish_all }
|
283
|
-
after { @please_finish_all = false }
|
284
|
-
after { flush_hooks :@after_kill }
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
169
|
class_init
|
292
170
|
end
|
293
171
|
end
|
data/lib/wires/router.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
module Wires
|
3
|
+
|
4
|
+
class Router
|
5
|
+
|
6
|
+
@table = Hash.new
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
attr_accessor :table
|
11
|
+
|
12
|
+
def clear_channels()
|
13
|
+
@initialized = true
|
14
|
+
@table = {}
|
15
|
+
@fuzzy_table = {}
|
16
|
+
Channel['*']
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_channel(chan_cls, name)
|
20
|
+
channel = @table[name] ||= (new_one=true; yield name)
|
21
|
+
|
22
|
+
if new_one and name.is_a? Regexp then
|
23
|
+
@fuzzy_table[name] = channel
|
24
|
+
channel.not_firable = [TypeError,
|
25
|
+
"Cannot fire on Regexp channel: #{name.inspect}."\
|
26
|
+
" Regexp channels can only used in event handlers."]
|
27
|
+
end
|
28
|
+
|
29
|
+
channel
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_receivers(chan)
|
33
|
+
name = chan.name
|
34
|
+
@fuzzy_table.keys.select do |k|
|
35
|
+
(begin; name =~ k; rescue TypeError; end)
|
36
|
+
end.map { |k| @fuzzy_table[k] } + [chan, @table['*']]
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/lib/wires/time.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
|
2
2
|
module Wires
|
3
3
|
|
4
|
-
class TimeSchedulerAnonEvent
|
4
|
+
class TimeSchedulerAnonEvent < Event; end
|
5
5
|
|
6
6
|
class TimeSchedulerItem
|
7
7
|
|
8
8
|
attr_reader :time, :event, :channel, :interval
|
9
|
+
attr_accessor :schedulers
|
9
10
|
|
10
11
|
def initialize(time, event, channel='*',
|
11
12
|
interval:0.seconds, count:1,
|
@@ -33,18 +34,20 @@ module Wires
|
|
33
34
|
@interval = interval
|
34
35
|
|
35
36
|
@event = Event.new_from(event)
|
36
|
-
@channel =
|
37
|
+
@channel = channel.is_a?(Channel) ? channel : Channel.new(channel)
|
37
38
|
@kwargs = kwargs
|
39
|
+
|
40
|
+
@schedulers = []
|
38
41
|
end
|
39
42
|
|
40
43
|
def active?; @active end
|
41
|
-
def inactive?;
|
44
|
+
def inactive?; !@active end
|
42
45
|
|
43
|
-
def ready?(at_time=Time.now);
|
46
|
+
def ready?(at_time=Time.now); @active and (at_time>=@time) end
|
44
47
|
|
45
|
-
def time_until; (@active ? [(Time.now
|
48
|
+
def time_until; (@active ? [(@time - Time.now), 0].max : nil) end
|
46
49
|
|
47
|
-
def cancel;
|
50
|
+
def cancel; self.count=0 ;nil end
|
48
51
|
|
49
52
|
# Get/set @count (and apply constraints on set)
|
50
53
|
def count; @count end
|
@@ -60,24 +63,22 @@ module Wires
|
|
60
63
|
@channel.fire(@event, **(@kwargs.merge(kwargs)))
|
61
64
|
count_dec
|
62
65
|
@time += @interval if @active
|
66
|
+
notify_schedulers
|
63
67
|
nil end
|
64
68
|
|
65
69
|
# Fire the event only if it is ready
|
66
70
|
def fire_if_ready(**args); self.fire(**kwargs) if ready? end
|
67
71
|
|
68
|
-
|
69
|
-
def wait_until_ready; sleep 0 until ready? end
|
72
|
+
private
|
70
73
|
|
71
|
-
|
72
|
-
def fire_when_ready(**kwargs);
|
73
|
-
wait_until_ready
|
74
|
-
self.fire(**kwargs)
|
75
|
-
end
|
74
|
+
def notify_schedulers; @schedulers.each &:refresh end
|
76
75
|
|
77
|
-
# Lock
|
78
|
-
|
79
|
-
|
80
|
-
|
76
|
+
# Lock some of the methods to try to make them atomic
|
77
|
+
# Must exclude methods that get called from within the TimeScheduler lock
|
78
|
+
threadlock :fire,
|
79
|
+
:count=,
|
80
|
+
:count_inc,
|
81
|
+
:count_dec
|
81
82
|
end
|
82
83
|
|
83
84
|
# A singleton class to schedule future firing of events
|
@@ -85,142 +86,58 @@ module Wires
|
|
85
86
|
@schedule = Array.new
|
86
87
|
@thread = Thread.new {nil}
|
87
88
|
@schedule_lock = Monitor.new
|
88
|
-
@
|
89
|
-
|
90
|
-
@grain = 1.seconds
|
89
|
+
@cond = @schedule_lock.new_cond
|
91
90
|
|
92
|
-
# Operate on the metaclass as a type of singleton pattern
|
93
91
|
class << self
|
94
92
|
|
95
|
-
attr_accessor :grain
|
96
|
-
|
97
93
|
# Add an event to the schedule
|
98
94
|
def add(*args)
|
99
|
-
new_item =
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
95
|
+
new_item = args.first
|
96
|
+
new_item = (TimeSchedulerItem.new *args) \
|
97
|
+
unless new_item.is_a? TimeSchedulerItem
|
98
|
+
|
99
|
+
new_item.schedulers << self
|
100
|
+
schedule_update new_item
|
101
|
+
new_item
|
102
|
+
end
|
104
103
|
|
105
104
|
# Add an event to the schedule using << operator
|
106
105
|
def <<(arg); add(*arg); end
|
107
106
|
|
108
107
|
# Get a copy of the event schedule from outside the class
|
109
|
-
def list;
|
108
|
+
def list; @schedule_lock.synchronize { @schedule.dup } end
|
110
109
|
# Clear the event schedule from outside the class
|
111
|
-
def clear;
|
110
|
+
def clear; @schedule_lock.synchronize { @schedule.clear } end
|
111
|
+
# Make the scheduler wake up and re-evaluate
|
112
|
+
def refresh; schedule_update end
|
112
113
|
|
113
114
|
private
|
114
|
-
|
115
|
-
def schedule_clear
|
116
|
-
@schedule.clear
|
117
|
-
end
|
118
|
-
|
119
|
-
def schedule_reshuffle
|
120
|
-
@schedule.select! {|x| x.active?}
|
121
|
-
@schedule.sort! {|a,b| a.time <=> b.time}
|
122
|
-
nil end
|
123
115
|
|
124
|
-
def
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
break unless new_item.ready?
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
if new_item.ready?(@next_pass)
|
135
|
-
Thread.new do
|
136
|
-
loop do
|
137
|
-
new_item.fire_when_ready(blocking:true)
|
138
|
-
break unless new_item.ready?(@next_pass)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
116
|
+
def schedule_update(item_to_add=nil)
|
117
|
+
@schedule_lock.synchronize do
|
118
|
+
@schedule << item_to_add if item_to_add
|
119
|
+
@schedule.select! {|x| x.active?}
|
120
|
+
@schedule.sort! {|a,b| a.time <=> b.time}
|
121
|
+
@cond.broadcast
|
142
122
|
end
|
143
|
-
|
144
|
-
@schedule << new_item
|
145
|
-
schedule_reshuffle
|
146
|
-
|
147
|
-
nil end
|
148
|
-
|
149
|
-
def schedule_concat(other_list)
|
150
|
-
@schedule.concat other_list
|
151
|
-
schedule_reshuffle
|
152
123
|
nil end
|
153
124
|
|
154
|
-
def schedule_pull
|
155
|
-
pending_now = Array.new
|
156
|
-
pending_soon = Array.new
|
157
|
-
while ((not @schedule.empty?) and @schedule[0].ready?)
|
158
|
-
pending_now << @schedule.shift
|
159
|
-
end
|
160
|
-
while ((not @schedule.empty?) and @schedule[0].ready?(@next_pass))
|
161
|
-
pending_soon << @schedule.shift
|
162
|
-
end
|
163
|
-
return [pending_now, pending_soon]
|
164
|
-
end
|
165
|
-
|
166
|
-
def schedule_next_pass
|
167
|
-
@next_pass = Time.now+@grain
|
168
|
-
end
|
169
|
-
|
170
|
-
# Put all functions dealing with @schedule under @schedule_lock
|
171
|
-
threadlock :list,
|
172
|
-
:schedule_clear,
|
173
|
-
:schedule_reshuffle,
|
174
|
-
:schedule_add,
|
175
|
-
:schedule_concat,
|
176
|
-
:schedule_pull,
|
177
|
-
:schedule_next_pass,
|
178
|
-
lock: :@schedule_lock
|
179
|
-
|
180
125
|
def main_loop
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
# Pull, fire, and requeue relevant events
|
191
|
-
pending_now, pending_soon = schedule_pull
|
192
|
-
pending_now.each { |x| x.fire }
|
193
|
-
pending_soon.each{ |x| Thread.new{ x.fire_when_ready(blocking:true) }}
|
194
|
-
# schedule_concat pending_now
|
195
|
-
|
196
|
-
sleep [@next_pass-Time.now, 0].max
|
126
|
+
pending = []
|
127
|
+
loop do
|
128
|
+
@schedule_lock.synchronize do
|
129
|
+
timeout = (@schedule.first.time_until unless @schedule.empty?)
|
130
|
+
@cond.wait timeout
|
131
|
+
pending = @schedule.take_while &:ready?
|
132
|
+
end
|
133
|
+
pending.each &:fire
|
197
134
|
end
|
198
|
-
|
199
135
|
nil end
|
200
136
|
|
201
137
|
end
|
202
138
|
|
203
|
-
|
204
|
-
Hub.after_run(true) do
|
205
|
-
@keepgoing = true
|
206
|
-
@thread = Thread.new { main_loop }
|
207
|
-
end
|
208
|
-
|
209
|
-
# Stop the main loop upon death of Hub
|
210
|
-
Hub.before_kill(true) do
|
211
|
-
Thread.exclusive do
|
212
|
-
@keepgoing=false
|
213
|
-
@next_pass=Time.now
|
214
|
-
@thread.wakeup
|
215
|
-
end
|
216
|
-
@thread.join
|
217
|
-
schedule_clear
|
218
|
-
end
|
139
|
+
@thread = Thread.new { main_loop }
|
219
140
|
|
220
141
|
end
|
221
142
|
|
222
143
|
end
|
223
|
-
|
224
|
-
|
225
|
-
# TODO: Repeatable event sugar?
|
226
|
-
# TODO: Tests for all new functionality
|
data/lib/wires.rb
CHANGED
@@ -1,18 +1,14 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
require 'thread'
|
3
|
-
require 'active_support/core_ext' # Convenience functions from Rails
|
4
3
|
require 'threadlock' # Easily add re-entrant lock to instance methods
|
5
|
-
require 'hegemon' # State machine management
|
6
|
-
|
7
|
-
require 'wires/util/expect_type'
|
8
|
-
require 'wires/util/hooks'
|
9
|
-
|
10
|
-
require 'wires/event'
|
11
|
-
require 'wires/hub'
|
12
|
-
require 'wires/channel'
|
13
|
-
require 'wires/time'
|
14
4
|
|
15
|
-
|
16
|
-
|
17
|
-
include Wires::Convenience # require 'wires/clean' to uninclude Convenience
|
5
|
+
require_relative 'wires/util/expect_type'
|
6
|
+
require_relative 'wires/util/hooks'
|
18
7
|
|
8
|
+
require_relative 'wires/event'
|
9
|
+
require_relative 'wires/hub'
|
10
|
+
require_relative 'wires/router'
|
11
|
+
require_relative 'wires/channel'
|
12
|
+
require_relative 'wires/time'
|
13
|
+
require_relative 'wires/core_ext'
|
14
|
+
require_relative 'wires/convenience'
|
metadata
CHANGED
@@ -1,43 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wires
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe McIlvain
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-09-
|
11
|
+
date: 2013-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: threadlock
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '1.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: hegemon
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ~>
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 0.0.8
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ~>
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 0.0.8
|
26
|
+
version: '1.2'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: rake
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,20 +66,6 @@ dependencies:
|
|
80
66
|
- - '>='
|
81
67
|
- !ruby/object:Gem::Version
|
82
68
|
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: starkfish
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - '>='
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - '>='
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
69
|
description: An asynchronous (threaded) event routing framework in Ruby. Patch your
|
98
70
|
objects together with wires. Inspired by the python 'circuits' framework.
|
99
71
|
email: joe.eli.mac@gmail.com
|
@@ -104,8 +76,8 @@ files:
|
|
104
76
|
- lib/wires.rb
|
105
77
|
- lib/wires/time.rb
|
106
78
|
- lib/wires/hub.rb
|
79
|
+
- lib/wires/router.rb
|
107
80
|
- lib/wires/core_ext.rb
|
108
|
-
- lib/wires/clean.rb
|
109
81
|
- lib/wires/util/expect_type.rb
|
110
82
|
- lib/wires/util/hooks.rb
|
111
83
|
- lib/wires/convenience.rb
|
@@ -115,7 +87,7 @@ files:
|
|
115
87
|
- README.md
|
116
88
|
homepage: https://github.com/jemc/wires/
|
117
89
|
licenses:
|
118
|
-
-
|
90
|
+
- Copyright 2013 Joe McIlvain. All rights reserved.
|
119
91
|
metadata: {}
|
120
92
|
post_install_message:
|
121
93
|
rdoc_options: []
|
data/lib/wires/clean.rb
DELETED