data/CHANGELOG ADDED
@@ -0,0 +1,29 @@
1
+ = CHANGELOG
2
+
3
+ == 0.2.1
4
+ * Use simple monitor
5
+ * Add support for rounding sleep time within a delta
6
+ * Raise unexpected exception to creator thread after shutting down
7
+ * Fix arity checks for < 1.9 versions
8
+
9
+ == 0.2.0
10
+ * hashed arguments for Timer#add and Action#new
11
+ * Timer#mass_add replaced with Timer#register for single and arrays of Actions
12
+ * Timer#registered? added to see if an Action is registered with the timer
13
+ * Timer#actions to see Actions registered with the timer
14
+ * Action#timer= to allow Actions to be moved
15
+
16
+ == 0.1.1
17
+ * fix for float calculations
18
+ * downcased gem name
19
+
20
+ == 0.1.0
21
+ * remove excess exceptions
22
+ * use logger directly
23
+ * add a splat for data passage
24
+
25
+ == 0.0.2
26
+ * added new Timer::running? method
27
+ * added new Timer::pause method
28
+ * fixed single iteration action bug
29
+ * added unit tests for proper checks
data/LICENSE ADDED
@@ -0,0 +1,165 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
data/README.rdoc ADDED
@@ -0,0 +1,120 @@
1
+ == ActionTimer: Simple timing for a complex world
2
+
3
+ ActionTimer is a helper for timed events. It allows for single and recurring actions to be executed in an efficient manner. It makes use of a single thread to keep time on registered actions and uses an ActionPool to execute actions. Simple and effective.
4
+
5
+
6
+ === install (easy):
7
+
8
+ gem install actiontimer
9
+
10
+ === install (less easy):
11
+
12
+ git clone http://github.com/spox/actiontimer.git
13
+ cd actiontimer && gem build *.gemspec && gem install ./
14
+
15
+ === install (less easy that's a little easier)
16
+
17
+ {rip}[http://hellorip.com/about.html] makes it easy to install directly from a github repository.
18
+
19
+ === Testing
20
+
21
+ ActionTimer is currently tested on:
22
+
23
+ * Ruby 1.8.6-p383
24
+ * Ruby 1.8.7-p248
25
+ * Ruby 1.9.1-p376
26
+ * JRuby 1.4.0
27
+
28
+ === Using the timer:
29
+
30
+ ==== Simple example:
31
+
32
+ require 'actiontimer'
33
+ timer = ActionTimer::Timer.new
34
+ timer.add(1){ puts "#{Time.now}: This is timed every 1 second." }
35
+ timer.add(2){ puts "#{Time.now}: This is timed every 2 seconds." }
36
+ loop do
37
+ puts "#{Time.now}: Main loop sleeps for 3 seconds."
38
+ sleep(3)
39
+ end
40
+
41
+ =>
42
+ 2010-01-05 17:52:46 -0800: Main loop sleeps for 3 seconds.
43
+ 2010-01-05 17:52:47 -0800: This is timed every 1 second.
44
+ 2010-01-05 17:52:48 -0800: This is timed every 1 second.
45
+ 2010-01-05 17:52:48 -0800: This is timed every 2 seconds.
46
+ 2010-01-05 17:52:49 -0800: Main loop sleeps for 3 seconds.
47
+ 2010-01-05 17:52:49 -0800: This is timed every 1 second.
48
+ 2010-01-05 17:52:50 -0800: This is timed every 1 second.
49
+ 2010-01-05 17:52:50 -0800: This is timed every 2 seconds.
50
+ 2010-01-05 17:52:51 -0800: This is timed every 1 second.
51
+ 2010-01-05 17:52:52 -0800: Main loop sleeps for 3 seconds.
52
+
53
+ ==== Other examples:
54
+
55
+ What if you want to sleep for less than a second? Well, sure we can do that:
56
+
57
+ require 'actiontimer'
58
+ result = 0
59
+ timer = ActionTimer::Timer.new
60
+ timer.add(0.1){ result += 1 }
61
+ sleep(1.01)
62
+ p result
63
+
64
+ => 10
65
+
66
+ How about passing data to your block:
67
+
68
+ require 'actiontimer'
69
+ data = :foobar
70
+ timer = ActionTimer::Timer.new
71
+ timer.add(0.01, false, data){|x| puts "Data: #{x}" }
72
+ data = :fubar
73
+ p data
74
+ sleep(0.011)
75
+ p data
76
+
77
+ =>
78
+ :fubar
79
+ Data: foobar
80
+ :fubar
81
+
82
+ Or maybe you don't want the timer to start right away:
83
+
84
+ require 'actiontimer'
85
+ timer = ActionTimer::Timer.new(:auto_start => false)
86
+ output = 0
87
+ timer.add(0.1){ output += 1 }
88
+ sleep(1)
89
+ p output
90
+ timer.start
91
+ sleep(1.01)
92
+ p output
93
+
94
+ =>
95
+ 0
96
+ 10
97
+
98
+ What if you want to add multiple actions at one time? We can do this:
99
+
100
+ require 'actiontimer'
101
+ timer = ActionTimer::Timer.new
102
+ result = 0
103
+ actions = []
104
+ actions << ActionTimer::Action.new(timer, 0.1){ result += 1}
105
+ actions << ActionTimer::Action.new(timer, 0.2){ result += 1}
106
+ actions << ActionTimer::Action.new(timer, 0.3){ result += 1}
107
+ timer.register(actions)
108
+ sleep(0.41)
109
+ p result
110
+
111
+ => 7
112
+
113
+ == Last remarks
114
+
115
+ If you find any bugs, please report them through {github}[http://github.com/spox/actiontimer/issues]. If you are in need of any help, you can generally find me on DALnet and Freenode.
116
+
117
+ == License
118
+
119
+ ActionPool is licensed under the LGPLv3
120
+ Copyright (c) 2009 spox <spox@modspox.com>
data/actiontimer.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ spec = Gem::Specification.new do |s|
2
+ s.name = 'actiontimer'
3
+ s.author = 'spox'
4
+ s.email = 'spox@modspox.com'
5
+ s.version = '0.2.1'
6
+ s.summary = 'Simple timer for a complex world'
7
+ s.platform = Gem::Platform::RUBY
8
+ s.has_rdoc = true
9
+ s.rdoc_options = %w(--title ActionTimer --main README.rdoc --line-numbers --inline-source)
10
+ s.extra_rdoc_files = %w(README.rdoc LICENSE CHANGELOG)
11
+ s.files = Dir['**/*']
12
+ s.require_paths = %w(lib)
13
+ s.add_dependency 'actionpool', '~> 0.2.3'
14
+ s.add_dependency 'splib', '~> 1.4'
15
+ s.required_ruby_version = '>= 1.8.6'
16
+ s.homepage = 'http://github.com/spox/actiontimer'
17
+ s.description = 'ActionTimer is a simple timer for recurring actions. It supports single and recurring actions with an easy to use API.'
18
+ end
data/lib/actiontimer.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'actiontimer/Timer.rb'
3
+ require 'splib'
4
+ Splib.load :Array, :Float, :Monitor
data/lib/actiontimer/Action.rb ADDED
@@ -0,0 +1,91 @@
1
+ module ActionTimer
2
+ class Action
3
+
4
+ attr_accessor :owner
5
+ attr_accessor :timer
6
+
7
+ # timer:: Timer this action resides within
8
+ # period:: amount of time between runs
9
+ # once:: only run this action once
10
+ # data:: data to pass to block
11
+ # block:: block to be executed
12
+ def initialize(hash, &block)
13
+ raise ArgumentError.new('Period must be supplied') unless hash[:period]
14
+ raise ArgumentError.new('Block must be provided') unless block_given?
15
+ raise ArgumentError.new('Block must accept data value') if hash[:data] && block.arity == 0
16
+ if((block.arity > 0 || block.arity < -1) && (!hash.has_key?(:data) || hash[:data].nil?))
17
+ raise ArgumentError.new('Data must be supplied for block')
18
+ end
19
+ args = {:once => false, :data => nil, :owner => nil}.merge(hash)
20
+ @period = args[:period].to_f
21
+ @block = block
22
+ @data = args[:data]
23
+ @once = args[:once]
24
+ @timer = args[:timer]
25
+ @completed = false
26
+ @wait_remaining = @period
27
+ @owner = args[:owner]
28
+ end
29
+
30
+ # t:: ActionTimer::Timer
31
+ # Set timer for action to be associated with
32
+ def timer=(t)
33
+ raise ArgumentError.new('Expecting an ActionTimer::Timer') unless t.is_a?(ActionTimer::Timer)
34
+ @timer = t
35
+ end
36
+
37
+ # o:: Object that added this action
38
+ # Adds an owner for this action. Useful
39
+ # for clearing all actions for a given
40
+ # object from the timer
41
+ def owner=(o)
42
+ @owner = o
43
+ end
44
+
45
+ # amount:: amount of time that has passed
46
+ # Decrement remaining wait time by given amount
47
+ def tick(amount)
48
+ amount = amount.to_f
49
+ amount = 0 if amount < 0
50
+ @wait_remaining = @wait_remaining - amount if @wait_remaining > 0
51
+ @wait_remaining = 0 if @wait_remaining < 0
52
+ @completed = true if @once && @wait_remaining <= 0
53
+ end
54
+
55
+ # Time remaning before Action is due
56
+ def remaining
57
+ @wait_remaining <= 0 ? 0 : @wait_remaining
58
+ end
59
+
60
+ # new_time:: new period
61
+ # Resets the wait period between runs
62
+ def reset_period(new_time)
63
+ @period = new_time.to_f
64
+ @wait_remaining = @period
65
+ @completed = false
66
+ @timer.wakeup unless @timer.nil?
67
+ end
68
+
69
+ # Action is ready to be destroyed
70
+ def is_complete?
71
+ @completed
72
+ end
73
+
74
+ # Used for scheduling with Timer. Resets the interval
75
+ # and returns itself
76
+ def schedule
77
+ @wait_remaining = @period
78
+ return self
79
+ end
80
+
81
+ # Is action due for execution
82
+ def due?
83
+ @wait_remaining <= 0
84
+ end
85
+
86
+ # Run the action
87
+ def run
88
+ @data.nil? ? @block.call : @block.call(*@data)
89
+ end
90
+ end
91
+ end
data/lib/actiontimer/Exceptions.rb ADDED
@@ -0,0 +1,8 @@
1
+ module ActionTimer
2
+
3
+ class AlreadyRunning < Exception
4
+ end
5
+
6
+ class NotRunning < Exception
7
+ end
8
+ end
data/lib/actiontimer/Timer.rb ADDED
@@ -0,0 +1,203 @@
1
+ require 'actionpool'
2
+ ['Action', 'Exceptions'].each{|f| require "actiontimer/#{f}"}
3
+
4
+ module ActionTimer
5
+ class Timer
6
+ # pool:: ActionPool for processing actions
7
+ # Creates a new timer
8
+ # Argument hash: {:pool, :logger, :auto_start}
9
+ def initialize(args={}, extra=nil)
10
+ auto_start = true
11
+ @delta = nil
12
+ if(args.is_a?(Hash))
13
+ @pool = args[:pool] ? args[:pool] : ActionPool::Pool.new
14
+ @logger = args[:logger] && args[:logger].is_a?(Logger) ? args[:logger] : Logger.new(nil)
15
+ auto_start = args.has_key?(:auto_start) ? args[:auto_start] : true
16
+ @delta = args[:delta] ? args[:delta].to_f : nil
17
+ else
18
+ @pool = args.is_a?(ActionPool::Pool) ? args : ActionPool::Pool.new
19
+ @logger = extra && extra.is_a?(Logger) ? extra : Logger.new(nil)
20
+ end
21
+ @actions = []
22
+ @new_actions = []
23
+ @timer_thread = nil
24
+ @stop_timer = false
25
+ @add_lock = Splib::Monitor.new
26
+ @awake_lock = Splib::Monitor.new
27
+ @sleeper = Splib::Monitor.new
28
+ @respond_to = Thread.current
29
+ start if auto_start
30
+ end
31
+
32
+ # Forcibly wakes the timer early
33
+ def wakeup
34
+ raise NotRunning.new unless running?
35
+ if(@sleeper.waiters > 0)
36
+ @sleeper.signal
37
+ else
38
+ @timer_thread.wakeup if @timer_thread.alive? && @timer_thread.stop?
39
+ end
40
+ end
41
+
42
+ # period:: amount of time between runs
43
+ # once:: only run this action once
44
+ # data:: data to pass to block
45
+ # owner:: owner of Action
46
+ # func:: block to be executed
47
+ # Add a new action to block
48
+ def add(hash, &func)
49
+ raise ArgumentError.new('Expecting hash of arguments') unless hash.is_a?(Hash)
50
+ raise ArgumentError.new('A period must be provided for timed action') unless hash[:period]
51
+ raise ArgumentError.new('Block must be provided') unless block_given?
52
+ raise ArgumentError.new('Block must accept data value') if hash[:data] && func.arity == 0
53
+ args = {:once => false, :data => nil, :owner => nil}.merge(hash)
54
+ action = Action.new(args.merge(:timer => self), &func)
55
+ @add_lock.synchronize{ @new_actions << action }
56
+ wakeup if running?
57
+ action
58
+ end
59
+
60
+ # actions:: Array of actions or single ActionTimer::Action
61
+ # Add single or multiple Actions to the timer at once
62
+ def register(action)
63
+ if(action.is_a?(Array))
64
+ if(action.find{|x|x.is_a?(Action)}.nil?)
65
+ raise ArgumentError.new('Array contains non ActionTimer::Action objects')
66
+ end
67
+ else
68
+ raise ArgumentError.new('Expecting an ActionTimer::Action object') unless action.is_a?(Action)
69
+ action = [action]
70
+ end
71
+ @add_lock.synchronize{ @new_actions = @new_actions + action }
72
+ wakeup if running?
73
+ end
74
+
75
+ # action:: Action to remove from timer
76
+ # Remove given action from timer
77
+ def remove(action)
78
+ raise ArgumentError.new('Expecting an action') unless action.is_a?(Action)
79
+ @actions.delete(action)
80
+ wakeup if running?
81
+ end
82
+
83
+ # Start the timer
84
+ def start
85
+ raise AlreadyRunning.new unless @timer_thread.nil?
86
+ @stop_timer = false
87
+ @timer_thread = Thread.new do
88
+ begin
89
+ until @stop_timer do
90
+ to_sleep = get_min_sleep
91
+ if((to_sleep.nil? || to_sleep > 0) && @new_actions.empty?)
92
+ @awake_lock.unlock if @awake_lock.locked?
93
+ start = Time.now.to_f
94
+ to_sleep.nil? ? @sleeper.wait : sleep(to_sleep)
95
+ actual_sleep = Time.now.to_f - start
96
+ if(@delta && to_sleep && actual_sleep.within_delta?(:expected => to_sleep, :delta => @delta))
97
+ actual_sleep = to_sleep
98
+ end
99
+ @awake_lock.lock
100
+ else
101
+ actual_sleep = 0
102
+ end
103
+ tick(actual_sleep)
104
+ add_waiting_actions
105
+ end
106
+ rescue Object => boom
107
+ @timer_thread = nil
108
+ clean_actions
109
+ @logger.fatal("Timer encountered an unexpected error and is shutting down: #{boom}\n#{boom.backtrace.join("\n")}")
110
+ @respond_to.raise boom
111
+ end
112
+ end
113
+ end
114
+
115
+ # Pause the timer in its current state.
116
+ def pause
117
+ @stop_timer = true
118
+ if(running?)
119
+ wakeup
120
+ @timer_thread.join
121
+ end
122
+ @timer_thread = nil
123
+ end
124
+
125
+ # Stop the timer. Unlike pause, this will completely
126
+ # stop the timer and remove all actions from the timer
127
+ def stop
128
+ @stop_timer = true
129
+ if(running?)
130
+ wakeup
131
+ clean_actions
132
+ @timer_thread.join
133
+ end
134
+ @timer_thread = nil
135
+ end
136
+
137
+ # owner:: owner actions to remove
138
+ # Clears timer of actions. If an owner is supplied
139
+ # only actions owned by owner will be removed
140
+ def clear(owner=nil)
141
+ if(owner.nil?)
142
+ @actions.clear
143
+ @new_actions.clear
144
+ else
145
+ @actions.each{|a| @actions.delete(a) if a.owner == owner}
146
+ end
147
+ wakeup if running?
148
+ end
149
+
150
+ # Is timer currently running?
151
+ def running?
152
+ !@timer_thread.nil?
153
+ end
154
+
155
+ # action:: ActionTimer::Action
156
+ # Is action currently in timer
157
+ def registered?(action)
158
+ @actions.include?(action)
159
+ end
160
+
161
+ # Actions registered with the timer
162
+ def actions
163
+ @actions.dup
164
+ end
165
+
166
+ private
167
+
168
+ def get_min_sleep
169
+ min = @actions.min{|a,b|a.remaining <=> b.remaining}
170
+ min.remaining if min
171
+ end
172
+
173
+ def add_waiting_actions
174
+ @add_lock.synchronize do
175
+ @actions = @actions + @new_actions
176
+ @new_actions.clear
177
+ end
178
+ end
179
+
180
+ def tick(time_passed)
181
+ @actions.each do |action|
182
+ action.tick(time_passed)
183
+ if(action.due?)
184
+ @actions.delete(action) if action.is_complete?
185
+ action = action.schedule
186
+ @pool.process do
187
+ begin
188
+ action.run
189
+ rescue StandardError => boom
190
+ @logger.error("Timer caught an error while running action: #{boom}\n#{boom.backtrace.join("\n")}")
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ def clean_actions
198
+ @actions.clear
199
+ @new_actions.clear
200
+ end
201
+
202
+ end
203
+ end
data/tests/cases/action.rb ADDED
@@ -0,0 +1,128 @@
1
+ require 'test/unit'
2
+ require 'actiontimer'
3
+
4
+ class ActionTests < Test::Unit::TestCase
5
+ def setup
6
+ @timer = ActionTimer::Timer.new
7
+ end
8
+ def teardown
9
+ end
10
+
11
+ def test_create
12
+ assert_raise(ArgumentError) do
13
+ action = ActionTimer::Action.new(:timer => @timer){true}
14
+ end
15
+ assert_raise(ArgumentError) do
16
+ action = ActionTimer::Action.new(:timer => @timer, :period => 1){|x|true}
17
+ end
18
+ assert_raise(ArgumentError) do
19
+ action = ActionTimer::Action.new(:timer => @timer, :period => 1, :data => 1)
20
+ end
21
+ if(RUBY_VERSION > "1.9.0")
22
+ assert_raise(ArgumentError) do
23
+ action = ActionTimer::Action.new(:timer => @timer, :period => 1, :data => 1){true}
24
+ end
25
+ end
26
+ assert_kind_of(ActionTimer::Action, ActionTimer::Action.new(:timer => @timer, :period => 1){true})
27
+ assert_kind_of(ActionTimer::Action, ActionTimer::Action.new(:timer => @timer, :period => 1, :once => true){true})
28
+ assert_kind_of(ActionTimer::Action, ActionTimer::Action.new(:timer => @timer, :period => 1, :once => false){true})
29
+ end
30
+
31
+ def test_timer
32
+ mytimer = ActionTimer::Timer.new
33
+ action = ActionTimer::Action.new(:timer => @timer, :period => 1){true}
34
+ assert_equal(@timer, action.timer)
35
+ action.timer = mytimer
36
+ assert_equal(mytimer, action.timer)
37
+ end
38
+
39
+ def test_owner
40
+ object = Object.new
41
+ action = ActionTimer::Action.new(:timer => @timer, :period => 1, :owner => object){true}
42
+ assert_equal(object, action.owner)
43
+ other_object = Object.new
44
+ action.owner = other_object
45
+ assert_equal(other_object, action.owner)
46
+ action.owner = object
47
+ assert_equal(object, action.owner)
48
+ end
49
+
50
+ def test_tick
51
+ action = ActionTimer::Action.new(:timer => @timer, :period => 2){true}
52
+ action.tick(1)
53
+ assert_equal(1, action.remaining)
54
+ action.tick(0.1)
55
+ assert_equal(0.9, action.remaining)
56
+ action.tick(0.11)
57
+ assert_equal(0.79, action.remaining)
58
+ end
59
+
60
+ def test_reset_period
61
+ action = ActionTimer::Action.new(:timer => @timer, :period => 2){true}
62
+ action.tick(1)
63
+ assert_equal(1, action.remaining)
64
+ action.reset_period(3)
65
+ assert_equal(3, action.remaining)
66
+ action.tick(3)
67
+ assert_equal(0, action.remaining)
68
+ assert(!action.is_complete?)
69
+ end
70
+
71
+ def test_complete
72
+ action = ActionTimer::Action.new(:timer => @timer, :period => 2, :once => true){true}
73
+ action.tick(2)
74
+ assert_equal(0, action.remaining)
75
+ assert(action.is_complete?)
76
+ end
77
+
78
+ def test_schedule
79
+ action = ActionTimer::Action.new(:timer => @timer, :period => 2){true}
80
+ action.tick(1)
81
+ assert_equal(1, action.remaining)
82
+ assert_equal(action, action.schedule)
83
+ assert_equal(2, action.remaining)
84
+ end
85
+
86
+ def test_due
87
+ action = ActionTimer::Action.new(:timer => @timer, :period => 2){true}
88
+ assert(!action.due?)
89
+ action.tick(2)
90
+ assert(action.due?)
91
+ end
92
+
93
+ def test_run_noargs
94
+ a = false
95
+ action = ActionTimer::Action.new(:timer => @timer, :period => 2){ a = true }
96
+ assert(!a)
97
+ action.run
98
+ assert(a)
99
+ end
100
+
101
+ def test_run_args
102
+ a = []
103
+ action = ActionTimer::Action.new(:timer => @timer, :period => 2, :data => [1,2,[3]]) do |b,c,d|
104
+ a << b
105
+ a << c
106
+ a << d
107
+ end
108
+ action.run
109
+ assert_kind_of(Array, a.pop)
110
+ assert_equal(2, a.pop)
111
+ assert_equal(1, a.pop)
112
+ assert(a.empty?)
113
+ action = ActionTimer::Action.new(:timer => @timer, :period => 2, :data => [1,2,[3]]) do |*b|
114
+ a = b
115
+ end
116
+ action.run
117
+ assert_kind_of(Array, a)
118
+ assert_kind_of(Array, a.pop)
119
+ assert_equal(2, a.pop)
120
+ assert_equal(1, a.pop)
121
+ assert(a.empty?)
122
+ action = ActionTimer::Action.new(:timer => @timer, :period => 2, :data => :foo) do |b|
123
+ a = b
124
+ end
125
+ action.run
126
+ assert_equal(:foo, a)
127
+ end
128
+ end
data/tests/cases/timer.rb ADDED
@@ -0,0 +1,85 @@
1
+ require 'test/unit'
2
+ require 'actiontimer'
3
+
4
+ class TimerTests < Test::Unit::TestCase
5
+ def setup
6
+ @timer = ActionTimer::Timer.new
7
+ end
8
+
9
+ def teardown
10
+ end
11
+
12
+ def test_add_bad
13
+ assert_raise(ArgumentError) do
14
+ @timer.add{true}
15
+ end
16
+ assert_raise(ArgumentError) do
17
+ @timer.add(1){true}
18
+ end
19
+ assert_raise(ArgumentError) do
20
+ @timer.add(:period => 1)
21
+ end
22
+ if(RUBY_VERSION > "1.9.0")
23
+ assert_raise(ArgumentError) do
24
+ @timer.add(:period => 1, :data => 2){true}
25
+ end
26
+ end
27
+ end
28
+
29
+ def test_add
30
+ output = []
31
+ action = @timer.add(:period => 0.1){true}
32
+ assert_kind_of(ActionTimer::Action, action)
33
+ sleep(0.01)
34
+ assert_equal(1, @timer.actions.size)
35
+ assert(@timer.registered?(action))
36
+ @timer.add(:period => 0.1, :once => true, :data => :foo){|x| output << x }
37
+ sleep(0.01)
38
+ assert_equal(2, @timer.actions.size)
39
+ sleep(0.14)
40
+ assert_equal(:foo, output.pop)
41
+ assert_equal(1, @timer.actions.size)
42
+ end
43
+
44
+ def test_register
45
+ output = []
46
+ action = ActionTimer::Action.new(:period => 0.01){output << :action}
47
+ @timer.register(action)
48
+ sleep(0.113)
49
+ @timer.pause
50
+ assert_equal(10, output.size)
51
+ assert_equal(1, @timer.actions.size)
52
+ assert(@timer.registered?(action))
53
+ @timer.clear
54
+ assert(@timer.actions.empty?)
55
+ @timer.start
56
+ output.clear
57
+ actions = [action]
58
+ actions << ActionTimer::Action.new(:period => 0.02){output << :fubar}
59
+ @timer.register(actions)
60
+ sleep(0.051)
61
+ @timer.pause
62
+ assert_equal(7, output.size)
63
+ assert(output.include?(:fubar))
64
+ assert_equal(2, @timer.actions.size)
65
+ actions.each{|x| assert(@timer.registered?(x)) }
66
+ end
67
+
68
+ def test_remove
69
+ output = []
70
+ action = ActionTimer::Action.new(:period => 0.01){output << :action}
71
+ @timer.register(action)
72
+ sleep(0.029)
73
+ @timer.remove(action)
74
+ assert_equal(2, output.size)
75
+ assert(@timer.actions.empty?)
76
+ output.clear
77
+ assert(output.empty?)
78
+ action = @timer.add(:period => 0.01){output << :action}
79
+ sleep(0.021)
80
+ @timer.remove(action)
81
+ assert(!@timer.registered?(action))
82
+ assert(2, output.size)
83
+ end
84
+
85
+ end
data/tests/run_tests.rb ADDED
@@ -0,0 +1,150 @@
1
+ $LOAD_PATH.unshift(File.expand_path("#{__FILE__}/../../lib"))
2
+
3
+ require 'test/unit'
4
+ require 'actiontimer'
5
+
6
+ Dir.new("#{File.dirname(__FILE__)}/cases").each{|f|
7
+ require "#{File.dirname(__FILE__)}/cases/#{f}" if f[-2..f.size] == 'rb'
8
+ }
9
+ #
10
+ #
11
+ # class TimerTests < Test::Unit::TestCase
12
+ # def setup
13
+ # @timer = ActionTimer::Timer.new
14
+ # end
15
+ #
16
+ # # Simple test of basic repetitive action
17
+ # def test_basic
18
+ # result = 0
19
+ # @timer.add(2){ result += 1 }
20
+ # sleep(5)
21
+ # assert_equal(2, result)
22
+ # end
23
+ #
24
+ # # Check the the running? method properly reports
25
+ # def test_running
26
+ # @timer.add(1){ 1 + 1}
27
+ # assert(@timer.running?)
28
+ # @timer.pause
29
+ # assert(!@timer.running?)
30
+ # @timer.start
31
+ # assert(@timer.running?)
32
+ # @timer.stop
33
+ # assert(!@timer.running?)
34
+ # end
35
+ #
36
+ # # Check that a value 0 < t < 1 works
37
+ # # as expected
38
+ # def test_float
39
+ # result = 0
40
+ # @timer.add(0.1){ result += 1 }
41
+ # sleep(1.01)
42
+ # assert_equal(10, result)
43
+ # end
44
+ #
45
+ # # Check that a single iterative action is only
46
+ # # completed once
47
+ # def test_once
48
+ # result = 0
49
+ # @timer.add(1, true){ result += 1 }
50
+ # sleep(3)
51
+ # assert_equal(1, result)
52
+ # end
53
+ #
54
+ # # Check that timer can be paused and restarted
55
+ # # without registered actions being effected
56
+ # def test_pause
57
+ # result = 0
58
+ # @timer.add(1){ result += 1 }
59
+ # sleep(3.1)
60
+ # @timer.pause
61
+ # sleep(2)
62
+ # @timer.start
63
+ # sleep(2)
64
+ # assert_equal(5, result)
65
+ # end
66
+ #
67
+ # # Check that data can be passed to the block
68
+ # # properly when created
69
+ # def test_data
70
+ # result = 0
71
+ # @timer.add(1, true, 3){|a| result = a}
72
+ # sleep(2)
73
+ # assert_equal(3, result)
74
+ # @timer.add(1, true, [3,4,['foobar']]){|a,b,c| result = [b,a,c]}
75
+ # sleep(2)
76
+ # assert_equal(4, result[0])
77
+ # assert_equal(3, result[1])
78
+ # assert(result[2].is_a?(Array))
79
+ # end
80
+ #
81
+ # # Check that the timer's auto starting mechanism
82
+ # # can be disabled
83
+ # def test_auto_start
84
+ # timer = ActionTimer::Timer.new(:auto_start => false)
85
+ # timer.add(1){ 1+1 }
86
+ # assert(!timer.running?)
87
+ # timer.start
88
+ # assert(timer.running?)
89
+ # end
90
+ #
91
+ # # Check that the actions can be cleared out of the
92
+ # # timer and the timer is still left in a "running"
93
+ # # state.
94
+ # def test_clear
95
+ # result = 0
96
+ # @timer.add(1){ result += 1 }
97
+ # sleep(3)
98
+ # @timer.clear
99
+ # sleep(2)
100
+ # assert_equal(2, result)
101
+ # assert(@timer.running?)
102
+ # end
103
+ #
104
+ # # Check that the timer throws an exception when it
105
+ # # is instructed to wakeup while not running
106
+ # def test_wakeup
107
+ # @timer.stop
108
+ # assert_raise(ActionTimer::NotRunning){ @timer.wakeup }
109
+ # end
110
+ #
111
+ # # Check that the timer throws an exception when it
112
+ # # is instructed to start but is already running
113
+ # def test_start
114
+ # assert_raise(ActionTimer::AlreadyRunning){ @timer.start }
115
+ # end
116
+ #
117
+ # # Check that multiple actions can be added at once
118
+ # def test_mass_add
119
+ # result = 0
120
+ # actions = []
121
+ # actions << ActionTimer::Action.new(@timer, 1){ result += 1}
122
+ # actions << ActionTimer::Action.new(@timer, 3){ result += 1}
123
+ # actions << ActionTimer::Action.new(@timer, 5){ result += 1}
124
+ # @timer.mass_add(actions)
125
+ # sleep(5.3)
126
+ # assert_equal(7, result)
127
+ # end
128
+ #
129
+ # # Check that an action can be properly removed from
130
+ # # the timer
131
+ # def test_remove
132
+ # result = 0
133
+ # action = @timer.add(1){result += 1}
134
+ # sleep(2.1)
135
+ # @timer.remove(action)
136
+ # sleep(2)
137
+ # assert_equal(2, result)
138
+ # end
139
+ #
140
+ # # Check that an action's period can be dynamically
141
+ # # reset
142
+ # def test_action_reset
143
+ # result = 0
144
+ # action = @timer.add(1){ result += 1}
145
+ # sleep(2.1)
146
+ # action.reset_period(3)
147
+ # sleep(3.1)
148
+ # assert_equal(result, 3)
149
+ # end
150
+ # end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: actiontimer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - spox
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-13 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: actionpool
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 0.2.3
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: splib
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: "1.4"
34
+ version:
35
+ description: ActionTimer is a simple timer for recurring actions. It supports single and recurring actions with an easy to use API.
36
+ email: spox@modspox.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README.rdoc
43
+ - LICENSE
44
+ - CHANGELOG
45
+ files:
46
+ - actiontimer.gemspec
47
+ - tests/cases/timer.rb
48
+ - tests/cases/action.rb
49
+ - tests/run_tests.rb
50
+ - lib/actiontimer.rb
51
+ - lib/actiontimer/Timer.rb
52
+ - lib/actiontimer/Action.rb
53
+ - lib/actiontimer/Exceptions.rb
54
+ - CHANGELOG
55
+ - LICENSE
56
+ - README.rdoc
57
+ has_rdoc: true
58
+ homepage: http://github.com/spox/actiontimer
59
+ licenses: []
60
+
61
+ post_install_message:
62
+ rdoc_options:
63
+ - --title
64
+ - ActionTimer
65
+ - --main
66
+ - README.rdoc
67
+ - --line-numbers
68
+ - --inline-source
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 1.8.6
76
+ version:
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ requirements: []
84
+
85
+ rubyforge_project:
86
+ rubygems_version: 1.3.5
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: Simple timer for a complex world
90
+ test_files: []
91
+