crocodile 1.0 → 2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3e35b8e2d866cda5902a83846d31abcff811cc74
4
- data.tar.gz: 83e2ed773492325df10a068b6a8ed6b231df1531
3
+ metadata.gz: d2627bc70065abf622c9cb773882267676bd6fc8
4
+ data.tar.gz: 1b919c277bd260a7b972223111b07cc08e9d2536
5
5
  SHA512:
6
- metadata.gz: ddc93616e432e0e0b060d28cfa818a484e4f0234dd2a0f4fc1923a51e4b74e8669254fe6a342cea2a4dfc27add42d95affd9674ae3fb90eb45522d84f5d3040b
7
- data.tar.gz: 9ce1f4006abaf5a3c067760bb211fd7d0b70e7d5fc725feca5d9474e7e834fd06d10cffe9ba145a41a492a77f4dd2a2953008a886ded5baf1b8bedc0daa95aa8
6
+ metadata.gz: 6c7d0d8bf450e0f593004f9d10495d1a327b4a2a613b7f1283e0ace971e552ca252537aca06884774d8233d602fd95501b89771cc6d227326b2c7a6a20019c59
7
+ data.tar.gz: 753a061b3df20c39b1ff53db8c9e6dfbb77cc8dab37eefd9fb54792a8a2a4d75c81102ca59106474f366adfb6fdef24c931975c8ecaba28708f18fb9a8699ff7
@@ -0,0 +1 @@
1
+ mocha -v 1.1.0
@@ -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
+
@@ -1,62 +1,11 @@
1
1
  #! /usr/bin/env ruby
2
+
2
3
  require 'optparse'
3
- require 'eventmachine'
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 worker name"
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
-
@@ -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 RuntimeError.new("Must reimplement self.interval in a subclass")
9
+ raise NotImplementedError, "Must reimplement self.interval in a subclass"
10
10
  end
11
11
 
12
12
  def self.run
13
- raise RuntimeError.new("Must reimplement self.interval in a subclass")
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: '1.0'
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: 2014-07-15 00:00:00.000000000 Z
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