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.
data/lib/wires/time.rb CHANGED
@@ -1,198 +1,202 @@
1
1
 
2
- class TimeSchedulerStartEvent < Event; end
3
- class TimeSchedulerAnonEvent < Event; end
4
-
5
-
6
- class TimeSchedulerItem
2
+ module Wires
7
3
 
8
- attr_reader :time, :event, :channel, :interval
4
+ class TimeSchedulerStartEvent < Event; end
5
+ class TimeSchedulerAnonEvent < Event; end
9
6
 
10
- def initialize(time, event, channel='*',
11
- interval:0.seconds, count:1,
12
- ignore_past:false, cancel:false)
13
-
14
- expect_type time, Time
7
+
8
+ class TimeSchedulerItem
15
9
 
16
- @active = (not cancel)
17
- tempcount = count
10
+ attr_reader :time, :event, :channel, :interval
18
11
 
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
12
+ def initialize(time, event, channel='*',
13
+ interval:0.seconds, count:1,
14
+ ignore_past:false, cancel:false)
15
+
16
+ expect_type time, Time
17
+
18
+ @active = (not cancel)
19
+ tempcount = count
20
+
21
+ while (time < Time.now) and (tempcount > 0)
22
+ time += interval
23
+ tempcount -= 1
24
+ end
25
+ if not ignore_past
26
+ time -= interval
27
+ self.count = count
28
+ else
29
+ self.count = tempcount
30
+ end
31
+
32
+ @time = time
33
+ @event = Event.new_from(event)
34
+ @channel = channel
35
+ @interval = interval
36
+
28
37
  end
29
38
 
30
- @time = time
31
- @event = Event.new_from(event)
32
- @channel = channel
33
- @interval = interval
39
+ def active?; @active end
40
+ def inactive?; not @active end
41
+ def ready?; @active and (Time.now >= @time) end
42
+ def time_until; (@active ? [(Time.now - @time), 0].max : nil) end
34
43
 
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
77
-
78
- # A singleton class to schedule future firing of events
79
- class TimeScheduler
80
- @schedule = Array.new
81
- @thread = Thread.new {nil}
82
- @schedule_lock = Monitor.new
83
- @dont_sleep = false
84
-
85
- # Operate on the metaclass as a type of singleton pattern
86
- class << self
44
+ def cancel; @active = false ;nil end
87
45
 
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
46
+ # Get/set @count (and apply constraints on set)
47
+ def count; @count end
48
+ #TODO: handle explicit cancel?
49
+ def count=(x); @count=[x,0].max; @active&&=(count>0) ;nil end
96
50
 
97
- # Get a copy of the event schedule from outside the class
98
- def list; @schedule.clone end
99
- # Clear the event schedule from outside the class
100
- def clear; @schedule.clear end
51
+ # Inc/dec @count. Necessary because += and -= outside of lock are not atomic!
52
+ def count_inc(x=1); self.count=(@count+x) end
53
+ def count_dec(x=1); self.count=(@count-x) end
101
54
 
102
- private
103
-
104
- def schedule_reshuffle
105
- @schedule.select! {|x| x.active?}
106
- @schedule.sort! {|a,b| a.time <=> b.time}
55
+ # Fire the event now, regardless of time or active status
56
+ def fire(*args)
57
+ Channel.new(@channel).fire(@event, *args)
58
+ count_dec
59
+ @time += @interval if @active
107
60
  nil end
108
61
 
109
- def schedule_add(new_item)
110
- @schedule << new_item
111
- schedule_reshuffle
112
- nil end
62
+ # Fire the event only if it is ready
63
+ def fire_if_ready(*args); self.fire(*args) if ready? end
113
64
 
114
- def schedule_concat(other_list)
115
- @schedule.concat other_list
116
- schedule_reshuffle
117
- nil end
65
+ # Block until event is ready
66
+ def wait_until_ready; sleep 0 until ready? end
118
67
 
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]]
68
+ # Block until event is ready, then fire and block until it is done
69
+ def fire_when_ready(*args);
70
+ wait_until_ready
71
+ fire_if_ready(*args)
125
72
  end
126
73
 
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
135
-
136
- # Do scheduled firing of events as long as Hub is alive
137
- def main_loop
74
+ # Lock (almost) all instance methods with common re-entrant lock
75
+ threadlock instance_methods-superclass.instance_methods-[
76
+ :block_until_ready,
77
+ :fire_when_ready]
78
+ end
79
+
80
+ # A singleton class to schedule future firing of events
81
+ class TimeScheduler
82
+ @schedule = Array.new
83
+ @thread = Thread.new {nil}
84
+ @schedule_lock = Monitor.new
85
+ @dont_sleep = false
86
+
87
+ # Operate on the metaclass as a type of singleton pattern
88
+ class << self
89
+
90
+ # Add an event to the schedule
91
+ def add(new_item)
92
+ expect_type new_item, TimeSchedulerItem
93
+ schedule_add(new_item)
94
+ wakeup
95
+ nil end
96
+ # Add an event to the schedule using << operator
97
+ alias_method :<<, :add
98
+
99
+ # Get a copy of the event schedule from outside the class
100
+ def list; @schedule.clone end
101
+ # Clear the event schedule from outside the class
102
+ def clear; @schedule.clear end
103
+
104
+ private
105
+
106
+ def schedule_reshuffle
107
+ @schedule.select! {|x| x.active?}
108
+ @schedule.sort! {|a,b| a.time <=> b.time}
109
+ nil end
110
+
111
+ def schedule_add(new_item)
112
+ @schedule << new_item
113
+ schedule_reshuffle
114
+ nil end
138
115
 
139
- @keepgoing = true
140
- @thread = Thread.current
141
- pending = Array.new
142
- on_deck = nil
116
+ def schedule_concat(other_list)
117
+ @schedule.concat other_list
118
+ schedule_reshuffle
119
+ nil end
143
120
 
144
- while @keepgoing
121
+ def schedule_pull
122
+ pending = Array.new
123
+ while ((not @schedule.empty?) and @schedule[0].ready?)
124
+ pending << @schedule.shift
125
+ end
126
+ [pending, @schedule[0]]
127
+ end
128
+
129
+ # Put all functions dealing with @schedule under @schedule_lock
130
+ threadlock :list,
131
+ :clear,
132
+ :schedule_reshuffle,
133
+ :schedule_add,
134
+ :schedule_concat,
135
+ :schedule_pull,
136
+ lock: :@schedule_lock
137
+
138
+ # Do scheduled firing of events as long as Hub is alive
139
+ def main_loop
145
140
 
146
- # Pull, fire, and requeue relevant events
147
- pending, on_deck = schedule_pull
148
- pending.each { |x| x.fire }
149
- schedule_concat pending
141
+ @keepgoing = true
142
+ @thread = Thread.current
143
+ pending = Array.new
144
+ on_deck = nil
150
145
 
151
- @sleepzone = true
152
- # Calculate the time to sleep based on next event's time
153
- if on_deck
154
- sleep on_deck.time_until
155
- else # sleep until wakeup if no event is on deck
156
- sleep
146
+ while @keepgoing
147
+
148
+ # Pull, fire, and requeue relevant events
149
+ pending, on_deck = schedule_pull
150
+ pending.each { |x| x.fire }
151
+ schedule_concat pending
152
+
153
+ @sleepzone = true
154
+ # Calculate the time to sleep based on next event's time
155
+ if on_deck
156
+ sleep on_deck.time_until
157
+ else # sleep until wakeup if no event is on deck
158
+ sleep
159
+ end
160
+ @sleepzone = false
157
161
  end
158
- @sleepzone = false
159
- end
162
+
163
+ nil end
160
164
 
161
- nil end
165
+ def wakeup
166
+ sleep 0 until @sleepzone==true
167
+ sleep 0 until @thread.status=='sleep'
168
+ @thread.wakeup
169
+ nil end
170
+
171
+ end
172
+
173
+ # Use fired event to only start scheduler when Hub is running
174
+ # This also gets the scheduler loop its own thread within the Hub's threads
175
+ on :time_scheduler_start, self do; main_loop; end;
176
+ Channel.new(self).fire(:time_scheduler_start)
162
177
 
163
- def wakeup
178
+ # Stop the main loop upon death of Hub
179
+ Hub.before_kill(retain:true) do
164
180
  sleep 0 until @sleepzone==true
165
- sleep 0 until @thread.status=='sleep'
166
- @thread.wakeup
167
- nil end
181
+ @keepgoing=false
182
+ wakeup
183
+ end
184
+
185
+ # Refire the start event after Hub dies in case it restarts
186
+ Hub.after_kill(retain:true) do
187
+ Channel.new(self).fire(:time_scheduler_start)
188
+ end
168
189
 
169
190
  end
170
191
 
171
- # Use fired event to only start scheduler when Hub is running
172
- # This also gets the scheduler loop its own thread within the Hub's threads
173
- on :time_scheduler_start, self do; main_loop; end;
174
- Channel.new(self).fire(:time_scheduler_start)
175
-
176
- # Stop the main loop upon death of Hub
177
- Hub.before_kill(retain:true) do
178
- sleep 0 until @sleepzone==true
179
- @keepgoing=false
180
- wakeup
181
- end
182
-
183
- # Refire the start event after Hub dies in case it restarts
184
- Hub.after_kill(retain:true) do
185
- Channel.new(self).fire(:time_scheduler_start)
186
- end
187
-
188
- end
189
-
192
+ end # End Wires module.
190
193
 
191
194
  # Reopen the Time class and add the fire method to enable nifty syntax like:
192
195
  # 32.minutes.from_now.fire :event
193
196
  class Time
194
197
  def fire(event, channel='*', **kwargs)
195
- TimeScheduler << TimeSchedulerItem.new(self, event, channel, **kwargs)
198
+ Wires::TimeScheduler << \
199
+ Wires::TimeSchedulerItem.new(self, event, channel, **kwargs)
196
200
  end
197
201
  end
198
202
 
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.12
4
+ version: 0.2.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-07-05 00:00:00.000000000 Z
11
+ date: 2013-07-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
41
69
  description: An asynchronous (threaded) event routing framework in Ruby. Patch your
42
70
  objects together with wires. Inspired by the python 'circuits' framework.
43
71
  email: joe.eli.mac@gmail.com
@@ -49,8 +77,8 @@ files:
49
77
  - lib/wires/time.rb
50
78
  - lib/wires/hub.rb
51
79
  - lib/wires/expect_type.rb
52
- - lib/wires/events.rb
53
- - lib/wires/channels.rb
80
+ - lib/wires/channel.rb
81
+ - lib/wires/event.rb
54
82
  - LICENSE
55
83
  - README.md
56
84
  homepage: https://github.com/jemc/wires/
@@ -1,107 +0,0 @@
1
-
2
- def on(events, channels='*', &codeblock)
3
- channels = [channels] unless channels.is_a? Array
4
- for channel in channels
5
- Channel.new(channel).register(events, codeblock)
6
- end
7
- nil end
8
-
9
-
10
- def fire(event, channel='*')
11
- Channel.new(channel).fire(event, blocking:false)
12
- nil end
13
-
14
- def fire_and_wait(event, channel='*')
15
- Channel.new(channel).fire(event, blocking:true)
16
- nil end
17
-
18
-
19
- def Channel(*args) Channel.new(*args) end
20
-
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)
58
-
59
- if not proc.is_a?(Proc) then raise SyntaxError, \
60
- "No Proc given to execute on event: #{events}" end
61
-
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!
67
-
68
- @target_list << [events, proc]
69
- nil end
70
-
71
- # Fire an event on this channel
72
- def fire(event, blocking:false)
73
-
74
- # Create an instance object from one of several acceptable input forms
75
- event = Event.new_from event
76
-
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
83
-
84
- nil end
85
-
86
- def relevant_channels
87
- return @@channel_hash[hub].values if self==channel_star
88
-
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
103
- end
104
- end
105
- return relevant.uniq
106
- end
107
- end