wires 0.1.12 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 00826eb63c92ee41f33d4b9afca45cf79f6074ef
4
- data.tar.gz: 7547996adc41b8f58905b22b50f317f403ef56b3
3
+ metadata.gz: c13e2e944a09925c428bbcfd549162c0dd64a782
4
+ data.tar.gz: a7addc2fbf45a815e96ead41faf7bc9bd9f9e006
5
5
  SHA512:
6
- metadata.gz: 64f55f73930e572d8fdb589a8c84f302396a87ff56ec851e0a6fe24b7666dd8f572ee6638169a0571728e6364ea9b85d38d6d843b133656690cd22072cd00e8c
7
- data.tar.gz: efac20e8fc999a1e219ff778ee92cbeb0c5ebfd2e520708b382bd86411a6133c6ceac8ab7084620439e6c8ae4a4ff98bdece2136aa3f4bcc9a73dc3bb968bf71
6
+ metadata.gz: d175ae2bc54184972ca61169961256df0ff323a1fa0bbd77cdb328932d9f6f6a224902d181a23e8824bf7d57a7e715a8ffd5ace9ebab1978ea065a01d62afb23
7
+ data.tar.gz: f65e946b8aa4ac6ec4ceb3510d3ecb1ee65669e570f9989b6f276471386b7f3f9fb849c51b1c64d2db756ad2d73397817f4f81e332f5857aa07dba7d8f7f564b
data/lib/wires.rb CHANGED
@@ -4,7 +4,7 @@ require 'active_support/core_ext' # Convenience functions from Rails
4
4
  require 'threadlock' # Easily add re-entrant lock to instance methods
5
5
 
6
6
  require 'wires/expect_type'
7
- require 'wires/events'
7
+ require 'wires/event'
8
8
  require 'wires/hub'
9
- require 'wires/channels'
9
+ require 'wires/channel'
10
10
  require 'wires/time'
@@ -0,0 +1,109 @@
1
+
2
+ def on(events, channels='*', &codeblock)
3
+ channels = [channels] unless channels.is_a? Array
4
+ for channel in channels
5
+ Wires::Channel.new(channel).register(events, codeblock)
6
+ end
7
+ nil end
8
+
9
+
10
+ def fire(event, channel='*')
11
+ Wires::Channel.new(channel).fire(event, blocking:false)
12
+ nil end
13
+
14
+ def fire_and_wait(event, channel='*')
15
+ Wires::Channel.new(channel).fire(event, blocking:true)
16
+ nil end
17
+
18
+
19
+ def Channel(*args) Wires::Channel.new(*args) end
20
+
21
+ module Wires
22
+ class Channel
23
+
24
+ attr_reader :name
25
+ attr_reader :target_list
26
+
27
+ def initialize(name)
28
+ @name = name
29
+ @target_list = Set.new
30
+ nil end
31
+
32
+
33
+ # Redefine this class method to use an alternate Hub
34
+ def self.hub; Hub; end
35
+ # Don't redefine this instance method!
36
+ def hub; self.class.hub; end
37
+
38
+ # Channel registry hash and star channel reference are values
39
+ # In this Hash with the key being the reference to the Hub
40
+ @@channel_hash = Hash.new
41
+ @@channel_star = Hash.new
42
+
43
+ # Give out references to the star channel
44
+ def self.channel_star; @@channel_star[self.hub]; end
45
+ def channel_star; @@channel_star[self.hub]; end
46
+
47
+ # Ensure that there is only one instance of Channel per name
48
+ @@new_lock = Mutex.new
49
+ def self.new(*args, &block)
50
+ @@channel_star[self.hub] ||= Channel.new('*') unless (args[0]=='*')
51
+ @@new_lock.synchronize do
52
+ @@channel_hash[self.hub] ||= Hash.new
53
+ @@channel_hash[self.hub][args[0]] ||= super(*args, &block)
54
+ end
55
+ end
56
+
57
+ # Register a proc to be triggered by an event on this channel
58
+ def register(events, proc)
59
+
60
+ if not proc.is_a?(Proc) then raise SyntaxError, \
61
+ "No Proc given to execute on event: #{events}" end
62
+
63
+ # Convert all events to strings
64
+ events = [events] unless events.is_a? Array
65
+ events.flatten!
66
+ events.map! { |e| (e.is_a?(Class) ? e.codestring : e.to_s) }
67
+ events.uniq!
68
+
69
+ @target_list << [events, proc]
70
+ nil end
71
+
72
+ # Fire an event on this channel
73
+ def fire(event, blocking:false)
74
+
75
+ # Create an instance object from one of several acceptable input forms
76
+ event = Event.new_from event
77
+
78
+ # Fire to each relevant target on each channel
79
+ for chan in relevant_channels()
80
+ for target in chan.target_list
81
+ for string in target[0] & event.class.codestrings
82
+ self.class.hub << [string, event, blocking, *target[1..-1]]
83
+ end end end
84
+
85
+ nil end
86
+
87
+ def relevant_channels
88
+ return @@channel_hash[hub].values if self==channel_star
89
+
90
+ if self.name.is_a?(Regexp) then raise TypeError,
91
+ "Cannot fire on Regexp channel: #{self.name}."\
92
+ " Regexp channels can only used in event handlers." end
93
+
94
+ relevant = [channel_star]
95
+ for c in @@channel_hash[hub].values
96
+ relevant << c if \
97
+ if c.name.is_a?(Regexp)
98
+ self.name =~ c.name
99
+ elsif (defined?(c.name.channel_name) and
100
+ defined?(self.name.channel_name))
101
+ self.name.channel_name == c.name.channel_name
102
+ else
103
+ self.name.to_s == c.name.to_s
104
+ end
105
+ end
106
+ return relevant.uniq
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,161 @@
1
+
2
+ module Wires
3
+ # Store a list of all Event classes that get loaded.
4
+ class EventRegistry
5
+ @@registry = []
6
+
7
+ def self.<<(cls)
8
+ @@registry << cls
9
+ @@registry.uniq!
10
+ end
11
+
12
+ def self.list
13
+ @@registry
14
+ end
15
+ end
16
+
17
+ # All Event classes should inherit from this one
18
+ class Event < Object # explicit for the sake of Event.ancestry
19
+
20
+ # Register with the EventRegistry and make subclasses do the same
21
+ EventRegistry << self
22
+
23
+ # Operate on the metaclass as a type of singleton pattern
24
+ class << self
25
+
26
+ def inherited(subcls)
27
+
28
+ # Be sure codestring doesn't conflict
29
+ existing = _from_codestring(subcls.codestring)
30
+ if existing then raise NameError, \
31
+ "New Event subclass '#{subcls}' conflicts with"\
32
+ " existing Event subclass '#{existing}'."\
33
+ " The generated codestring '#{subcls.codestring}'"\
34
+ " must be unique for each Event subclass." end
35
+
36
+ # Register, then call super
37
+ EventRegistry << subcls
38
+ super
39
+ end
40
+
41
+ # List of class inheritance lineage back to but excluding Object
42
+ def ancestry(cls=self)
43
+ _next = cls.superclass
44
+ [cls==Object ? [] : [cls, ancestry(_next)]].flatten
45
+ end
46
+
47
+ # Convert class <ClassNameEvent> to string "class_name"
48
+ def codestring(cls=self)
49
+ File.basename cls.to_s
50
+ .underscore
51
+ .gsub(/_event$/, "")
52
+ end
53
+
54
+ # List of codestrings associated with this event and ancestors
55
+ def codestrings
56
+ x = ancestry
57
+ .map {|cls| cls.codestring}
58
+ end
59
+
60
+ # Pull class from registry by codestring
61
+ # (more reliable than crafting a reverse regexp)
62
+ def _from_codestring(str)
63
+ return EventRegistry.list
64
+ .select{|e| e.codestring==str}[0]
65
+ end; private :_from_codestring
66
+
67
+ def from_codestring(str)
68
+ cls = _from_codestring(str.to_s)
69
+ if not cls then raise NameError,
70
+ "No known Event subclass with codestring: '#{str}'" end
71
+ cls
72
+ end
73
+
74
+ # Convert an event from 'array notation' to an Event subclass instance
75
+ # TODO: List acceptable input forms here for documentation
76
+ def new_from(input)
77
+
78
+ # Standardize to array and pull out arguments if they exist
79
+ input = [input] unless input.is_a? Array
80
+ input, *args = input
81
+
82
+ # Create event object from event as an object, class, or symbol/string
83
+ event = case input
84
+ when Event
85
+ input
86
+ when Class
87
+ input.new(*args) if input < Event
88
+ else
89
+ Event.from_codestring(input.to_s).new(*args)
90
+ end
91
+ end
92
+
93
+ # Create attributes and accessors for all arguments to the constructor.
94
+ # This is done here rather than in initialize so that the functionality
95
+ # will remain if the user developer overrides initialize in the subclass.
96
+ def new(*args, &block)
97
+ obj = super
98
+
99
+ kwargs = args[-1].is_a?(Hash) ? args.pop : Hash.new
100
+ kwargs[:args] = args
101
+ kwargs[:codeblock] = block if block
102
+ for key in kwargs.keys
103
+ att = key.to_s
104
+ obj.instance_variable_set("@#{att}", kwargs[key])
105
+ class_eval("def #{att}; @#{att}; end")
106
+ class_eval("def #{att}=(val); @#{att}=val; end")
107
+ end
108
+
109
+ obj
110
+ end
111
+
112
+ end
113
+
114
+ # Calling super in new with *args will complain if this isn't here
115
+ def initialize(*args, &block) end
116
+ end
117
+
118
+
119
+ #
120
+ # Comparison support for Events and Symbols/Strings
121
+ #
122
+
123
+ # Reopen Event and add comparison functions
124
+ class Event
125
+ class << self
126
+ def ==(other)
127
+ other.is_a?(Class) ?
128
+ super : codestring==other.to_s
129
+ end
130
+ def <=(other)
131
+ other.is_a?(Class) ?
132
+ super : codestrings.include?(other.to_s)
133
+ end
134
+ def <(other)
135
+ other.is_a?(Class) ?
136
+ super : (self<=other and not self==other)
137
+ end
138
+ def >=(other)
139
+ other.is_a?(Class) ?
140
+ super : Event.from_codestring(other.to_s)<=self
141
+ end
142
+ def >(other)
143
+ other.is_a?(Class) ?
144
+ super : Event.from_codestring(other.to_s)<self
145
+ end
146
+ end
147
+ end
148
+
149
+ # Autogenerate the inverse comparison functions for Symbol/String
150
+ for cls in [Symbol, String]
151
+ %w(== < > <= >=).zip(%w(== > < >= <=))
152
+ .each do |ops|
153
+ op, opinv = ops # unzip operator and inverse operator
154
+ cls.class_eval(
155
+ "def #{op}(other)\n"\
156
+ " (other.is_a?(Class) and other<=Event) ? \n"\
157
+ " (other#{opinv}self) : super\n"\
158
+ "end\n")
159
+ end
160
+ end
161
+ end
data/lib/wires/hub.rb CHANGED
@@ -2,203 +2,204 @@
2
2
  # Make sure puts goes to $stdout for all threads!
3
3
  def puts(x) $stdout.puts(x) end
4
4
 
5
-
6
- # An Event Hub. Event/proc associations come in, and the procs
7
- # get called in new threads in the order received
8
- class Hub
9
- @queue = Queue.new
10
- @state = [:dead, :alive, :dying][0]
11
-
12
- @child_threads = Array.new
13
- @child_threads_lock = Mutex.new
14
-
15
- @before_kills = Queue.new
16
- @after_kills = Queue.new
17
-
18
- # Operate on the metaclass as a type of singleton pattern
19
- class << self
5
+ module Wires
6
+ # An Event Hub. Event/proc associations come in, and the procs
7
+ # get called in new threads in the order received
8
+ class Hub
9
+ @queue = Queue.new
10
+ @state = [:dead, :alive, :dying][0]
20
11
 
21
- def dead?; @state==:dead end
22
- def alive?; @state==:alive end
23
- def dying?; @state==:dying end
24
- def state; @state end
12
+ @child_threads = Array.new
13
+ @child_threads_lock = Mutex.new
25
14
 
26
- # Clear the Hub queue, but do not kill working threads
27
- def clear; @queue.clear end
15
+ @before_kills = Queue.new
16
+ @after_kills = Queue.new
28
17
 
29
- ##
30
- # Start the Hub event loop (optional flags change thread behavior)
31
- #
32
- # valid flags:
33
- # [+:blocking+] Hub event loop will be run in calling thread,
34
- # blocking until Hub is killed. If this flag is not
35
- # specified, the Hub event loop is run in a new thread,
36
- # which the main thread joins in at_exit.
37
- def run(*flags)
38
- if dead? # Only run if not already alive or dying
39
-
40
- # If :blocking is not set, run in a new thread and join at_exit
41
- if not (flags.include? :blocking)
42
- @thread = Thread.new() do
43
- self.send(:run_loop)
44
- end
45
- # Only join if main thread wasn't killed by an exception
46
- at_exit { @thread.join if not $! }
18
+ # Operate on the metaclass as a type of singleton pattern
19
+ class << self
20
+
21
+ def dead?; @state==:dead end
22
+ def alive?; @state==:alive end
23
+ def dying?; @state==:dying end
24
+ def state; @state end
25
+
26
+ # Clear the Hub queue, but do not kill working threads
27
+ def clear; @queue.clear end
28
+
29
+ ##
30
+ # Start the Hub event loop (optional flags change thread behavior)
31
+ #
32
+ # valid flags:
33
+ # [+:blocking+] Hub event loop will be run in calling thread,
34
+ # blocking until Hub is killed. If this flag is not
35
+ # specified, the Hub event loop is run in a new thread,
36
+ # which the main thread joins in at_exit.
37
+ def run(*flags)
38
+ if dead? # Only run if not already alive or dying
39
+
40
+ # If :blocking is not set, run in a new thread and join at_exit
41
+ if not (flags.include? :blocking)
42
+ @thread = Thread.new() do
43
+ self.send(:run_loop)
44
+ end
45
+ # Only join if main thread wasn't killed by an exception
46
+ at_exit { @thread.join if not $! }
47
+
48
+ # If :blocking is set, run in main thread and block until Hub death
49
+ else self.send(:run_loop) end
50
+
51
+ end
47
52
 
48
- # If :blocking is set, run in main thread and block until Hub death
49
- else self.send(:run_loop) end
53
+ sleep 0 # Yield to other threads
50
54
 
51
- end
55
+ nil end
52
56
 
53
- sleep 0 # Yield to other threads
57
+ ##
58
+ # Kill the Hub event loop (optional flags change thread behavior)
59
+ #
60
+ # valid flags:
61
+ # [+:finish_all+] Hub thread won't be done until all child threads done
62
+ # [+:blocking+] calling thread won't be done until Hub thread is done
63
+ def kill(*flags)
64
+ @finish_all = (flags.include? :finish_all)
65
+ @state=:dying
66
+ @thread.join if (dying? and flags.include? :blocking)
67
+ nil end
54
68
 
55
- nil end
56
-
57
- ##
58
- # Kill the Hub event loop (optional flags change thread behavior)
59
- #
60
- # valid flags:
61
- # [+:finish_all+] Hub thread won't be done until all child threads done
62
- # [+:blocking+] calling thread won't be done until Hub thread is done
63
- def kill(*flags)
64
- @finish_all = (flags.include? :finish_all)
65
- @state=:dying
66
- @thread.join if (dying? and flags.include? :blocking)
67
- nil end
68
-
69
- # Register hook to execute before kill - can call multiple times
70
- def before_kill(proc=nil, retain:false, &block)
71
- func = (block or proc)
72
- expect_type func, Proc
73
- @before_kills << [func, retain]
74
- nil end
75
-
76
- # Register hook to execute after kill - can call multiple times
77
- def after_kill(proc=nil, retain:false, &block)
78
- func = (block or proc)
79
- expect_type func, Proc
80
- @after_kills << [func, retain]
81
- nil end
82
-
83
- # Put x in the queue, and block until x is processed (if Hub is running)
84
- def fire(x)
85
- if not dead? # yield to event loop thread until awoken by it later
86
- @queue << [x, Thread.current]
87
- sleep
88
- else # don't wait if Hub isn't running - would cause lockup
89
- @queue << [x, nil]
90
- end
91
- nil end
92
- def <<(x); fire(x); end
93
-
94
- private
95
-
96
- # Kill all currently working child threads
97
- # Newly fired events could still queue up,
98
- # Waiting to be born until this thread is done killing
99
- def kill_children
100
- @child_threads_lock.synchronize do
101
- until @child_threads.empty?
102
- @child_threads.shift.exit
103
- end
104
- end
105
- nil end
106
-
107
- # Kill all currently working child threads
108
- # Newly fired events could still queue up,
109
- # But they will be cleared out and never be born
110
- def kill_children_and_clear
111
- @child_threads_lock.synchronize do
112
- until @child_threads.empty?
113
- @child_threads.shift.exit
69
+ # Register hook to execute before kill - can call multiple times
70
+ def before_kill(proc=nil, retain:false, &block)
71
+ func = (block or proc)
72
+ expect_type func, Proc
73
+ @before_kills << [func, retain]
74
+ nil end
75
+
76
+ # Register hook to execute after kill - can call multiple times
77
+ def after_kill(proc=nil, retain:false, &block)
78
+ func = (block or proc)
79
+ expect_type func, Proc
80
+ @after_kills << [func, retain]
81
+ nil end
82
+
83
+ # Put x in the queue, and block until x is processed (if Hub is running)
84
+ def fire(x)
85
+ if not dead? # yield to event loop thread until awoken by it later
86
+ @queue << [x, Thread.current]
87
+ sleep
88
+ else # don't wait if Hub isn't running - would cause lockup
89
+ @queue << [x, nil]
114
90
  end
115
- clear
116
- end
117
- nil end
91
+ nil end
92
+ def <<(x); fire(x); end
93
+
94
+ private
118
95
 
119
- # Join child threads, one by one, allowing more children to appear
120
- def join_children
121
- a_thread = Thread.new{nil}
122
- while a_thread
96
+ # Kill all currently working child threads
97
+ # Newly fired events could still queue up,
98
+ # Waiting to be born until this thread is done killing
99
+ def kill_children
123
100
  @child_threads_lock.synchronize do
124
- a_thread = @child_threads.shift
101
+ until @child_threads.empty?
102
+ @child_threads.shift.exit
103
+ end
125
104
  end
126
- a_thread.join if a_thread
127
- sleep 0 # Yield to other threads
128
- end
129
- nil end
130
-
131
- # Flush/run queue of [proc, retain]s, retaining those with retain==true
132
- def run_hooks(queue)
133
- retained = Queue.new
134
- while not queue.empty?
135
- proc, retain = queue.shift
136
- retained << [proc, retain] if retain
137
- proc.call
138
- end
139
- while not retained.empty?
140
- queue << retained.shift
141
- end
142
- nil end
143
-
144
- # Run before_kill hooks, optionally join child threads, then die
145
- def die
146
- run_hooks(@before_kills)
147
- join_children if @finish_all
148
- @state = :dead
149
- nil end
150
-
151
- # Run main event loop, not finished until Hub is killed
152
- def run_loop
153
- @state = :alive
154
-
155
- while not dead?
156
- if @queue.empty? then sleep(0)
157
- else process_item(@queue.shift) end
158
-
159
- if dying?
160
- die_thread ||= Thread.new { die }
105
+ nil end
106
+
107
+ # Kill all currently working child threads
108
+ # Newly fired events could still queue up,
109
+ # But they will be cleared out and never be born
110
+ def kill_children_and_clear
111
+ @child_threads_lock.synchronize do
112
+ until @child_threads.empty?
113
+ @child_threads.shift.exit
114
+ end
115
+ clear
161
116
  end
162
- end
117
+ nil end
163
118
 
164
- run_hooks(@after_kills)
165
- @finish_all = false
119
+ # Join child threads, one by one, allowing more children to appear
120
+ def join_children
121
+ a_thread = Thread.new{nil}
122
+ while a_thread
123
+ @child_threads_lock.synchronize do
124
+ a_thread = @child_threads.shift
125
+ end
126
+ a_thread.join if a_thread
127
+ sleep 0 # Yield to other threads
128
+ end
129
+ nil end
166
130
 
167
- nil end
131
+ # Flush/run queue of [proc, retain]s, retaining those with retain==true
132
+ def run_hooks(queue)
133
+ retained = Queue.new
134
+ while not queue.empty?
135
+ proc, retain = queue.shift
136
+ retained << [proc, retain] if retain
137
+ proc.call
138
+ end
139
+ while not retained.empty?
140
+ queue << retained.shift
141
+ end
142
+ nil end
168
143
 
169
- def process_item(x)
170
- x, waiting_thread = x
171
- string, event, blocking, proc = x
144
+ # Run before_kill hooks, optionally join child threads, then die
145
+ def die
146
+ run_hooks(@before_kills)
147
+ join_children if @finish_all
148
+ @state = :dead
149
+ nil end
172
150
 
173
- # Do all dealings with @child_threads under mutex
174
- @child_threads_lock.synchronize do
151
+ # Run main event loop, not finished until Hub is killed
152
+ def run_loop
153
+ @state = :alive
154
+
155
+ while not dead?
156
+ if @queue.empty? then sleep(0)
157
+ else process_item(@queue.shift) end
158
+
159
+ if dying?
160
+ die_thread ||= Thread.new { die }
161
+ end
162
+ end
175
163
 
176
- # Clear dead child threads to free up memory
177
- @child_threads.select! {|t| t.status}
164
+ run_hooks(@after_kills)
165
+ @finish_all = false
178
166
 
179
- # Start the new child thread
180
- @child_threads << Thread.new do
181
- begin
182
- waiting_thread.wakeup unless blocking or not waiting_thread
183
- proc.call(event)
184
- waiting_thread.wakeup if blocking and waiting_thread
185
-
186
- rescue Interrupt, SystemExit => e
187
- @state = :dying
188
- unhandled_exception(e)
189
-
190
- rescue Exception => e
191
- unhandled_exception(e)
167
+ nil end
168
+
169
+ def process_item(x)
170
+ x, waiting_thread = x
171
+ string, event, blocking, proc = x
172
+
173
+ # Do all dealings with @child_threads under mutex
174
+ @child_threads_lock.synchronize do
175
+
176
+ # Clear dead child threads to free up memory
177
+ @child_threads.select! {|t| t.status}
178
+
179
+ # Start the new child thread
180
+ @child_threads << Thread.new do
181
+ begin
182
+ waiting_thread.wakeup unless blocking or not waiting_thread
183
+ proc.call(event)
184
+ waiting_thread.wakeup if blocking and waiting_thread
185
+
186
+ rescue Interrupt, SystemExit => e
187
+ @state = :dying
188
+ unhandled_exception(e)
189
+
190
+ rescue Exception => e
191
+ unhandled_exception(e)
192
+ end
192
193
  end
194
+
193
195
  end
194
-
195
- end
196
-
197
- nil end
198
-
199
- def unhandled_exception(x)
200
- $stderr.puts $!
201
- $stderr.puts $@
202
- nil end
196
+
197
+ nil end
198
+
199
+ def unhandled_exception(x)
200
+ $stderr.puts $!
201
+ $stderr.puts $@
202
+ nil end
203
+ end
203
204
  end
204
- end
205
+ end