wires 0.3.8 → 0.4.0
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.
- 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