ngauthier-active-listener 0.2.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/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
|
+
|