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 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