wires 0.4.3 → 0.5.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: 2b7c2dd1598b631eb5b5db562ca8fcc480c7e5bd
4
- data.tar.gz: c04825a10cddd424a1e7a3dcab51c92106d72d94
3
+ metadata.gz: 04ad9bc6ef2ce9a54aad3343c13fca6ca69f1895
4
+ data.tar.gz: cf519182f4a9cd03776cb749d3bd922aeb5add3a
5
5
  SHA512:
6
- metadata.gz: 1fa7da548738147947d300b015d6f6e3e0c1317429af697a41aa933b1f7f912402c45e367e7d43dc51d915aa7dd3a171b220c015108e9ca68758adec644d34de
7
- data.tar.gz: 92d1414f7dabd03d733b1fdbb9a04ee618428c6e57c03efb3df6edff6f1baaba9da9f2c95c5c7f398a970d0e3d2974bedf0ada9410a5ddfebf597d8200155132
6
+ metadata.gz: 435a2df3af44aa3bd5b00a3f37d7abb273cb9be65276bfb717441b64bed14bb38b2b7bbc9044c60708b66db168799172319da9ef317bfb7ac0873d23ef3a9b15
7
+ data.tar.gz: b753ee2f1123be7b186f59ea37bfab07ba20d737e9fde3ec931d1cbe2cefc616d1149b4021da73b010210b81a4d3987b9dc9b424cdd39444909d2de3a53cea40
@@ -4,16 +4,19 @@ module Wires
4
4
  class Channel
5
5
 
6
6
  attr_reader :name
7
- attr_reader :target_list
7
+ attr_reader :handlers
8
8
  attr_accessor :not_firable
9
9
 
10
10
  def inspect; "#{self.class}(#{name.inspect})"; end
11
11
 
12
12
  @hub = Hub
13
13
  @router = Router::Default
14
- @new_lock = Mutex.new
14
+ @new_lock = Monitor.new
15
15
  @@aim_lock = Mutex.new
16
16
 
17
+ # Add hook methods
18
+ extend Util::Hooks
19
+
17
20
  class << self
18
21
  attr_accessor :hub
19
22
  attr_accessor :router
@@ -28,7 +31,7 @@ module Wires
28
31
 
29
32
  def initialize(name)
30
33
  @name = name
31
- @target_list = []
34
+ @handlers = []
32
35
  end
33
36
 
34
37
  # Register a proc to be triggered by an event on this channel
@@ -36,11 +39,11 @@ module Wires
36
39
  def register(*events, &proc)
37
40
  raise ArgumentError, "No callable given to execute on event: #{events}" \
38
41
  unless proc.respond_to? :call
39
- events = Event.new_from(*events)
42
+ events = Event.list_from *events
40
43
 
41
44
  @@aim_lock.synchronize do
42
- @target_list << [events, proc] \
43
- unless @target_list.include? [events, proc]
45
+ @handlers << [events, proc] \
46
+ unless @handlers.include? [events, proc]
44
47
  end
45
48
 
46
49
  proc
@@ -48,33 +51,16 @@ module Wires
48
51
 
49
52
  # Unregister a proc from the target list of this channel
50
53
  # Return true if at least one matching target was unregistered, else false
51
- def unregister(*events, &proc)
52
- events = events.empty? ? [] : Event.new_from(*events)
53
-
54
+ def unregister(proc)
54
55
  @@aim_lock.synchronize do
55
- !!(@target_list.reject! do |es,pr|
56
- (proc and proc==pr) and \
57
- (events.map{|event| es.map{|e| event=~e}.any?}.all?)
56
+ !!(@handlers.reject! do |stored_events, stored_proc|
57
+ proc==stored_proc
58
58
  end)
59
59
  end
60
60
  end
61
61
 
62
- # Add hook methods
63
- class << self
64
- include Util::Hooks
65
-
66
- def before_fire(*args, &proc)
67
- add_hook(:@before_fire, *args, &proc)
68
- end
69
-
70
- def after_fire(*args, &proc)
71
- add_hook(:@after_fire, *args, &proc)
72
- end
73
- end
74
-
75
62
  # Fire an event on this channel
76
63
  def fire(input, blocking:false, parallel:!blocking)
77
-
78
64
  raise *@not_firable if @not_firable
79
65
 
80
66
  return [] << Thread.new { fire(input, blocking:true, parallel:false) } \
@@ -82,7 +68,7 @@ module Wires
82
68
 
83
69
  backtrace = caller
84
70
 
85
- event = Event.new_from(*input)
71
+ event = Event.list_from input
86
72
 
87
73
  case event.count
88
74
  when 0
@@ -93,14 +79,14 @@ module Wires
93
79
  raise ArgumentError,"Can't fire on multiple events: #{event.inspect}"
94
80
  end
95
81
 
96
- self.class.run_hooks(:@before_fire, event, self)
82
+ self.class.run_hooks(:@before_fire, event, self.name)
97
83
 
98
84
  # Select appropriate targets
99
85
  procs = []
100
86
  @@aim_lock.synchronize do
101
87
  self.class.router
102
88
  .get_receivers(self).each do |chan|
103
- chan.target_list.each do |elist, pr|
89
+ chan.handlers.each do |elist, pr|
104
90
  elist.each do |e|
105
91
  procs << pr if e =~ event
106
92
  end
@@ -121,14 +107,14 @@ module Wires
121
107
 
122
108
  threads.each &:join if blocking and parallel
123
109
 
124
- self.class.run_hooks(:@after_fire, event, self)
110
+ self.class.run_hooks(:@after_fire, event, self.name)
125
111
 
126
112
  threads
127
113
  end
128
114
 
129
115
  # Fire a blocking event on this channel
130
- def fire!(event)
131
- kwargs[:blocking] ||= true
116
+ def fire!(*args, **kwargs)
117
+ kwargs[:blocking] = true unless kwargs.has_key? :blocking
132
118
  fire(*args, **kwargs)
133
119
  end
134
120
 
@@ -26,7 +26,7 @@ module Wires
26
26
  nil end
27
27
 
28
28
  def fire!(*args, **kwargs)
29
- kwargs[:blocking] ||= true
29
+ kwargs[:blocking] = true unless kwargs.has_key? :blocking
30
30
  fire(*args, **kwargs)
31
31
  end
32
32
 
@@ -0,0 +1,71 @@
1
+
2
+ module Wires
3
+
4
+ class Event
5
+ attr_accessor :type
6
+ attr_accessor :kwargs
7
+ attr_accessor :args
8
+ attr_accessor :codeblock
9
+
10
+ # Return a friendly output upon inspection
11
+ def inspect
12
+ list = [*args, **kwargs]
13
+ list << codeblock.to_s if codeblock
14
+ list = list.map(&:inspect).join ', '
15
+ the_type = type ? type.inspect : ''
16
+ "#{the_type}:[#{list}]"
17
+ end
18
+
19
+ # Internalize all *args and **kwargs and &block to be accessed later
20
+ def initialize(*args, **kwargs, &block)
21
+ if kwargs.has_key? :type
22
+ @type = kwargs[:type]
23
+ kwargs.delete :type
24
+ else
25
+ @type = :*
26
+ end
27
+
28
+ @args = args
29
+ @kwargs = kwargs
30
+ @codeblock = block
31
+
32
+ @kwargs.keys
33
+ .reject{ |m| [:kwargs, :args, :codeblock].include? m }
34
+ .each { |m| singleton_class.send(:define_method, m) { @kwargs[m] } }
35
+ end
36
+
37
+ # Convert to a Wires::Event; returns self, unaltered
38
+ def to_wires_event; self; end
39
+
40
+ # Directly access contents of @kwargs by key
41
+ def [](key); @kwargs[key]; end
42
+
43
+ # Returns true if all meaningful components of two events are equal
44
+ # Use #equal? instead if you want object identity comparison
45
+ def ==(other)
46
+ (other = other.to_wires_event if other.respond_to? :to_wires_event) ?
47
+ ((self.type == other.type) and
48
+ (self.args == other.args) and
49
+ (self.kwargs == other.kwargs) and
50
+ (self.codeblock == other.codeblock)) :
51
+ super
52
+ end
53
+
54
+ # Returns true if listening for 'self' would hear a firing of 'other'
55
+ # (not commutative)
56
+ def =~(other)
57
+ (other = other.to_wires_event if other.respond_to? :to_wires_event) ?
58
+ (([:*, other.type].include? self.type) and
59
+ (not self.kwargs.each_pair.detect{|k,v| other.kwargs[k]!=v}) and
60
+ (not self.args.each_with_index.detect{|a,i| other.args[i]!=a})) :
61
+ super
62
+ end
63
+
64
+ # Return an array of Event instance objects from the input
65
+ def self.list_from(*args)
66
+ args.flatten.map &:to_wires_event
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -5,6 +5,9 @@ module Wires
5
5
  class self::Hub
6
6
  class << self
7
7
 
8
+ # Refuse to instantiate; it's a singleton!
9
+ private :new
10
+
8
11
  # Allow user to get/set limit to number of child threads
9
12
  attr_accessor :max_children
10
13
 
@@ -60,12 +63,9 @@ module Wires
60
63
  spawn_neglected_task_threads
61
64
  end
62
65
 
63
- # Spawn a task
66
+ # Spawn a task - user code should never call this directly
64
67
  def spawn(*args) # :args: event, chan, proc, blocking, fire_bt
65
68
 
66
- return neglect(*args) \
67
- if @hold_lock.instance_variable_get(:@mon_mutex).locked?
68
-
69
69
  event, chan, proc, blocking, parallel, fire_bt = *args
70
70
  *proc_args = event, chan
71
71
  *exc_args = event, chan, fire_bt
@@ -81,6 +81,9 @@ module Wires
81
81
  return nil
82
82
  end
83
83
 
84
+ return neglect(*args) \
85
+ if @hold_lock.instance_variable_get(:@mon_mutex).locked?
86
+
84
87
  # If not parallel, clear old threads and spawn a new thread
85
88
  Thread.exclusive do
86
89
  begin
@@ -2,9 +2,10 @@
2
2
  module Wires
3
3
  module Router
4
4
 
5
-
6
5
  class Default
7
6
  class << self
7
+ # Refuse to instantiate; it's a singleton!
8
+ private :new
8
9
 
9
10
  def clear_channels()
10
11
  @table = {}
@@ -42,6 +43,8 @@ module Wires
42
43
 
43
44
  class Simple
44
45
  class << self
46
+ # Refuse to instantiate; it's a singleton!
47
+ private :new
45
48
 
46
49
  def clear_channels()
47
50
  @table = {}
@@ -10,6 +10,9 @@ module Wires
10
10
 
11
11
  class << self
12
12
 
13
+ # Refuse to instantiate; it's a singleton!
14
+ private :new
15
+
13
16
  # Add an event to the schedule
14
17
  def add(*args)
15
18
  new_item = args.first
@@ -0,0 +1,92 @@
1
+
2
+ module Wires
3
+
4
+ class TimeSchedulerItem
5
+
6
+ attr_accessor :schedulers
7
+ attr_reader :time, :events, :channel,
8
+ :count, :interval, :jitter
9
+ attr_accessor :fire_kwargs
10
+
11
+ def initialize(time, events, channel,
12
+ count:1, interval:0, jitter:0,
13
+ ignore_past:false, active:true,
14
+ **fire_kwargs)
15
+
16
+ time ||= Time.now
17
+
18
+ @events = Event.list_from(events)
19
+ @channel = channel.is_a?(Channel) ? channel : Channel.new(channel)
20
+
21
+ @interval = interval
22
+ @jitter = jitter
23
+
24
+ @active = active
25
+ @fire_kwargs = fire_kwargs
26
+
27
+ tempcount = count
28
+ if ignore_past
29
+ while (time < Time.now) and (tempcount > 0)
30
+ time += interval
31
+ tempcount -= 1
32
+ end
33
+ end
34
+ self.count = tempcount
35
+
36
+ @time = time
37
+
38
+ @schedulers = []
39
+ end
40
+
41
+ def active?; @active end
42
+ def active=(x) @active=x end
43
+
44
+ def ready?(at_time=Time.now)
45
+ @active and (at_time>=@time)
46
+ end
47
+
48
+ def time_until(from_time=Time.now)
49
+ (@active ? [(@time - from_time), 0].max : nil)
50
+ end
51
+
52
+ # Set @count (and apply constraints)
53
+ def count=(new_count)
54
+ @count=[new_count,0].max
55
+ .tap { |c| @active&&=(c>0) }
56
+ end
57
+
58
+ # Inc/dec @count. Necessary because += and -= without lock are not atomic!
59
+ def count_inc(diff=1); self.count=(@count+diff) end
60
+ def count_dec(diff=1); self.count=(@count-diff) end
61
+
62
+ # Fire the event now, regardless of time or active status
63
+ def fire(**kwargs) # kwargs merge with and override @kwargs
64
+ @channel.fire(@events, **(@fire_kwargs.merge kwargs))
65
+ count_dec
66
+
67
+ if @active
68
+ @time = [@time, Time.now].max + (@interval + (Random.rand*2-1)*@jitter)
69
+ end
70
+ notify_schedulers
71
+ true end
72
+
73
+ # Fire the event only if it is ready
74
+ def fire_if_ready(**kwargs)
75
+ self.fire(**kwargs) if ready?
76
+ end
77
+
78
+ private
79
+
80
+ def notify_schedulers
81
+ @schedulers.each &:refresh
82
+ end
83
+
84
+ # Lock some of the methods to try to make them atomic
85
+ # Must exclude methods that get called from within the TimeScheduler lock
86
+ threadlock :fire,
87
+ :count=,
88
+ :count_inc,
89
+ :count_dec
90
+ end
91
+
92
+ end
@@ -8,7 +8,7 @@ module Wires
8
8
  # >> module MyModule; end
9
9
  # >> Wires::Util.build_alt "::MyModule::MyWires"
10
10
  def self.build_alt(module_path)
11
- main_file = File.expand_path("../../wires.rb", File.dirname(__FILE__))
11
+ main_file = File.expand_path("../../base.rb", File.dirname(__FILE__))
12
12
 
13
13
  File.read(main_file)
14
14
  .scan(/require_relative[\s\(]+(["'])(.*)\1/)
@@ -16,6 +16,8 @@ module Wires
16
16
  .map { |file| File.expand_path("#{file}.rb", File.dirname(main_file)) }
17
17
  .map { |file| File.read file }
18
18
  .each { |code| eval code.gsub("Wires", "#{module_path}") }
19
+
20
+ eval "#{module_path}"
19
21
  end
20
22
 
21
23
  end
File without changes
data/lib/wires/base.rb ADDED
@@ -0,0 +1,14 @@
1
+
2
+ require 'thread'
3
+ require 'threadlock'
4
+
5
+ require_relative 'base/util/hooks'
6
+ require_relative 'base/util/build_alt'
7
+
8
+ require_relative 'base/event'
9
+ require_relative 'base/hub'
10
+ require_relative 'base/router'
11
+ require_relative 'base/channel'
12
+ require_relative 'base/time_scheduler_item'
13
+ require_relative 'base/time_scheduler'
14
+ require_relative 'base/convenience'
@@ -0,0 +1,35 @@
1
+
2
+ # Add Numeric => Numeric time-factor converters
3
+ {
4
+ [:second, :seconds] => '1',
5
+ [:minute, :minutes] => '60',
6
+ [:hour, :hours] => '3600',
7
+ [:day, :days] => '24.hours',
8
+ [:week, :weeks] => '7.days',
9
+ [:fortnight, :fortnights] => '2.weeks',
10
+ }.each_pair do |k,v|
11
+ ::Numeric.class_eval <<-CODE
12
+ def #{k.last}
13
+ self * #{v}
14
+ end
15
+ alias #{k.first.inspect} #{k.last.inspect}
16
+ CODE
17
+ end
18
+
19
+
20
+ # Add Numeric => Time converters with implicit anonymous fire
21
+ {
22
+ [:from_now, :since] => '+',
23
+ [:until, :ago] => '-',
24
+ }.each_pair do |k,v|
25
+ ::Numeric.class_eval <<-CODE
26
+ def #{k.last}(time = ::Time.now, &block)
27
+ if block
28
+ Wires::Channel[block.object_id].register :time_scheduler_anon, &block
29
+ self.#{k.last}(time).fire(:time_scheduler_anon, block.object_id)
30
+ end
31
+ time #{v} self
32
+ end
33
+ alias #{k.first.inspect} #{k.last.inspect}
34
+ CODE
35
+ end
@@ -0,0 +1,11 @@
1
+
2
+ # Add implicit conversion of symbol into an event
3
+ class ::Symbol
4
+ # Create a Wires::Event from any symbol with a payload of arguments
5
+ def [](*args, **kwargs, &block)
6
+ Wires::Event.new(*args, **kwargs, type:self, &block)
7
+ end
8
+
9
+ # Convert to a Wires::Event; returns an empty event with type:self
10
+ def to_wires_event; self.[]; end
11
+ end
@@ -0,0 +1,7 @@
1
+
2
+ # Add Time#fire for timed firing of events
3
+ class ::Time
4
+ def fire(events, channel, **kwargs)
5
+ Wires::TimeScheduler.add(self, events, channel, **kwargs)
6
+ end
7
+ end
@@ -1,65 +1,4 @@
1
1
 
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
11
-
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
16
- end
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 <<-CODE
22
- def fire(event, channel='*', **kwargs)
23
- #{TimeScheduler}.add(self, event, channel, **kwargs)
24
- end
25
- CODE
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
60
- end
61
-
62
- end
63
-
64
- end
65
- end
2
+ require_relative 'core_ext/symbol'
3
+ require_relative 'core_ext/time'
4
+ require_relative 'core_ext/numeric'
data/lib/wires.rb CHANGED
@@ -1,15 +1,7 @@
1
1
 
2
- require 'thread'
3
- require 'threadlock'
2
+ # Require all core wires functionality through 'wires/clean'
3
+ # If you don't consent to the core extensions, require 'wires/clean' directly
4
+ require_relative 'wires/base'
4
5
 
5
- require_relative 'wires/util/hooks'
6
- require_relative 'wires/util/build_alt'
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_scheduler_item'
13
- require_relative 'wires/time_scheduler'
6
+ # Add core extensions for syntax sugar and ease
14
7
  require_relative 'wires/core_ext'
15
- require_relative 'wires/convenience'
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.4.3
4
+ version: 0.5.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-10-06 00:00:00.000000000 Z
11
+ date: 2013-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: threadlock
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: wires-test
42
+ name: rdoc
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -53,7 +53,63 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: jemc-reporter
56
+ name: pry
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'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-rescue
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
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
+ - !ruby/object:Gem::Dependency
98
+ name: fivemat
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: timecop
57
113
  requirement: !ruby/object:Gem::Requirement
58
114
  requirements:
59
115
  - - ">="
@@ -74,16 +130,20 @@ extensions: []
74
130
  extra_rdoc_files: []
75
131
  files:
76
132
  - lib/wires.rb
77
- - lib/wires/hub.rb
78
- - lib/wires/router.rb
79
133
  - lib/wires/core_ext.rb
80
- - lib/wires/util/build_alt.rb
81
- - lib/wires/util/hooks.rb
82
- - lib/wires/convenience.rb
83
- - lib/wires/channel.rb
84
- - lib/wires/time_scheduler_item.rb
85
- - lib/wires/time_scheduler.rb
86
- - lib/wires/event.rb
134
+ - lib/wires/core_ext/time.rb
135
+ - lib/wires/core_ext/symbol.rb
136
+ - lib/wires/core_ext/numeric.rb
137
+ - lib/wires/base/hub.rb
138
+ - lib/wires/base/router.rb
139
+ - lib/wires/base/util/build_alt.rb
140
+ - lib/wires/base/util/hooks.rb
141
+ - lib/wires/base/convenience.rb
142
+ - lib/wires/base/channel.rb
143
+ - lib/wires/base/time_scheduler_item.rb
144
+ - lib/wires/base/time_scheduler.rb
145
+ - lib/wires/base/event.rb
146
+ - lib/wires/base.rb
87
147
  - LICENSE
88
148
  - README.md
89
149
  homepage: https://github.com/jemc/wires/
@@ -111,3 +171,4 @@ signing_key:
111
171
  specification_version: 4
112
172
  summary: wires
113
173
  test_files: []
174
+ has_rdoc:
data/lib/wires/event.rb DELETED
@@ -1,102 +0,0 @@
1
-
2
- module Wires
3
-
4
- class Event
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})"
12
- end
13
-
14
- # Internalize all *args and **kwargs and &block to be accessed later
15
- def initialize(*args, **kwargs, &block)
16
- cls = self.class
17
- self.event_type = cls unless cls==Wires::Event
18
-
19
- @ignore = []
20
- @kwargs = kwargs.dup
21
-
22
- @kwargs.keys.each do |m|
23
- if respond_to? m
24
- (class << self; self; end).class_eval do
25
- undef_method m
26
- end
27
- end
28
- end
29
-
30
- (@kwargs[:args] = args.freeze; @ignore<<:args) \
31
- unless @kwargs.has_key? :args
32
- (@kwargs[:codeblock] = block; @ignore<<:codeblock) \
33
- unless @kwargs.has_key? :codeblock
34
- @kwargs.freeze
35
- end
36
-
37
- # Directly access contents of @kwargs by key
38
- def [](key); @kwargs[key]; end
39
-
40
- # Used to fake a sort of read-only openstruct from contents of @kwargs
41
- def method_missing(sym, *args, &block)
42
- args.empty? and @kwargs.has_key?(sym) ?
43
- @kwargs[sym] :
44
- (sym==:kwargs ? @kwargs.reject{|k| @ignore.include? k} : super)
45
- end
46
-
47
- # Returns true if listening for 'self' would hear a firing of 'other'
48
- # (not commutative)
49
- def =~(other)
50
- (other.is_a? Event) ?
51
- ((self.class >= other.class) \
52
- and (self.event_type.nil? or self.event_type==other.event_type \
53
- or (self.event_type.is_a? Class and other.event_type.is_a? Class \
54
- and self.event_type >= other.event_type)) \
55
- and (not self.kwargs.each_pair.detect{|k,v| other.kwargs[k]!=v}) \
56
- and (not self.args.each_with_index.detect{|a,i| other.args[i]!=a})) :
57
- super
58
- end
59
-
60
- # Return an array of Event instance objects generated from
61
- # specially formatted input (see spec/event_spec.rb).
62
- def self.new_from(*args)
63
- args.flatten!
64
- list = []
65
-
66
- args.each do |x|
67
- (x.is_a? Hash) ?
68
- (x.each_pair { |x,y| list << [x,y] }) :
69
- (list << [x,[]])
70
- end
71
-
72
- list.map! do |type, args|
73
- case type
74
- when Event; obj = type
75
- when Class;
76
- if type<=Event
77
- obj = type.new(*args)
78
- end
79
- when Symbol
80
- obj = self.new(*args)
81
- obj.event_type = type
82
- obj if self==Wires::Event
83
- end
84
- obj
85
- end.tap do |x|
86
- raise ArgumentError,
87
- "Invalid event creation input: #{args} \noutput: #{x}" \
88
- if x.empty? or !x.all?
89
- end
90
- end
91
-
92
- # Ensure that self.new_from is not inherited
93
- def self.inherited(subcls)
94
- super
95
- class << subcls
96
- undef_method :new_from
97
- end if self == Wires::Event
98
- end
99
-
100
- end
101
-
102
- end
@@ -1,81 +0,0 @@
1
-
2
- module Wires
3
-
4
- class TimeSchedulerItem
5
-
6
- attr_reader :time, :event, :channel, :interval
7
- attr_accessor :schedulers
8
-
9
- def initialize(time, event, channel='*',
10
- interval:0.seconds, count:1,
11
- ignore_past:false, cancel:false,
12
- **kwargs)
13
-
14
- time ||= Time.now
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
- @interval = interval
32
-
33
- @event = Event.new_from(event)
34
- @channel = channel.is_a?(Channel) ? channel : Channel.new(channel)
35
- @kwargs = kwargs
36
-
37
- @schedulers = []
38
- end
39
-
40
- def active?; @active end
41
- def inactive?; !@active end
42
-
43
- def ready?(at_time=Time.now); @active and (at_time>=@time) end
44
-
45
- def time_until; (@active ? [(@time - Time.now), 0].max : nil) end
46
-
47
- def cancel; self.count=0 ;nil end
48
-
49
- # Get/set @count (and apply constraints on set)
50
- def count; @count end
51
- #TODO: handle explicit cancel?
52
- def count=(x); @count=[x,0].max; @active&&=(count>0) ;nil end
53
-
54
- # Inc/dec @count. Necessary because += and -= outside of lock are not atomic!
55
- def count_inc(x=1); self.count=(@count+x) end
56
- def count_dec(x=1); self.count=(@count-x) end
57
-
58
- # Fire the event now, regardless of time or active status
59
- def fire(**kwargs) # kwargs merge with and override @kwargs
60
- @channel.fire(@event, **(@kwargs.merge(kwargs)))
61
- count_dec
62
- @time += @interval if @active
63
- notify_schedulers
64
- nil end
65
-
66
- # Fire the event only if it is ready
67
- def fire_if_ready(**args); self.fire(**kwargs) if ready? end
68
-
69
- private
70
-
71
- def notify_schedulers; @schedulers.each &:refresh end
72
-
73
- # Lock some of the methods to try to make them atomic
74
- # Must exclude methods that get called from within the TimeScheduler lock
75
- threadlock :fire,
76
- :count=,
77
- :count_inc,
78
- :count_dec
79
- end
80
-
81
- end