run_once 1.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/CHANGELOG +1 -0
- data/Manifest +6 -0
- data/README +47 -0
- data/Rakefile +7 -0
- data/lib/run_once.rb +82 -0
- data/run_once.gemspec +31 -0
- data/test/test_all.rb +67 -0
- metadata +77 -0
data/CHANGELOG
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
v1.0. First version
|
data/Manifest
ADDED
data/README
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
run_once
|
3
|
+
---------
|
4
|
+
|
5
|
+
This gem lets a code block run only once every N seconds.
|
6
|
+
|
7
|
+
For example:
|
8
|
+
|
9
|
+
> loop { RunOnce.in(3) { puts Time.now } }
|
10
|
+
2011-07-31 10:29:02 +1000
|
11
|
+
2011-07-31 10:29:05 +1000
|
12
|
+
2011-07-31 10:29:08 +1000
|
13
|
+
...
|
14
|
+
|
15
|
+
|
16
|
+
Usage
|
17
|
+
-----
|
18
|
+
|
19
|
+
First, gem install 'run_once'. RunOnce uses a text file to keep track of
|
20
|
+
the state and puts it in /tmp by default. The file is locked during update
|
21
|
+
so you can safely use it from multiple threads.
|
22
|
+
|
23
|
+
require 'run_once'
|
24
|
+
|
25
|
+
RunOnce.use_file = 'run_once.db'
|
26
|
+
# or
|
27
|
+
RunOnce.use_path = '/tmp/'
|
28
|
+
|
29
|
+
|
30
|
+
Caller Context
|
31
|
+
--------------
|
32
|
+
|
33
|
+
RunOnce looks at the ruby stack to know where its being called from, so you
|
34
|
+
can use it in different places in the same program.
|
35
|
+
|
36
|
+
Sometimes you may set the context manually using with_context(). You can
|
37
|
+
re-use the same context in different places in your program, for example to
|
38
|
+
make sure warning emails of any kind are only sent once every 2 minutes, eg:
|
39
|
+
|
40
|
+
RunOnce.with_context('email alert').in(120) { EmailAlert.deliver ... }
|
41
|
+
|
42
|
+
|
43
|
+
Contact the author
|
44
|
+
------------------
|
45
|
+
|
46
|
+
Andrew Snow <andrew@modulus.org>
|
47
|
+
Andys^ on irc.freenode.net
|
data/Rakefile
ADDED
data/lib/run_once.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
class RunOnce
|
3
|
+
class << self
|
4
|
+
attr_accessor :db_file
|
5
|
+
|
6
|
+
def use_path=(path)
|
7
|
+
self.use_file = path + "/#{self}.db.#{$$}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def use_file=(file)
|
11
|
+
self.db_file = file
|
12
|
+
File.open(@db_file, 'a+') { }
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_a_context
|
16
|
+
self.new(caller[1])
|
17
|
+
end
|
18
|
+
|
19
|
+
def with_context(context)
|
20
|
+
new(context)
|
21
|
+
end
|
22
|
+
|
23
|
+
def in(seconds, &bl)
|
24
|
+
get_a_context.in(seconds, &bl)
|
25
|
+
end
|
26
|
+
|
27
|
+
def update_db(key, val)
|
28
|
+
formatted_val = '%015.3f' % val.to_f
|
29
|
+
File.open(@db_file, 'w+') do |io|
|
30
|
+
io.flock(File::LOCK_EX)
|
31
|
+
io.rewind
|
32
|
+
io.puts key if !search_db(io,key)
|
33
|
+
io.puts formatted_val
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def lookup_db(key)
|
38
|
+
retval = nil
|
39
|
+
File.open(@db_file, 'r') do |io|
|
40
|
+
io.flock(File::LOCK_SH)
|
41
|
+
retval = io.readline.strip if search_db(io,key)
|
42
|
+
end
|
43
|
+
retval
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
def search_db(io, key)
|
48
|
+
retval = nil
|
49
|
+
keywithnewline = "#{key}\n"
|
50
|
+
while(retval.nil? && line = io.gets)
|
51
|
+
if(line == keywithnewline)
|
52
|
+
retval = true
|
53
|
+
else
|
54
|
+
io.gets # skip over the next value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
retval
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
@db_file = "/tmp/#{self}.db.#{$$}"
|
62
|
+
|
63
|
+
def initialize(context)
|
64
|
+
@context = context
|
65
|
+
end
|
66
|
+
|
67
|
+
def in(seconds)
|
68
|
+
if(!last_happened || last_happened && (Time.now.to_f > (last_happened + seconds.to_f)))
|
69
|
+
update_last_happened
|
70
|
+
yield
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def last_happened
|
75
|
+
@last_happened ||= (self.class.lookup_db(@context).to_f rescue nil)
|
76
|
+
end
|
77
|
+
|
78
|
+
def update_last_happened
|
79
|
+
self.class.update_db(@context, Time.now.to_f)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
data/run_once.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{run_once}
|
5
|
+
s.version = "1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Andrew Snow"]
|
9
|
+
s.date = %q{2011-07-31}
|
10
|
+
s.description = %q{Ruby Gem to limit rate of code execution to every N seconds}
|
11
|
+
s.email = %q{andrew@modulus.org}
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "README", "lib/run_once.rb"]
|
13
|
+
s.files = ["CHANGELOG", "Manifest", "README", "Rakefile", "lib/run_once.rb", "test/test_all.rb", "run_once.gemspec"]
|
14
|
+
s.homepage = %q{https://github.com/andys/run_once}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Run_once", "--main", "README"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{run_once}
|
18
|
+
s.rubygems_version = %q{1.3.7}
|
19
|
+
s.summary = %q{Ruby Gem to limit rate of code execution to every N seconds}
|
20
|
+
s.test_files = ["test/test_all.rb"]
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 3
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
27
|
+
else
|
28
|
+
end
|
29
|
+
else
|
30
|
+
end
|
31
|
+
end
|
data/test/test_all.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require "./#{File.dirname(__FILE__)}/../lib/run_once.rb"
|
4
|
+
|
5
|
+
class TestRunOnce < Test::Unit::TestCase
|
6
|
+
class TestException < Exception ; end
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@fn = './run_once.db'
|
10
|
+
File.unlink @fn rescue nil
|
11
|
+
RunOnce.use_file = @fn
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
File.unlink @fn rescue nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_db_file
|
19
|
+
assert File.exists?(@fn)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_db_ops
|
23
|
+
assert_equal nil, RunOnce.lookup_db('A')
|
24
|
+
RunOnce.update_db('A', 1.1)
|
25
|
+
assert_equal 1.1, RunOnce.lookup_db('A').to_f
|
26
|
+
RunOnce.update_db('A', 2.2)
|
27
|
+
assert_equal 2.2, RunOnce.lookup_db('A').to_f
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_manual_context
|
31
|
+
assert_raises TestException do
|
32
|
+
RunOnce.with_context('test').in(0.5) { raise TestException.new }
|
33
|
+
end
|
34
|
+
3.times do
|
35
|
+
assert_nothing_raised TestException do
|
36
|
+
RunOnce.with_context('test').in(0.5) { raise TestException.new }
|
37
|
+
end
|
38
|
+
sleep 0.1
|
39
|
+
end
|
40
|
+
sleep 0.3
|
41
|
+
assert_raises TestException do
|
42
|
+
RunOnce.with_context('test').in(0.5) { raise TestException.new }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_auto_context
|
47
|
+
block = lambda do
|
48
|
+
RunOnce.in(0.5) { raise TestException.new }
|
49
|
+
end
|
50
|
+
assert_raises TestException, &block
|
51
|
+
3.times do
|
52
|
+
assert_nothing_raised TestException, &block
|
53
|
+
sleep 0.1
|
54
|
+
end
|
55
|
+
sleep 0.3
|
56
|
+
assert_raises TestException, &block
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_1_sec
|
60
|
+
stop_time = Time.now + 0.95
|
61
|
+
counter = 0
|
62
|
+
while(stop_time > Time.now)
|
63
|
+
RunOnce.in(0.1) { counter += 1 }
|
64
|
+
end
|
65
|
+
assert_equal 10, counter
|
66
|
+
end
|
67
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: run_once
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
version: "1.0"
|
9
|
+
platform: ruby
|
10
|
+
authors:
|
11
|
+
- Andrew Snow
|
12
|
+
autorequire:
|
13
|
+
bindir: bin
|
14
|
+
cert_chain: []
|
15
|
+
|
16
|
+
date: 2011-07-31 00:00:00 +10:00
|
17
|
+
default_executable:
|
18
|
+
dependencies: []
|
19
|
+
|
20
|
+
description: Ruby Gem to limit rate of code execution to every N seconds
|
21
|
+
email: andrew@modulus.org
|
22
|
+
executables: []
|
23
|
+
|
24
|
+
extensions: []
|
25
|
+
|
26
|
+
extra_rdoc_files:
|
27
|
+
- CHANGELOG
|
28
|
+
- README
|
29
|
+
- lib/run_once.rb
|
30
|
+
files:
|
31
|
+
- CHANGELOG
|
32
|
+
- Manifest
|
33
|
+
- README
|
34
|
+
- Rakefile
|
35
|
+
- lib/run_once.rb
|
36
|
+
- test/test_all.rb
|
37
|
+
- run_once.gemspec
|
38
|
+
has_rdoc: true
|
39
|
+
homepage: https://github.com/andys/run_once
|
40
|
+
licenses: []
|
41
|
+
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options:
|
44
|
+
- --line-numbers
|
45
|
+
- --inline-source
|
46
|
+
- --title
|
47
|
+
- Run_once
|
48
|
+
- --main
|
49
|
+
- README
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
segments:
|
66
|
+
- 1
|
67
|
+
- 2
|
68
|
+
version: "1.2"
|
69
|
+
requirements: []
|
70
|
+
|
71
|
+
rubyforge_project: run_once
|
72
|
+
rubygems_version: 1.3.7
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: Ruby Gem to limit rate of code execution to every N seconds
|
76
|
+
test_files:
|
77
|
+
- test/test_all.rb
|