ngauthier-active-listener 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +24 -0
- data/VERSION.yml +4 -0
- data/bin/active-listener +33 -0
- data/lib/active-listener.rb +124 -0
- data/test/active_listener.yml +6 -0
- data/test/active_listener_test.rb +118 -0
- data/test/test_helper.rb +8 -0
- metadata +63 -0
data/README.markdown
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Active Listener: Event listening and firing system for ruby
|
2
|
+
|
3
|
+
## Description
|
4
|
+
This gem allows you to define tasks that can be run at specific time intervals. In the future, we plan to allow event listening and firing.
|
5
|
+
|
6
|
+
## Configuration
|
7
|
+
|
8
|
+
### Add a config file
|
9
|
+
|
10
|
+
### Include in your boot
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
### Latest Stable
|
15
|
+
sudo gem install ngauthier-active-listener
|
16
|
+
|
17
|
+
### Cutting Edge
|
18
|
+
git clone git@github.com:ngauthier/active-listener.git active-listener
|
19
|
+
cd active-listener
|
20
|
+
rake gem install
|
21
|
+
|
22
|
+
## Other notes
|
23
|
+
This gem uses Jeweler.
|
24
|
+
|
data/VERSION.yml
ADDED
data/bin/active-listener
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'active-listener'
|
3
|
+
|
4
|
+
running = true
|
5
|
+
Signal.trap("TERM") do
|
6
|
+
running = false
|
7
|
+
end
|
8
|
+
|
9
|
+
if ARGV[0].nil? or ARGV[1].nil? or (ARGV[0] != '--stop' and ARGV[2].nil?)
|
10
|
+
puts "USAGE:"
|
11
|
+
puts "\tactive-listener <config_file> <log_file> <rake_root>"
|
12
|
+
puts "\tactive-listener --stop <pid_file>"
|
13
|
+
exit(1)
|
14
|
+
end
|
15
|
+
|
16
|
+
if ARGV[0] == "--stop"
|
17
|
+
ActiveListener.stop(:pid_file => ARGV[1])
|
18
|
+
exit(0)
|
19
|
+
end
|
20
|
+
|
21
|
+
@al = ActiveListener.new :config => ARGV[0], :log_file => ARGV[1], :rake_root => ARGV[2]
|
22
|
+
|
23
|
+
if @al.events.empty?
|
24
|
+
log = File.new(ARGV[1], 'a')
|
25
|
+
log.write "ActiveListener is very boring without events"
|
26
|
+
log.close
|
27
|
+
running = false
|
28
|
+
end
|
29
|
+
|
30
|
+
while running
|
31
|
+
@al.fire_events
|
32
|
+
@al.sleep_to_next_event
|
33
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
class ActiveListener
|
5
|
+
attr_reader :events
|
6
|
+
|
7
|
+
def self.autostart(opts = {})
|
8
|
+
begin
|
9
|
+
config_file = File.expand_path(opts[:config])
|
10
|
+
pid_file = File.expand_path(opts[:pid_file])
|
11
|
+
log_file = File.expand_path(opts[:log_file])
|
12
|
+
rake_root = File.expand_path(opts[:rake_root])
|
13
|
+
rescue
|
14
|
+
raise "Need :config :pid_file :log_file :rake_root"
|
15
|
+
end
|
16
|
+
ActiveListener.stop(opts)
|
17
|
+
command = [
|
18
|
+
"start-stop-daemon --start",
|
19
|
+
"--make-pidfile --pidfile #{pid_file}",
|
20
|
+
"--background",
|
21
|
+
"--exec #{File.expand_path(File.join(File.dirname(__FILE__), '..', 'bin', 'active-listener'))}",
|
22
|
+
"--chdir #{File.expand_path(File.dirname(__FILE__))}",
|
23
|
+
"--",
|
24
|
+
"#{config_file}",
|
25
|
+
"#{log_file}",
|
26
|
+
"#{rake_root}"
|
27
|
+
].join(" ")
|
28
|
+
`#{command}`
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.stop(opts = {})
|
32
|
+
pid_file = opts[:pid_file]
|
33
|
+
`start-stop-daemon --stop --oknodo --quiet --pidfile #{File.expand_path(pid_file)}`
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(opts = {})
|
37
|
+
self.events = []
|
38
|
+
self.log_file = opts[:log_file]
|
39
|
+
self.rake_root = opts[:rake_root]
|
40
|
+
clear_log
|
41
|
+
log("ActiveListener Initialized")
|
42
|
+
load_events(opts[:config])
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_event(evt)
|
46
|
+
self.events.push(evt)
|
47
|
+
log("Added Event #{evt.inspect}")
|
48
|
+
end
|
49
|
+
|
50
|
+
def fire_events
|
51
|
+
self.events.select{|e| e.time_to_fire < 0}.each do |evt|
|
52
|
+
log("Firing event: #{evt.inspect}")
|
53
|
+
log(evt.fire(:rake_root => rake_root))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def sleep_to_next_event
|
58
|
+
self.events.sort{|x,y| x.time_to_fire <=> y.time_to_fire}
|
59
|
+
if self.events.first
|
60
|
+
sleep_time = self.events.first.time_to_fire+0.01
|
61
|
+
else
|
62
|
+
sleep_time = 0.5
|
63
|
+
end
|
64
|
+
log("Sleeping for #{sleep_time}")
|
65
|
+
sleep(sleep_time)
|
66
|
+
end
|
67
|
+
|
68
|
+
class Event
|
69
|
+
def initialize(opts = {})
|
70
|
+
self.task = opts[:task] || opts["task"]
|
71
|
+
self.period = opts[:period] || opts["period"]
|
72
|
+
self.last_fire = 0
|
73
|
+
end
|
74
|
+
|
75
|
+
def time_to_fire
|
76
|
+
last_fire + period - Time.now.to_f
|
77
|
+
end
|
78
|
+
|
79
|
+
def fire(opts = {})
|
80
|
+
self.last_fire = Time.now.to_f
|
81
|
+
Dir.chdir(opts[:rake_root]) if opts[:rake_root]
|
82
|
+
`rake #{task}`
|
83
|
+
opts[:rake_root]
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
attr_accessor :task, :period, :last_fire
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
attr_writer :events
|
95
|
+
attr_accessor :log_file, :rake_root
|
96
|
+
|
97
|
+
def load_events(config_file)
|
98
|
+
return if config_file.nil?
|
99
|
+
unless File.exists?(config_file)
|
100
|
+
log("Config file not found at #{config_file}")
|
101
|
+
return
|
102
|
+
end
|
103
|
+
log("Loading tasks from #{config_file}")
|
104
|
+
f = File.new(config_file,'r')
|
105
|
+
yml = YAML.load(f)
|
106
|
+
yml["tasks"].each do |task|
|
107
|
+
self.add_event(Event.new(task))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def clear_log
|
112
|
+
return unless log_file
|
113
|
+
FileUtils.rm_f log_file
|
114
|
+
end
|
115
|
+
|
116
|
+
def log(text)
|
117
|
+
return unless log_file
|
118
|
+
f = File.new(log_file, 'a')
|
119
|
+
f.write text
|
120
|
+
f.write "\n"
|
121
|
+
f.close
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
require 'active-listener'
|
3
|
+
|
4
|
+
class ActiveListenerTest < Test::Unit::TestCase
|
5
|
+
context "A new listener" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
@al = ActiveListener.new({})
|
9
|
+
end
|
10
|
+
|
11
|
+
should "have an empty events list" do
|
12
|
+
assert @al.events.empty?
|
13
|
+
end
|
14
|
+
|
15
|
+
should "be able to add an event" do
|
16
|
+
@al.add_event(
|
17
|
+
ActiveListener::Event.new(
|
18
|
+
:task => "meh",
|
19
|
+
:period => 5
|
20
|
+
)
|
21
|
+
)
|
22
|
+
assert !@al.events.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
should "sleep when it has no events" do
|
26
|
+
@al.sleep_to_next_event
|
27
|
+
end
|
28
|
+
|
29
|
+
context "with a file timer" do
|
30
|
+
setup do
|
31
|
+
@al.add_event(
|
32
|
+
ActiveListener::Event.new(
|
33
|
+
:task => "test:touch_file",
|
34
|
+
:period => 1
|
35
|
+
)
|
36
|
+
)
|
37
|
+
@sample_file = File.join(File.dirname(__FILE__),'sample.txt')
|
38
|
+
FileUtils.rm_f(@sample_file)
|
39
|
+
end
|
40
|
+
|
41
|
+
teardown do
|
42
|
+
FileUtils.rm_f(File.join(File.dirname(__FILE__),'sample.txt'))
|
43
|
+
end
|
44
|
+
|
45
|
+
should "touch a file" do
|
46
|
+
assert !File.exists?(@sample_file)
|
47
|
+
@al.fire_events
|
48
|
+
assert File.exists?(@sample_file)
|
49
|
+
end
|
50
|
+
|
51
|
+
should "need to be fired when added" do
|
52
|
+
assert @al.events[0].time_to_fire < 0
|
53
|
+
end
|
54
|
+
|
55
|
+
should "need to be fired 1 second after being fired" do
|
56
|
+
@al.fire_events
|
57
|
+
assert @al.events[0].time_to_fire > 0
|
58
|
+
FileUtils.rm_f(@sample_file)
|
59
|
+
@al.fire_events
|
60
|
+
assert !File.exists?(@sample_file)
|
61
|
+
@al.sleep_to_next_event
|
62
|
+
assert @al.events[0].time_to_fire < 0
|
63
|
+
assert !File.exists?(@sample_file)
|
64
|
+
@al.fire_events
|
65
|
+
assert File.exists?(@sample_file)
|
66
|
+
assert @al.events[0].time_to_fire > 0
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "A listener" do
|
73
|
+
setup do
|
74
|
+
@config_path = File.join(File.dirname(__FILE__), 'active_listener.yml')
|
75
|
+
@sample_file = File.join(File.dirname(__FILE__),'sample.txt')
|
76
|
+
FileUtils.rm_f @sample_file
|
77
|
+
end
|
78
|
+
|
79
|
+
should "be able to read events from yaml" do
|
80
|
+
@al = ActiveListener.new(:config => @config_path)
|
81
|
+
assert @al.events.size > 0
|
82
|
+
assert !File.exists?(@sample_file)
|
83
|
+
@al.fire_events
|
84
|
+
assert File.exists?(@sample_file)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
context "An autostarted listener" do
|
90
|
+
|
91
|
+
setup do
|
92
|
+
ActiveListener.autostart(
|
93
|
+
:config => File.join(File.dirname(__FILE__), 'active_listener.yml'),
|
94
|
+
:pid_file => File.join(File.dirname(__FILE__), 'active_listener.pid'),
|
95
|
+
:log_file => File.join(File.dirname(__FILE__), 'active_listener.log'),
|
96
|
+
:rake_root => File.join(File.dirname(__FILE__), '..')
|
97
|
+
)
|
98
|
+
@sample_file = File.join(File.dirname(__FILE__),'sample.txt')
|
99
|
+
end
|
100
|
+
|
101
|
+
teardown do
|
102
|
+
ActiveListener.stop(
|
103
|
+
:pid_file => File.join(File.dirname(__FILE__), 'active_listener.pid')
|
104
|
+
)
|
105
|
+
FileUtils.rm_f(File.join(File.dirname(__FILE__),'sample.txt'))
|
106
|
+
end
|
107
|
+
|
108
|
+
should "load events from the config file" do
|
109
|
+
sleep(1)
|
110
|
+
assert File.exists?(@sample_file)
|
111
|
+
FileUtils.rm_f(@sample_file)
|
112
|
+
assert !File.exists?(@sample_file)
|
113
|
+
sleep(1)
|
114
|
+
assert File.exists?(@sample_file)
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ngauthier-active-listener
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nick Gauthier
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-01-29 00:00:00 -08:00
|
13
|
+
default_executable: active-listener
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: TODO
|
17
|
+
email: nick@smartlogicsolutions.com
|
18
|
+
executables:
|
19
|
+
- active-listener
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- VERSION.yml
|
26
|
+
- README.markdown
|
27
|
+
- bin/active-listener
|
28
|
+
- lib/activelistener
|
29
|
+
- lib/active-listener.rb
|
30
|
+
- test/test_helper.rb
|
31
|
+
- test/active_listener.log
|
32
|
+
- test/active_listener.pid
|
33
|
+
- test/active_listener.yml
|
34
|
+
- test/active_listener_test.rb
|
35
|
+
has_rdoc: true
|
36
|
+
homepage: http://github.com/ngauthier/active-listener
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options:
|
39
|
+
- --inline-source
|
40
|
+
- --charset=UTF-8
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: "0"
|
48
|
+
version:
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 1.2.0
|
59
|
+
signing_key:
|
60
|
+
specification_version: 2
|
61
|
+
summary: TODO
|
62
|
+
test_files: []
|
63
|
+
|