wires 0.1.11 → 0.1.12
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/channels.rb +82 -80
- data/lib/wires/events.rb +19 -0
- data/lib/wires/expect_type.rb +6 -0
- data/lib/wires/hub.rb +5 -8
- data/lib/wires/time.rb +147 -55
- data/lib/wires.rb +2 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00826eb63c92ee41f33d4b9afca45cf79f6074ef
|
4
|
+
data.tar.gz: 7547996adc41b8f58905b22b50f317f403ef56b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64f55f73930e572d8fdb589a8c84f302396a87ff56ec851e0a6fe24b7666dd8f572ee6638169a0571728e6364ea9b85d38d6d843b133656690cd22072cd00e8c
|
7
|
+
data.tar.gz: efac20e8fc999a1e219ff778ee92cbeb0c5ebfd2e520708b382bd86411a6133c6ceac8ab7084620439e6c8ae4a4ff98bdece2136aa3f4bcc9a73dc3bb968bf71
|
data/lib/wires/channels.rb
CHANGED
@@ -1,105 +1,107 @@
|
|
1
1
|
|
2
2
|
def on(events, channels='*', &codeblock)
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
channels = [channels] unless channels.is_a? Array
|
4
|
+
for channel in channels
|
5
|
+
Channel.new(channel).register(events, codeblock)
|
6
|
+
end
|
7
7
|
nil end
|
8
8
|
|
9
9
|
|
10
10
|
def fire(event, channel='*')
|
11
|
-
|
11
|
+
Channel.new(channel).fire(event, blocking:false)
|
12
12
|
nil end
|
13
13
|
|
14
14
|
def fire_and_wait(event, channel='*')
|
15
|
-
|
15
|
+
Channel.new(channel).fire(event, blocking:true)
|
16
16
|
nil end
|
17
17
|
|
18
18
|
|
19
19
|
def Channel(*args) Channel.new(*args) end
|
20
20
|
|
21
21
|
class Channel
|
22
|
+
|
23
|
+
attr_reader :name
|
24
|
+
attr_reader :target_list
|
25
|
+
|
26
|
+
def initialize(name)
|
27
|
+
@name = name
|
28
|
+
@target_list = Set.new
|
29
|
+
nil end
|
30
|
+
|
31
|
+
|
32
|
+
# Redefine this class method to use an alternate Hub
|
33
|
+
def self.hub; Hub; end
|
34
|
+
# Don't redefine this instance method!
|
35
|
+
def hub; self.class.hub; end
|
36
|
+
|
37
|
+
# Channel registry hash and star channel reference are values
|
38
|
+
# In this Hash with the key being the reference to the Hub
|
39
|
+
@@channel_hash = Hash.new
|
40
|
+
@@channel_star = Hash.new
|
41
|
+
|
42
|
+
# Give out references to the star channel
|
43
|
+
def self.channel_star; @@channel_star[self.hub]; end
|
44
|
+
def channel_star; @@channel_star[self.hub]; end
|
45
|
+
|
46
|
+
# Ensure that there is only one instance of Channel per name
|
47
|
+
@@new_lock = Mutex.new
|
48
|
+
def self.new(*args, &block)
|
49
|
+
@@channel_star[self.hub] ||= Channel.new('*') unless (args[0]=='*')
|
50
|
+
@@new_lock.synchronize do
|
51
|
+
@@channel_hash[self.hub] ||= Hash.new
|
52
|
+
@@channel_hash[self.hub][args[0]] ||= super(*args, &block)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Register a proc to be triggered by an event on this channel
|
57
|
+
def register(events, proc)
|
22
58
|
|
23
|
-
|
24
|
-
|
59
|
+
if not proc.is_a?(Proc) then raise SyntaxError, \
|
60
|
+
"No Proc given to execute on event: #{events}" end
|
25
61
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
62
|
+
# Convert all events to strings
|
63
|
+
events = [events] unless events.is_a? Array
|
64
|
+
events.flatten!
|
65
|
+
events.map! { |e| (e.is_a?(Class) ? e.codestring : e.to_s) }
|
66
|
+
events.uniq!
|
30
67
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
@@channel_hash[args[0]] ||= super(*args, &block)
|
37
|
-
end
|
38
|
-
end
|
68
|
+
@target_list << [events, proc]
|
69
|
+
nil end
|
70
|
+
|
71
|
+
# Fire an event on this channel
|
72
|
+
def fire(event, blocking:false)
|
39
73
|
|
40
|
-
#
|
41
|
-
|
74
|
+
# Create an instance object from one of several acceptable input forms
|
75
|
+
event = Event.new_from event
|
42
76
|
|
43
|
-
#
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
# Convert all events to strings
|
50
|
-
events = [events] unless events.is_a? Array
|
51
|
-
events.flatten!
|
52
|
-
events.map! { |e| (e.is_a?(Class) ? e.codestring : e.to_s) }
|
53
|
-
events.uniq!
|
54
|
-
|
55
|
-
@target_list << [events, proc]
|
56
|
-
nil end
|
77
|
+
# Fire to each relevant target on each channel
|
78
|
+
for chan in relevant_channels()
|
79
|
+
for target in chan.target_list
|
80
|
+
for string in target[0] & event.class.codestrings
|
81
|
+
self.class.hub << [string, event, blocking, *target[1..-1]]
|
82
|
+
end end end
|
57
83
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
_event = [_event] unless _event.is_a? Array
|
63
|
-
_event, *args = _event
|
64
|
-
|
65
|
-
# Create event object from event as an object, class, or symbol/string
|
66
|
-
event = case _event
|
67
|
-
when Event
|
68
|
-
_event
|
69
|
-
when Class
|
70
|
-
_event.new(*args) if _event < Event
|
71
|
-
else
|
72
|
-
cls = Event.from_codestring(_event.to_s).new(*args)
|
73
|
-
end
|
74
|
-
|
75
|
-
# Fire to each relevant target on each channel
|
76
|
-
for chan in relevant_channels()
|
77
|
-
for target in chan.target_list
|
78
|
-
for string in target[0] & event.class.codestrings
|
79
|
-
Hub << [string, event, blocking, *target[1..-1]]
|
80
|
-
end end end
|
81
|
-
|
82
|
-
nil end
|
84
|
+
nil end
|
85
|
+
|
86
|
+
def relevant_channels
|
87
|
+
return @@channel_hash[hub].values if self==channel_star
|
83
88
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
self.name.channel_name == c.name.channel_name
|
99
|
-
else
|
100
|
-
self.name.to_s == c.name.to_s
|
101
|
-
end
|
89
|
+
if self.name.is_a?(Regexp) then raise TypeError,
|
90
|
+
"Cannot fire on Regexp channel: #{self.name}."\
|
91
|
+
" Regexp channels can only used in event handlers." end
|
92
|
+
|
93
|
+
relevant = [channel_star]
|
94
|
+
for c in @@channel_hash[hub].values
|
95
|
+
relevant << c if \
|
96
|
+
if c.name.is_a?(Regexp)
|
97
|
+
self.name =~ c.name
|
98
|
+
elsif (defined?(c.name.channel_name) and
|
99
|
+
defined?(self.name.channel_name))
|
100
|
+
self.name.channel_name == c.name.channel_name
|
101
|
+
else
|
102
|
+
self.name.to_s == c.name.to_s
|
102
103
|
end
|
103
|
-
return relevant.uniq
|
104
104
|
end
|
105
|
+
return relevant.uniq
|
106
|
+
end
|
105
107
|
end
|
data/lib/wires/events.rb
CHANGED
@@ -69,7 +69,26 @@ class Event < Object # explicit for the sake of Event.ancestry
|
|
69
69
|
"No known Event subclass with codestring: '#{str}'" end
|
70
70
|
cls
|
71
71
|
end
|
72
|
+
|
73
|
+
# Convert an event from 'array notation' to an Event subclass instance
|
74
|
+
# TODO: List acceptable input forms here for documentation
|
75
|
+
def new_from(input)
|
76
|
+
|
77
|
+
# Standardize to array and pull out arguments if they exist
|
78
|
+
input = [input] unless input.is_a? Array
|
79
|
+
input, *args = input
|
72
80
|
|
81
|
+
# Create event object from event as an object, class, or symbol/string
|
82
|
+
event = case input
|
83
|
+
when Event
|
84
|
+
input
|
85
|
+
when Class
|
86
|
+
input.new(*args) if input < Event
|
87
|
+
else
|
88
|
+
Event.from_codestring(input.to_s).new(*args)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
73
92
|
# Create attributes and accessors for all arguments to the constructor.
|
74
93
|
# This is done here rather than in initialize so that the functionality
|
75
94
|
# will remain if the user developer overrides initialize in the subclass.
|
data/lib/wires/hub.rb
CHANGED
@@ -62,25 +62,21 @@ class Hub
|
|
62
62
|
# [+:blocking+] calling thread won't be done until Hub thread is done
|
63
63
|
def kill(*flags)
|
64
64
|
@finish_all = (flags.include? :finish_all)
|
65
|
-
@state=:dying
|
66
|
-
@thread.join if (flags.include? :blocking)
|
65
|
+
@state=:dying
|
66
|
+
@thread.join if (dying? and flags.include? :blocking)
|
67
67
|
nil end
|
68
68
|
|
69
69
|
# Register hook to execute before kill - can call multiple times
|
70
70
|
def before_kill(proc=nil, retain:false, &block)
|
71
71
|
func = (block or proc)
|
72
|
-
|
73
|
-
raise TypeError, "Expected a Proc or code block to execute."
|
74
|
-
end
|
72
|
+
expect_type func, Proc
|
75
73
|
@before_kills << [func, retain]
|
76
74
|
nil end
|
77
75
|
|
78
76
|
# Register hook to execute after kill - can call multiple times
|
79
77
|
def after_kill(proc=nil, retain:false, &block)
|
80
78
|
func = (block or proc)
|
81
|
-
|
82
|
-
raise TypeError, "Expected a Proc or code block to execute."
|
83
|
-
end
|
79
|
+
expect_type func, Proc
|
84
80
|
@after_kills << [func, retain]
|
85
81
|
nil end
|
86
82
|
|
@@ -195,6 +191,7 @@ class Hub
|
|
195
191
|
unhandled_exception(e)
|
196
192
|
end
|
197
193
|
end
|
194
|
+
|
198
195
|
end
|
199
196
|
|
200
197
|
nil end
|
data/lib/wires/time.rb
CHANGED
@@ -1,45 +1,137 @@
|
|
1
|
-
require 'pry'
|
2
1
|
|
3
|
-
class
|
4
|
-
class
|
5
|
-
|
2
|
+
class TimeSchedulerStartEvent < Event; end
|
3
|
+
class TimeSchedulerAnonEvent < Event; end
|
4
|
+
|
5
|
+
|
6
|
+
class TimeSchedulerItem
|
7
|
+
|
8
|
+
attr_reader :time, :event, :channel, :interval
|
9
|
+
|
10
|
+
def initialize(time, event, channel='*',
|
11
|
+
interval:0.seconds, count:1,
|
12
|
+
ignore_past:false, cancel:false)
|
13
|
+
|
14
|
+
expect_type time, Time
|
15
|
+
|
16
|
+
@active = (not cancel)
|
17
|
+
tempcount = count
|
18
|
+
|
19
|
+
while (time < Time.now) and (tempcount > 0)
|
20
|
+
time += interval
|
21
|
+
tempcount -= 1
|
22
|
+
end
|
23
|
+
if not ignore_past
|
24
|
+
time -= interval
|
25
|
+
self.count = count
|
26
|
+
else
|
27
|
+
self.count = tempcount
|
28
|
+
end
|
29
|
+
|
30
|
+
@time = time
|
31
|
+
@event = Event.new_from(event)
|
32
|
+
@channel = channel
|
33
|
+
@interval = interval
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def active?; @active end
|
38
|
+
def inactive?; not @active end
|
39
|
+
def ready?; @active and (Time.now >= @time) end
|
40
|
+
def time_until; (@active ? [(Time.now - @time), 0].max : nil) end
|
41
|
+
|
42
|
+
def cancel; @active = false ;nil end
|
43
|
+
|
44
|
+
# Get/set @count (and apply constraints on set)
|
45
|
+
def count; @count end
|
46
|
+
#TODO: handle explicit cancel?
|
47
|
+
def count=(x); @count=[x,0].max; @active&&=(count>0) ;nil end
|
48
|
+
|
49
|
+
# Inc/dec @count. Necessary because += and -= outside of lock are not atomic!
|
50
|
+
def count_inc(x=1); self.count=(@count+x) end
|
51
|
+
def count_dec(x=1); self.count=(@count-x) end
|
52
|
+
|
53
|
+
# Fire the event now, regardless of time or active status
|
54
|
+
def fire(*args)
|
55
|
+
Channel.new(@channel).fire(@event, *args)
|
56
|
+
count_dec
|
57
|
+
@time += @interval if @active
|
58
|
+
nil end
|
59
|
+
|
60
|
+
# Fire the event only if it is ready
|
61
|
+
def fire_if_ready(*args); self.fire(*args) if ready? end
|
62
|
+
|
63
|
+
# Block until event is ready
|
64
|
+
def wait_until_ready; sleep 0 until ready? end
|
65
|
+
|
66
|
+
# Block until event is ready, then fire and block until it is done
|
67
|
+
def fire_when_ready(*args);
|
68
|
+
wait_until_ready
|
69
|
+
fire_if_ready(*args)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Lock (almost) all instance methods with common re-entrant lock
|
73
|
+
threadlock instance_methods-superclass.instance_methods-[
|
74
|
+
:block_until_ready,
|
75
|
+
:fire_when_ready]
|
76
|
+
end
|
6
77
|
|
7
78
|
# A singleton class to schedule future firing of events
|
8
79
|
class TimeScheduler
|
9
80
|
@schedule = Array.new
|
10
|
-
@schedule_lock = Mutex.new
|
11
81
|
@thread = Thread.new {nil}
|
12
|
-
@
|
82
|
+
@schedule_lock = Monitor.new
|
83
|
+
@dont_sleep = false
|
13
84
|
|
14
85
|
# Operate on the metaclass as a type of singleton pattern
|
15
86
|
class << self
|
16
87
|
|
88
|
+
# Add an event to the schedule
|
89
|
+
def add(new_item)
|
90
|
+
expect_type new_item, TimeSchedulerItem
|
91
|
+
schedule_add(new_item)
|
92
|
+
wakeup
|
93
|
+
nil end
|
94
|
+
# Add an event to the schedule using << operator
|
95
|
+
alias_method :<<, :add
|
96
|
+
|
17
97
|
# Get a copy of the event schedule from outside the class
|
18
|
-
def list; @
|
98
|
+
def list; @schedule.clone end
|
19
99
|
# Clear the event schedule from outside the class
|
20
|
-
def clear; @
|
100
|
+
def clear; @schedule.clear end
|
21
101
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
# Ignore past events if flag is set
|
29
|
-
if ignore_past and time < Time.now; return nil; end
|
30
|
-
|
31
|
-
# Under mutex, push the event into the schedule and sort
|
32
|
-
@schedule_lock.synchronize do
|
33
|
-
@schedule << {time:time, event:event, channel:channel}
|
34
|
-
@schedule.sort! { |a,b| a[:time] <=> b[:time] }
|
35
|
-
end
|
36
|
-
|
37
|
-
# Wakeup main_loop thread if it is sleeping
|
38
|
-
begin @thread.wakeup; rescue ThreadError; end
|
39
|
-
|
102
|
+
private
|
103
|
+
|
104
|
+
def schedule_reshuffle
|
105
|
+
@schedule.select! {|x| x.active?}
|
106
|
+
@schedule.sort! {|a,b| a.time <=> b.time}
|
40
107
|
nil end
|
41
108
|
|
42
|
-
|
109
|
+
def schedule_add(new_item)
|
110
|
+
@schedule << new_item
|
111
|
+
schedule_reshuffle
|
112
|
+
nil end
|
113
|
+
|
114
|
+
def schedule_concat(other_list)
|
115
|
+
@schedule.concat other_list
|
116
|
+
schedule_reshuffle
|
117
|
+
nil end
|
118
|
+
|
119
|
+
def schedule_pull
|
120
|
+
pending = Array.new
|
121
|
+
while ((not @schedule.empty?) and @schedule[0].ready?)
|
122
|
+
pending << @schedule.shift
|
123
|
+
end
|
124
|
+
[pending, @schedule[0]]
|
125
|
+
end
|
126
|
+
|
127
|
+
# Put all functions dealing with @schedule under @schedule_lock
|
128
|
+
threadlock :list,
|
129
|
+
:clear,
|
130
|
+
:schedule_reshuffle,
|
131
|
+
:schedule_add,
|
132
|
+
:schedule_concat,
|
133
|
+
:schedule_pull,
|
134
|
+
lock: :@schedule_lock
|
43
135
|
|
44
136
|
# Do scheduled firing of events as long as Hub is alive
|
45
137
|
def main_loop
|
@@ -51,60 +143,56 @@ class TimeScheduler
|
|
51
143
|
|
52
144
|
while @keepgoing
|
53
145
|
|
54
|
-
#
|
55
|
-
pending
|
56
|
-
|
57
|
-
|
58
|
-
(Time.now > @schedule[0][:time]))
|
59
|
-
pending << @schedule.shift
|
60
|
-
end
|
61
|
-
on_deck = @schedule[0]
|
62
|
-
end
|
63
|
-
|
64
|
-
# Fire pending events
|
65
|
-
pending.each { |x| Channel(x[:channel]).fire(x[:event]) }
|
146
|
+
# Pull, fire, and requeue relevant events
|
147
|
+
pending, on_deck = schedule_pull
|
148
|
+
pending.each { |x| x.fire }
|
149
|
+
schedule_concat pending
|
66
150
|
|
151
|
+
@sleepzone = true
|
67
152
|
# Calculate the time to sleep based on next event's time
|
68
153
|
if on_deck
|
69
|
-
sleep
|
154
|
+
sleep on_deck.time_until
|
70
155
|
else # sleep until wakeup if no event is on deck
|
71
|
-
sleep
|
156
|
+
sleep
|
72
157
|
end
|
73
|
-
|
158
|
+
@sleepzone = false
|
74
159
|
end
|
75
160
|
|
76
161
|
nil end
|
77
162
|
|
163
|
+
def wakeup
|
164
|
+
sleep 0 until @sleepzone==true
|
165
|
+
sleep 0 until @thread.status=='sleep'
|
166
|
+
@thread.wakeup
|
167
|
+
nil end
|
78
168
|
|
79
169
|
end
|
80
170
|
|
81
171
|
# Use fired event to only start scheduler when Hub is running
|
82
172
|
# This also gets the scheduler loop its own thread within the Hub's threads
|
83
173
|
on :time_scheduler_start, self do; main_loop; end;
|
84
|
-
Channel(self).fire(:time_scheduler_start)
|
174
|
+
Channel.new(self).fire(:time_scheduler_start)
|
85
175
|
|
86
176
|
# Stop the main loop upon death of Hub
|
87
177
|
Hub.before_kill(retain:true) do
|
88
|
-
@
|
89
|
-
|
90
|
-
|
91
|
-
end
|
92
|
-
sleep 0
|
178
|
+
sleep 0 until @sleepzone==true
|
179
|
+
@keepgoing=false
|
180
|
+
wakeup
|
93
181
|
end
|
182
|
+
|
94
183
|
# Refire the start event after Hub dies in case it restarts
|
95
184
|
Hub.after_kill(retain:true) do
|
96
|
-
Channel(self).fire(:time_scheduler_start)
|
185
|
+
Channel.new(self).fire(:time_scheduler_start)
|
97
186
|
end
|
98
187
|
|
99
|
-
|
100
188
|
end
|
101
189
|
|
102
190
|
|
103
191
|
# Reopen the Time class and add the fire method to enable nifty syntax like:
|
104
192
|
# 32.minutes.from_now.fire :event
|
105
193
|
class Time
|
106
|
-
def fire(*
|
107
|
-
TimeScheduler.
|
194
|
+
def fire(event, channel='*', **kwargs)
|
195
|
+
TimeScheduler << TimeSchedulerItem.new(self, event, channel, **kwargs)
|
108
196
|
end
|
109
197
|
end
|
110
198
|
|
@@ -116,7 +204,7 @@ class ActiveSupport::Duration
|
|
116
204
|
alias :__original_since :since
|
117
205
|
def since(*args, &block)
|
118
206
|
if block
|
119
|
-
on :time_scheduler_anon, block.object_id do block.call end
|
207
|
+
on :time_scheduler_anon, block.object_id do |e| block.call(e) end
|
120
208
|
__original_since(*args).fire(:time_scheduler_anon,
|
121
209
|
block.object_id)
|
122
210
|
nil
|
@@ -129,7 +217,7 @@ class ActiveSupport::Duration
|
|
129
217
|
alias :__original_ago :ago
|
130
218
|
def ago(*args, &block)
|
131
219
|
if block
|
132
|
-
on :time_scheduler_anon, block.object_id do block.call end
|
220
|
+
on :time_scheduler_anon, block.object_id do |e| block.call(e) end
|
133
221
|
__original_ago(*args).fire(:time_scheduler_anon,
|
134
222
|
block.object_id)
|
135
223
|
nil
|
@@ -139,4 +227,8 @@ class ActiveSupport::Duration
|
|
139
227
|
end
|
140
228
|
alias :until :ago
|
141
229
|
|
142
|
-
end
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
# TODO: Repeatable event sugar?
|
234
|
+
# TODO: Tests for all new functionality
|
data/lib/wires.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'set'
|
2
2
|
require 'thread'
|
3
3
|
require 'active_support/core_ext' # Convenience functions from Rails
|
4
|
+
require 'threadlock' # Easily add re-entrant lock to instance methods
|
4
5
|
|
6
|
+
require 'wires/expect_type'
|
5
7
|
require 'wires/events'
|
6
8
|
require 'wires/hub'
|
7
9
|
require 'wires/channels'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wires
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.12
|
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-07-
|
11
|
+
date: 2013-07-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: threadlock
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
description: An asynchronous (threaded) event routing framework in Ruby. Patch your
|
28
42
|
objects together with wires. Inspired by the python 'circuits' framework.
|
29
43
|
email: joe.eli.mac@gmail.com
|
@@ -34,6 +48,7 @@ files:
|
|
34
48
|
- lib/wires.rb
|
35
49
|
- lib/wires/time.rb
|
36
50
|
- lib/wires/hub.rb
|
51
|
+
- lib/wires/expect_type.rb
|
37
52
|
- lib/wires/events.rb
|
38
53
|
- lib/wires/channels.rb
|
39
54
|
- LICENSE
|