metrognome 0.0.1 → 0.0.2
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 +15 -0
- data/.gitignore +4 -0
- data/README.md +69 -1
- data/lib/metrognome.rb +0 -1
- data/lib/metrognome/registrar.rb +31 -17
- data/lib/metrognome/scheduler.rb +23 -19
- data/lib/metrognome/version.rb +1 -1
- data/lib/tasks/metrognome.rake +3 -1
- data/metrognome.gemspec +2 -0
- data/spec/metrognome/scheduler_spec.rb +42 -0
- data/spec/spec_helper.rb +4 -0
- metadata +24 -11
- data/lib/metrognome/runner.rb +0 -55
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YzU2MDJhNDNmODk3YzA0ZGFlYWEyMDQyNWMyOGIzZWUzZDM0ZjBmOQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YTk4OWZiNmRiMWY4ODIwZDdlNmIzOGUxMmRlMzY0MmViOGQwYzFiNw==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZTJkNjRjM2FhMGU4MDcyZWRlZmM5NThlMzUxZWZiZDhiNzk1N2NjNGUwZjlh
|
10
|
+
NzBhY2Q1NjdkMjBiM2ZmZTU5ZWVkMDA3MWYzMDE3ZDU3OWY4ZjA4ZWM2MmI2
|
11
|
+
YmYyNTc1YzY5NTg5ZjFjZDAzMjQ3NzUyNmM0YzVjYmNkYTkzMTc=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MGIyYjM0YzEzNzFjNjBmYThkNGM2ZTY3MzA0ZWY2NGZjNTI2MGUzNmZlNDMw
|
14
|
+
M2EwODE3NmEwZGRjYjhlNTdjMzQ3YTRmNmFkMzFiZGZlZWUwNmU2YzM3Nzdl
|
15
|
+
YmFlZDEyMzkxNDNiMmVjOTA2Mjc2OWEzOTBmOWFjNzk1MDlmOWU=
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -18,7 +18,75 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
## Usage
|
20
20
|
|
21
|
-
|
21
|
+
### Starting and Stopping
|
22
|
+
|
23
|
+
To start the metrognome, run:
|
24
|
+
|
25
|
+
$ bundle exec rake metrognome:start
|
26
|
+
|
27
|
+
In production, use RAILS\_ENV to let Rails know to start the right environment:
|
28
|
+
|
29
|
+
$ bundle exec rake RAILS_ENV=production metrognome:start
|
30
|
+
|
31
|
+
To stop the metrognome, run:
|
32
|
+
|
33
|
+
$ bundle exec rake metrognome:stop
|
34
|
+
|
35
|
+
### Schedulers
|
36
|
+
|
37
|
+
Schedulers are registered with metrognome and run on a periodic basis.
|
38
|
+
Schedulers are `.rb` files read from the folder `config/metrognome`
|
39
|
+
|
40
|
+
#### Initialization and task
|
41
|
+
|
42
|
+
All schedulers are instances of Metrognome::Scheduler, initialized like:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
beeper = Metrognome::Scheduler.new(10.seconds)
|
46
|
+
```
|
47
|
+
|
48
|
+
The beeper task will be executed every 10 seconds beginning when the metrognome
|
49
|
+
starts. To define the task to execute, pass a block to `beeper.task`:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
beeper.task do
|
53
|
+
puts "Beep!"
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
Finally, register the task with `Metrognome::Registrar.register`:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
Metrognome::Registrar.register beeper
|
61
|
+
```
|
62
|
+
|
63
|
+
#### Setup and local variables
|
64
|
+
|
65
|
+
Most schedulers can benefit from some persistence, or at least some setup. Let's
|
66
|
+
have our beeper keep track of how many times it's beeped.
|
67
|
+
|
68
|
+
Accessors can be declared when the scheduler is initialized:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
beeper = Metrognome::Scheduler.new(10.seconds, :counter)
|
72
|
+
```
|
73
|
+
|
74
|
+
The counter can be given an initial value by passing a block to `beeper.setup`:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
beeper.setup do
|
78
|
+
self.counter = 0
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
Finally, the counter can be used in the task block:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
beeper.task do
|
86
|
+
self.counter += 1
|
87
|
+
logger.info "Beep! #{counter} time(s)"
|
88
|
+
end
|
89
|
+
```
|
22
90
|
|
23
91
|
## Contributing
|
24
92
|
|
data/lib/metrognome.rb
CHANGED
data/lib/metrognome/registrar.rb
CHANGED
@@ -1,27 +1,41 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
1
3
|
module Metrognome
|
2
4
|
class Registrar
|
3
|
-
|
4
|
-
def registered
|
5
|
-
@registered ||= []
|
6
|
-
end
|
5
|
+
include Singleton
|
7
6
|
|
8
|
-
|
9
|
-
|
7
|
+
def self.register scheduler
|
8
|
+
self.instance.register scheduler
|
9
|
+
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
scheduler.
|
14
|
-
scheduler.last = Time.now - 1.year
|
15
|
-
scheduler.call_setup
|
11
|
+
def register scheduler
|
12
|
+
unless scheduler.is_a? Metrognome::Scheduler
|
13
|
+
raise ArgumentError.new "scheduler #{scheduler.inspect} must be a Metrognome::Scheduler."
|
16
14
|
end
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
@registered ||= []
|
17
|
+
@registered << scheduler
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
@registered.each { |scheduler| scheduler.run }
|
22
|
+
|
23
|
+
wait_for_sigterm
|
24
|
+
Signal.trap("TERM") { exit 1 }
|
25
|
+
|
26
|
+
@registered.each do |scheduler|
|
27
|
+
scheduler.stop
|
28
|
+
scheduler.thread.run
|
29
|
+
end
|
30
|
+
@registered.each { |scheduler| scheduler.thread.join }
|
24
31
|
end
|
32
|
+
|
33
|
+
private
|
34
|
+
# Sleep until we get a SIGTERM.
|
35
|
+
def wait_for_sigterm
|
36
|
+
Signal.trap("TERM") { return }
|
37
|
+
sleep
|
38
|
+
end
|
25
39
|
end
|
26
40
|
end
|
27
41
|
|
data/lib/metrognome/scheduler.rb
CHANGED
@@ -1,18 +1,11 @@
|
|
1
1
|
module Metrognome
|
2
2
|
class Scheduler
|
3
|
-
|
3
|
+
attr_reader :thread
|
4
4
|
|
5
|
-
def initialize interval,
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
raise ArgumentError.new "All variables must be Strings or Symbols!" unless v.is_a? String or v.is_a? Symbol
|
10
|
-
eval "class << self; attr_accessor :#{v}; end"
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def lock
|
15
|
-
File.join(Rails.root, 'tmp', @lock)
|
5
|
+
def initialize interval, description = 'Anonymous scheduler'
|
6
|
+
@interval = interval
|
7
|
+
@description = description
|
8
|
+
@stopped = false
|
16
9
|
end
|
17
10
|
|
18
11
|
def setup &block
|
@@ -23,15 +16,26 @@ module Metrognome
|
|
23
16
|
@task = block
|
24
17
|
end
|
25
18
|
|
26
|
-
def
|
27
|
-
|
19
|
+
def stop
|
20
|
+
@stopped = true
|
28
21
|
end
|
29
22
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
23
|
+
def run
|
24
|
+
@thread = Thread.new do
|
25
|
+
instance_eval(&@setup) if @setup
|
26
|
+
|
27
|
+
until @stopped do
|
28
|
+
start = Time.now
|
29
|
+
Rails.logger.info "#{@description} beginning task execution."
|
30
|
+
instance_eval(&@task) if @task
|
31
|
+
Rails.logger.info "#{@description} completed task execution."
|
32
|
+
# TODO log stopping task
|
33
|
+
|
34
|
+
until @stopped do
|
35
|
+
to_sleep = start + @interval - Time.now
|
36
|
+
break if to_sleep <= 0 or Kernel.sleep(to_sleep) >= to_sleep
|
37
|
+
end
|
38
|
+
end
|
35
39
|
end
|
36
40
|
end
|
37
41
|
end
|
data/lib/metrognome/version.rb
CHANGED
data/lib/tasks/metrognome.rake
CHANGED
@@ -14,11 +14,13 @@ namespace :metrognome do
|
|
14
14
|
desc 'Begin the metrognome scheduler'
|
15
15
|
task :start => :register do
|
16
16
|
unless File.exists? Metrognome::PIDFILE
|
17
|
+
# Ignore SIGHUP since we'll probably be started from an SSH session.
|
17
18
|
Signal.trap 'HUP', 'IGNORE'
|
18
19
|
|
19
20
|
pid = fork do
|
20
21
|
Rails.logger = Logger.new 'log/metrognome.log'
|
21
|
-
|
22
|
+
Rails.logger.formatter = Logger::Formatter.new
|
23
|
+
Metrognome::Registrar.instance.start
|
22
24
|
end
|
23
25
|
|
24
26
|
Process.detach pid
|
data/metrognome.gemspec
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_support/time'
|
3
|
+
|
4
|
+
describe Metrognome::Scheduler do
|
5
|
+
before do
|
6
|
+
@dummy_scheduler = Metrognome::Scheduler.new 1.minute, 'Test scheduler'
|
7
|
+
@dummy_scheduler.setup { @task_counter = 0 }
|
8
|
+
@dummy_scheduler.task do
|
9
|
+
if @task_counter == 5
|
10
|
+
self.stop
|
11
|
+
else
|
12
|
+
@task_counter += 1
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
@slept = 0
|
17
|
+
Kernel.stub(:sleep) do |arg|
|
18
|
+
# Make sure we're only sleeping up to the schedule time.
|
19
|
+
arg.should be < 1.minute
|
20
|
+
@slept += 1
|
21
|
+
arg + 1 # Always break out of the sleep loop.
|
22
|
+
end
|
23
|
+
|
24
|
+
# Metrognome should run in a Rails app, so set the logger we use.
|
25
|
+
Rails.logger = Logger.new nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should count to 5' do
|
29
|
+
@dummy_scheduler.run
|
30
|
+
Thread.pass while @dummy_scheduler.thread.alive?
|
31
|
+
@slept.should == 5
|
32
|
+
@dummy_scheduler.instance_eval { @task_counter }.should == 5
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should exit without exception' do
|
36
|
+
@dummy_scheduler.run
|
37
|
+
Thread.pass while @dummy_scheduler.thread.alive?
|
38
|
+
|
39
|
+
# The scheduler thread status is nil on exit due to exception.
|
40
|
+
@dummy_scheduler.thread.status.should === false
|
41
|
+
end
|
42
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metrognome
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Chris Carlon
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2014-03-31 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rails
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,11 +20,24 @@ dependencies:
|
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ~>
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '3.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
30
41
|
description: A simple repeating task scheduler for Rails applications
|
31
42
|
email:
|
32
43
|
- chris.carlon25@gmail.com
|
@@ -42,33 +53,35 @@ files:
|
|
42
53
|
- lib/metrognome.rb
|
43
54
|
- lib/metrognome/railtie.rb
|
44
55
|
- lib/metrognome/registrar.rb
|
45
|
-
- lib/metrognome/runner.rb
|
46
56
|
- lib/metrognome/scheduler.rb
|
47
57
|
- lib/metrognome/version.rb
|
48
58
|
- lib/tasks/metrognome.rake
|
49
59
|
- metrognome.gemspec
|
60
|
+
- spec/metrognome/scheduler_spec.rb
|
61
|
+
- spec/spec_helper.rb
|
50
62
|
homepage: https://github.com/cjc25/metrognome
|
51
63
|
licenses: []
|
64
|
+
metadata: {}
|
52
65
|
post_install_message:
|
53
66
|
rdoc_options: []
|
54
67
|
require_paths:
|
55
68
|
- lib
|
56
69
|
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
70
|
requirements:
|
59
71
|
- - ! '>='
|
60
72
|
- !ruby/object:Gem::Version
|
61
73
|
version: '0'
|
62
74
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
-
none: false
|
64
75
|
requirements:
|
65
76
|
- - ! '>='
|
66
77
|
- !ruby/object:Gem::Version
|
67
78
|
version: '0'
|
68
79
|
requirements: []
|
69
80
|
rubyforge_project:
|
70
|
-
rubygems_version: 1.
|
81
|
+
rubygems_version: 2.1.11
|
71
82
|
signing_key:
|
72
|
-
specification_version:
|
83
|
+
specification_version: 4
|
73
84
|
summary: A simple repeating task scheduler for Rails applications
|
74
|
-
test_files:
|
85
|
+
test_files:
|
86
|
+
- spec/metrognome/scheduler_spec.rb
|
87
|
+
- spec/spec_helper.rb
|
data/lib/metrognome/runner.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
module Metrognome
|
2
|
-
class Runner
|
3
|
-
class << self
|
4
|
-
# TODO pull out reaper_interval into config
|
5
|
-
attr_accessor :running, :reaper_last
|
6
|
-
REAPER_INTERVAL = 1
|
7
|
-
|
8
|
-
def start
|
9
|
-
setup
|
10
|
-
|
11
|
-
loop do
|
12
|
-
if @_sigterm
|
13
|
-
running.each { |t| t.kill }
|
14
|
-
reap
|
15
|
-
break
|
16
|
-
end
|
17
|
-
reap if Time.now - reaper_last > REAPER_INTERVAL
|
18
|
-
|
19
|
-
Metrognome::Registrar.registered.each do |sched|
|
20
|
-
if Time.now - sched.last > sched.interval
|
21
|
-
running << Thread.new do
|
22
|
-
sched.call_task
|
23
|
-
|
24
|
-
# DB connections will no longer be closed automatically, so
|
25
|
-
# close it manually here
|
26
|
-
ActiveRecord::Base.connection.close
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
# TODO GCD sleep instead of 1
|
31
|
-
sleep 1
|
32
|
-
end
|
33
|
-
|
34
|
-
Metrognome::Registrar.registered.each { |s| File.delete s.lock }
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
def setup
|
39
|
-
require 'set'
|
40
|
-
self.running = Set.new
|
41
|
-
self.reaper_last = Time.now
|
42
|
-
|
43
|
-
Signal.trap "TERM" do
|
44
|
-
exit 1 if @_sigterm
|
45
|
-
@_sigterm = 1
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def reap
|
50
|
-
running.delete_if { |t| t.join(0) }
|
51
|
-
self.reaper_last = Time.now
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|