crocodile 1.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.
- checksums.yaml +4 -4
- data/.gems-test +1 -0
- data/README.md +129 -0
- data/bin/crocodile +12 -56
- data/crocodile.gemspec +19 -0
- data/lib/crocodile/crocodile_job.rb +2 -2
- data/lib/crocodile/crocodile_process.rb +64 -0
- data/test/unit/crocodile_job_test.rb +15 -0
- data/test/unit/crocodile_process_test.rb +46 -0
- data/test/unit/worker_test.rb +18 -0
- metadata +27 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2627bc70065abf622c9cb773882267676bd6fc8
|
4
|
+
data.tar.gz: 1b919c277bd260a7b972223111b07cc08e9d2536
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c7d0d8bf450e0f593004f9d10495d1a327b4a2a613b7f1283e0ace971e552ca252537aca06884774d8233d602fd95501b89771cc6d227326b2c7a6a20019c59
|
7
|
+
data.tar.gz: 753a061b3df20c39b1ff53db8c9e6dfbb77cc8dab37eefd9fb54792a8a2a4d75c81102ca59106474f366adfb6fdef24c931975c8ecaba28708f18fb9a8699ff7
|
data/.gems-test
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
mocha -v 1.1.0
|
data/README.md
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
##Crocodile
|
2
|
+
|
3
|
+
Crocodile is a gem for running periodic [background] jobs written in Ruby.
|
4
|
+
Crocodile provides a worker binary that's in charge of running the jobs you define, it also provides a base class for those jobs.
|
5
|
+
|
6
|
+
###Install
|
7
|
+
|
8
|
+
```
|
9
|
+
gem install crocodile
|
10
|
+
```
|
11
|
+
|
12
|
+
###Jobs
|
13
|
+
|
14
|
+
Crocodile is based on some conventions for running your jobs:
|
15
|
+
|
16
|
+
* It will look for the jobs files in the `jobs` directory of your application
|
17
|
+
* The job file name is the name of the job to run
|
18
|
+
* The job class name must be the filename ending in `Job`, so if your file is `dummy.rb`, the class defined inside the file must be `DummyJob`.
|
19
|
+
|
20
|
+
Your job class must respond to an interface in order to be ran by crocodile, for this, Crocodile provides the `CrocodileJob` class for you to inherit from there.
|
21
|
+
|
22
|
+
However, the interface is very simple, you can write your own class and forget about CrocodileJob as long as:
|
23
|
+
|
24
|
+
* You have a class method `YourClass#message` that returns a string
|
25
|
+
* You have a class method `YourClass#interval` that returns an integer which indicates, in seconds, how often your job will be ran
|
26
|
+
* You have a class method `YourClass#one_run_only` that returns a boolean indicating if crocodile has to run it only once
|
27
|
+
* You have a class method `YourClass#logger` that returns a `Logger` object
|
28
|
+
* You have a class method `YourClass#run` that implements the logic of your job
|
29
|
+
|
30
|
+
If you use the `CrocodileJob` as the base class, you only need to reimplement:
|
31
|
+
|
32
|
+
* `CrocodileJob#interval`
|
33
|
+
* `CrocodileJob#run`
|
34
|
+
|
35
|
+
The default logger will log to `STDOUT`, if you want to log to a file, reimplement `CrocodileJob#logger` with something like `Logger.new("jobs/logs/dummy.log")`
|
36
|
+
|
37
|
+
###Example
|
38
|
+
|
39
|
+
Suppose you have an application, let's say `dummy_app` and you need to run periodic background jobs on it.
|
40
|
+
Go to the `dummy_app` dir and create the necessary directories:
|
41
|
+
|
42
|
+
```
|
43
|
+
mkdir -p jobs/pids
|
44
|
+
```
|
45
|
+
|
46
|
+
Then define a dummy job for your dummy app:
|
47
|
+
|
48
|
+
```Ruby
|
49
|
+
# file: jobs/dummy.rb
|
50
|
+
|
51
|
+
require 'crocodile'
|
52
|
+
|
53
|
+
class DummyJob < CrocodileJob
|
54
|
+
def self.interval
|
55
|
+
ENV['DUMMY_JOB_INTERVAL'] || 10
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.run
|
59
|
+
puts "Look ma, I'm running!!"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
Once the job is defined, go to the terminal and run it:
|
65
|
+
|
66
|
+
```
|
67
|
+
crocodile dummy start
|
68
|
+
```
|
69
|
+
|
70
|
+
You will see the output of the `run` method every 10 seconds. If you want to override the interval, just export the evn var:
|
71
|
+
|
72
|
+
```
|
73
|
+
DUMMY_JOB_INTERVAL=3 crocodile dummy start
|
74
|
+
```
|
75
|
+
|
76
|
+
This is useful for testing your job works correcly, however, it won't be very useful when you need to run it in a server, since you will want to log out from the server and keep the job running, so you need to _daemonize_ your job. Also, to keep track of what your job is doing, you might want to use a log file:
|
77
|
+
|
78
|
+
```Ruby
|
79
|
+
# file: jobs/dummy.rb
|
80
|
+
|
81
|
+
require 'crocodile'
|
82
|
+
|
83
|
+
class DummyJob < CrocodileJob
|
84
|
+
def self.interval
|
85
|
+
ENV['DUMMY_JOB_INTERVAL'] || 10
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.run
|
89
|
+
puts "Look ma, I'm running!!"
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.logger
|
93
|
+
Logger.new("/tmp/dummy.log")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
Then, run it as a daemon:
|
99
|
+
|
100
|
+
```
|
101
|
+
crocodile -d dummy start
|
102
|
+
```
|
103
|
+
|
104
|
+
Now you can check the file `/tmp/dummy.log` for the output of your job at any given time.
|
105
|
+
|
106
|
+
The file `jobs/pids/dummy.pid` will show the PID of the running job.
|
107
|
+
|
108
|
+
If you need to stop the job, you do it through command line as well:
|
109
|
+
|
110
|
+
```
|
111
|
+
crocodile dummy stop
|
112
|
+
```
|
113
|
+
|
114
|
+
If you need just schedule the job instead to run it immediately, you do it through command line as well:
|
115
|
+
|
116
|
+
```
|
117
|
+
crocodile dummy schedule
|
118
|
+
```
|
119
|
+
|
120
|
+
|
121
|
+
So, that's pretty much it. We hope you enjoy it.
|
122
|
+
|
123
|
+
###How to collaborate
|
124
|
+
|
125
|
+
If you find a bug or want to collaborate with the code, you can:
|
126
|
+
|
127
|
+
* Report issues trhough the issue tracker
|
128
|
+
* Fork the repository into your own account and submit a Pull Request
|
129
|
+
|
data/bin/crocodile
CHANGED
@@ -1,62 +1,11 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
|
+
|
2
3
|
require 'optparse'
|
3
|
-
|
4
|
+
require_relative '../lib/crocodile/crocodile_process'
|
4
5
|
|
5
6
|
module Crocodile
|
6
7
|
def self.print_help
|
7
|
-
$stdout.puts "Usage: crocodile [-d] [-p /path/to/pids] job_name (start|stop)"
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
class CrocodileProcess
|
12
|
-
def initialize(job_name, pids_dir)
|
13
|
-
@name = job_name
|
14
|
-
@job_path = File.expand_path("jobs/#{job_name}")
|
15
|
-
@pid_path = File.expand_path("#{job_name}.pid", pids_dir)
|
16
|
-
end
|
17
|
-
|
18
|
-
def start
|
19
|
-
require @job_path
|
20
|
-
job_class = constantize(@name)
|
21
|
-
|
22
|
-
Signal.trap("INT") { EventMachine.stop }
|
23
|
-
Signal.trap("TERM") { EventMachine.stop }
|
24
|
-
|
25
|
-
EventMachine.run do
|
26
|
-
timer = EventMachine::PeriodicTimer.new(job_class.interval || 60) do
|
27
|
-
job_class.logger.info job_class.message
|
28
|
-
job_class.run
|
29
|
-
if job_class.one_run_only
|
30
|
-
timer.cancel
|
31
|
-
EventMachine.stop
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def stop
|
38
|
-
if File.exists?(@pid_path)
|
39
|
-
Process.kill :TERM, File.open(@pid_path).read.strip.to_i
|
40
|
-
File.delete(@pid_path)
|
41
|
-
true
|
42
|
-
else
|
43
|
-
false
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def daemonize!
|
48
|
-
Process.daemon(true)
|
49
|
-
File.open(@pid_path, File::RDWR|File::EXCL|File::CREAT, 0600) { |io| io.write(Process.pid) }
|
50
|
-
end
|
51
|
-
|
52
|
-
def clean
|
53
|
-
if File.exists?(@pid_path)
|
54
|
-
File.delete(@pid_path)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def constantize(name)
|
59
|
-
eval name.split("_").push("job").map(&:capitalize).join
|
8
|
+
$stdout.puts "Usage: crocodile [-d] [-p /path/to/pids] job_name (start|schedule|stop)"
|
60
9
|
end
|
61
10
|
end
|
62
11
|
|
@@ -76,7 +25,7 @@ job_name = ARGV.shift
|
|
76
25
|
action = ARGV.shift
|
77
26
|
|
78
27
|
unless job_name
|
79
|
-
$stderr.puts "Must indicate a
|
28
|
+
$stderr.puts "Must indicate a job name"
|
80
29
|
Crocodile.print_help
|
81
30
|
exit 1
|
82
31
|
end
|
@@ -102,6 +51,14 @@ when "start"
|
|
102
51
|
$stdout.puts "Starting Job #{job_name} #{opts[:daemonize] ? "(daemonized)" : ""}"
|
103
52
|
process.start
|
104
53
|
|
54
|
+
when "schedule"
|
55
|
+
if opts[:daemonize]
|
56
|
+
process.daemonize!
|
57
|
+
end
|
58
|
+
|
59
|
+
$stdout.puts "Scheduling Job #{job_name} #{opts[:daemonize] ? "(daemonized)" : ""}"
|
60
|
+
process.schedule
|
61
|
+
|
105
62
|
when "stop"
|
106
63
|
$stdout.puts "Stopping Job #{job_name}"
|
107
64
|
if process.stop
|
@@ -118,4 +75,3 @@ end
|
|
118
75
|
at_exit do
|
119
76
|
process.clean
|
120
77
|
end
|
121
|
-
|
data/crocodile.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "crocodile"
|
5
|
+
s.version = "2.0"
|
6
|
+
s.summary = "Periodic Ruby jobs runner"
|
7
|
+
s.license = "MIT"
|
8
|
+
s.description = "A simple worker to run Ruby jobs periodically"
|
9
|
+
s.authors = ["Lautaro Orazi", "Leonardo Mateo"]
|
10
|
+
s.email = ["taro@threefunkymonkeys.com", "kandalf@threefunkymonkeys.com"]
|
11
|
+
s.homepage = "https://github.com/threefunkymonkeys/crocodile"
|
12
|
+
s.require_paths = ["lib"]
|
13
|
+
s.bindir = "bin"
|
14
|
+
s.executables << "crocodile"
|
15
|
+
s.add_dependency "eventmachine", '~> 1.0'
|
16
|
+
s.add_development_dependency 'mocha', '1.1.0'
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
end
|
@@ -6,11 +6,11 @@ class CrocodileJob
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def self.interval
|
9
|
-
raise
|
9
|
+
raise NotImplementedError, "Must reimplement self.interval in a subclass"
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.run
|
13
|
-
raise
|
13
|
+
raise NotImplementedError, "Must reimplement self.run in a subclass"
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.one_run_only
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
class CrocodileProcess
|
4
|
+
def initialize(job_name, pids_dir)
|
5
|
+
@name = job_name
|
6
|
+
@job_path = File.expand_path("jobs/#{job_name}")
|
7
|
+
@pid_path = File.expand_path("#{job_name}.pid", pids_dir)
|
8
|
+
end
|
9
|
+
|
10
|
+
def start
|
11
|
+
schedule(launch_immediately=true)
|
12
|
+
end
|
13
|
+
|
14
|
+
def schedule(launch_immediately=false)
|
15
|
+
require @job_path
|
16
|
+
job_class = constantize(@name)
|
17
|
+
|
18
|
+
Signal.trap("INT") { EventMachine.stop }
|
19
|
+
Signal.trap("TERM") { EventMachine.stop }
|
20
|
+
|
21
|
+
EventMachine.run do
|
22
|
+
EventMachine.next_tick do
|
23
|
+
job_class.logger.info job_class.message
|
24
|
+
job_class.run
|
25
|
+
EventMachine.stop if job_class.one_run_only
|
26
|
+
end if launch_immediately
|
27
|
+
|
28
|
+
timer = EventMachine::PeriodicTimer.new(job_class.interval || 60) do
|
29
|
+
job_class.logger.info job_class.message
|
30
|
+
job_class.run
|
31
|
+
if job_class.one_run_only
|
32
|
+
timer.cancel
|
33
|
+
EventMachine.stop
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def stop
|
40
|
+
if File.exists?(@pid_path)
|
41
|
+
Process.kill :TERM, File.open(@pid_path).read.strip.to_i
|
42
|
+
File.delete(@pid_path)
|
43
|
+
true
|
44
|
+
else
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def daemonize!
|
50
|
+
Process.daemon(true)
|
51
|
+
File.open(@pid_path, File::RDWR|File::EXCL|File::CREAT, 0600) { |io| io.write(Process.pid) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def clean
|
55
|
+
if File.exists?(@pid_path)
|
56
|
+
File.delete(@pid_path)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def constantize(name)
|
61
|
+
Kernel.const_get(name.split("_").push("job").map(&:capitalize).join)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'minitest/spec'
|
3
|
+
require 'mocha/mini_test'
|
4
|
+
require_relative '../../lib/crocodile/crocodile_job'
|
5
|
+
|
6
|
+
describe 'CrocodileJob' do
|
7
|
+
it 'should respond to basic methods' do
|
8
|
+
CrocodileJob.must_respond_to :message
|
9
|
+
CrocodileJob.must_respond_to :interval
|
10
|
+
CrocodileJob.must_respond_to :run
|
11
|
+
CrocodileJob.must_respond_to :one_run_only
|
12
|
+
CrocodileJob.must_respond_to :logger
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'minitest/spec'
|
3
|
+
require 'mocha/mini_test'
|
4
|
+
require_relative '../../lib/crocodile/crocodile_process'
|
5
|
+
require_relative '../../lib/crocodile/crocodile_job'
|
6
|
+
|
7
|
+
describe 'CrocodileProcess' do
|
8
|
+
def setup
|
9
|
+
job =<<-CLASS
|
10
|
+
class TestJob < CrocodileJob
|
11
|
+
def self.interval;1;end
|
12
|
+
def self.logger;Logger.new('/dev/null');end
|
13
|
+
def self.run;end
|
14
|
+
end
|
15
|
+
CLASS
|
16
|
+
|
17
|
+
Dir.mkdir("jobs") unless File.exist? "jobs"
|
18
|
+
File.open("jobs/test.rb", "w") do |f|
|
19
|
+
f.puts job
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def teardown
|
24
|
+
File.unlink("jobs/test.rb")
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should run the job' do
|
28
|
+
require './jobs/test' #required to be able to set expectation
|
29
|
+
|
30
|
+
process = CrocodileProcess.new('test', '/tmp')
|
31
|
+
TestJob.expects(:interval).returns(0.1)
|
32
|
+
TestJob.expects(:message).returns("Testing Process")
|
33
|
+
TestJob.expects(:run)
|
34
|
+
TestJob.expects(:one_run_only).returns(true)
|
35
|
+
|
36
|
+
process.start
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should daemonize the process' do
|
40
|
+
process = CrocodileProcess.new('test', '/tmp')
|
41
|
+
|
42
|
+
Process.expects(:daemon).returns(true)
|
43
|
+
process.daemonize!
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'minitest/spec'
|
3
|
+
|
4
|
+
describe 'Executable Worker' do
|
5
|
+
it 'should ask for job name' do
|
6
|
+
output = `./bin/crocodile 2>&1`
|
7
|
+
|
8
|
+
output.must_include "Must indicate a job name"
|
9
|
+
output.must_include "Usage"
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should ask for an action' do
|
13
|
+
output = `./bin/crocodile test 2>&1`
|
14
|
+
|
15
|
+
output.must_include "Must indicate an action"
|
16
|
+
output.must_include "Usage"
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crocodile
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '
|
4
|
+
version: '2.0'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lautaro Orazi
|
@@ -9,22 +9,36 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-03-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "
|
18
|
+
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '0'
|
20
|
+
version: '1.0'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - "
|
25
|
+
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '0'
|
27
|
+
version: '1.0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: mocha
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - '='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.1.0
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 1.1.0
|
28
42
|
description: A simple worker to run Ruby jobs periodically
|
29
43
|
email:
|
30
44
|
- taro@threefunkymonkeys.com
|
@@ -35,10 +49,17 @@ extensions: []
|
|
35
49
|
extra_rdoc_files: []
|
36
50
|
files:
|
37
51
|
- ".gems"
|
52
|
+
- ".gems-test"
|
38
53
|
- LICENSE
|
54
|
+
- README.md
|
39
55
|
- bin/crocodile
|
56
|
+
- crocodile.gemspec
|
40
57
|
- lib/crocodile.rb
|
41
58
|
- lib/crocodile/crocodile_job.rb
|
59
|
+
- lib/crocodile/crocodile_process.rb
|
60
|
+
- test/unit/crocodile_job_test.rb
|
61
|
+
- test/unit/crocodile_process_test.rb
|
62
|
+
- test/unit/worker_test.rb
|
42
63
|
homepage: https://github.com/threefunkymonkeys/crocodile
|
43
64
|
licenses:
|
44
65
|
- MIT
|