run_once 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ v1.0. First version
@@ -0,0 +1,6 @@
1
+ CHANGELOG
2
+ Manifest
3
+ README
4
+ Rakefile
5
+ lib/run_once.rb
6
+ test/test_all.rb
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
@@ -0,0 +1,7 @@
1
+ require 'echoe'
2
+ Echoe.new('run_once') do |g|
3
+ g.summary = "Ruby Gem to limit rate of code execution to every N seconds"
4
+ g.author = 'Andrew Snow'
5
+ g.email = 'andrew@modulus.org'
6
+ g.url = "https://github.com/andys/run_once"
7
+ end
@@ -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
@@ -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
@@ -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