wires 0.1.12 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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