rufus-runner 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 026ca77e2c60d3b31c2930dd9c8bf03e5fb08478
4
+ data.tar.gz: f20bf4a2f524d4700765805266a6592436655c59
5
+ SHA512:
6
+ metadata.gz: c3ad75069a777e751f8d57b6b440061b69cc3bccf85d31580d1e85cfd4b44258ee6a3d864ed974c437d521e979d0a00f7e59d7be30f6d1f35f6b6eee1bef7811
7
+ data.tar.gz: 5cbd8a66992378eea954e891845c585368b401f641d9b0e8384158f7e8d50b40200e6821821ba815205445641aa0694711f9e60bea8bc875406b83af5e1bbbf5
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 HouseTrip
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # rufus-runner
2
+
3
+ [![Build Status](https://travis-ci.org/HouseTrip/rufus-runner.png)](https://travis-ci.org/HouseTrip/rufus-runner)
4
+
5
+ Wraps [rufus-scheduler](http://github.com/jmettraux/rufus-scheduler) in a
6
+ standalone process, with job timing, logging, and safe defaults.
7
+
8
+ **Note: I did not write this gem, only deployed it to RubyGems. The project appears to be dead and I'll provide
9
+ support the best I can. If the original authors decide they want to take it back over, just let me know.**
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'rufus-runner'
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install rufus-runner
21
+
22
+ Create a configuration file:
23
+
24
+ # config/schedule.rb
25
+ Rufus::TrackingScheduler.start do |scheduler|
26
+ # sentinel job that keeps running to prove Rufus is still alive
27
+ scheduler.run :name => 'no-op', :every => '60s' do
28
+ Kernel.sleep(1e-3)
29
+ end
30
+ end
31
+
32
+ The `#run` method simply takes the same options as Rufus's `#every` method,
33
+ with these safe defaults:
34
+
35
+ :mutex => <m> # because Rails is not thread-safe
36
+ :timeout => 60 # we don't want no long-running jobs, they should be DJ'd (seconds)
37
+ :discard_past => true # don't catch up with past jobs
38
+ :allow_overlapping => false # don't try to run the same job twice simultaneously
39
+
40
+ Where `<m>` is a shared, global instance of `Mutex`.
41
+
42
+ Finally, run it:
43
+
44
+ $ rufus-runner config/schedule.rb
45
+
46
+
47
+ ### Rails
48
+
49
+ If you're running Rails, you might want to have an environment loaded in the
50
+ scheduler. Simply add on top of your schedule:
51
+
52
+ # Rails 2
53
+ require 'config/boot'
54
+ require 'config/environment'
55
+
56
+ # Rails 3
57
+ require 'config/application'
58
+
59
+ When Rails is present, `rufus-runner` will call `ActiveRecord::Base.clear_active_connections!` after each job for you.
60
+
61
+ ## Contributing
62
+
63
+ 1. Fork it
64
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
65
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
66
+ 4. Push to the branch (`git push origin my-new-feature`)
67
+ 5. Create new Pull Request
data/bin/rufus-runner ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Driver for Rufus-scheduler.
4
+ # Pass it a schedule file as a command-line argument
5
+ #
6
+ $PROGRAM_NAME = 'rufus'
7
+
8
+ require 'rubygems'
9
+ require 'pathname'
10
+ require 'bundler/setup'
11
+ require 'rufus-runner'
12
+
13
+ if ARGV.empty?
14
+ puts 'Sorry, but I need a schedule. Pass it to me as the first command-line argument.'
15
+ Process.exit! 1
16
+ end
17
+
18
+ load File.expand_path(ARGV[0])
@@ -0,0 +1,37 @@
1
+ require 'rufus-runner/tracking_scheduler/job_runner'
2
+
3
+ class Rufus::TrackingScheduler::ForkingJobRunner < Rufus::TrackingScheduler::JobRunner
4
+
5
+ class UnexpectedExitStatus < StandardError; end
6
+
7
+ def run_block
8
+ @pid = fork do
9
+ $PROGRAM_NAME = "rufus: #{@name}"
10
+ @block.call
11
+ exit 0
12
+ end
13
+ status = Process.wait2(@pid)[1]
14
+ unless status.success?
15
+ raise UnexpectedExitStatus.new(status.exitstatus)
16
+ end
17
+ rescue Rufus::Scheduler::TimeOutError
18
+ log("timed out, killing")
19
+ kill
20
+ raise
21
+ end
22
+
23
+ def shutdown
24
+ kill
25
+ end
26
+
27
+
28
+ private
29
+
30
+ def kill
31
+ Process.kill("KILL", @pid)
32
+ Process.wait(@pid)
33
+ rescue Errno::ESRCH, Errno::ECHILD
34
+ # process already dead, which is fine
35
+ end
36
+
37
+ end
@@ -0,0 +1,56 @@
1
+ class Rufus::Scheduler::Job
2
+ attr_accessor :job_runner
3
+ end
4
+
5
+ class Rufus::TrackingScheduler::JobRunner
6
+
7
+ def initialize(options)
8
+ @name = options.fetch(:name)
9
+ @job = options.fetch(:job)
10
+ @block = options.fetch(:block)
11
+ @scheduler = options.fetch(:scheduler)
12
+ end
13
+
14
+ def run
15
+ return if @scheduler.shutting_down?
16
+ @job.job_runner = self
17
+ start_time = Time.now
18
+ log("starting")
19
+
20
+ begin
21
+ run_block
22
+ rescue Exception => exception
23
+ log("failed with #{exception.class.name} (#{exception.message})")
24
+ if defined?(ActiveRecord::ConnectionTimeoutError) && exception.kind_of?(ActiveRecord::ConnectionTimeoutError)
25
+ log("connection broken, exiting scheduler")
26
+ exit 0
27
+ end
28
+ else
29
+ total_time = Time.now - start_time
30
+ log("completed in %.3f s" % total_time)
31
+ end
32
+
33
+ if defined?(ActiveRecord::Base)
34
+ ActiveRecord::Base.clear_active_connections!
35
+ end
36
+ end
37
+
38
+ def shutdown
39
+ raise NotImplementedError.new
40
+ end
41
+
42
+ private
43
+
44
+ def run_block
45
+ raise NotImplementedError.new
46
+ end
47
+
48
+ def job_id
49
+ @job_id ||= '%08x' % @job.job_id.gsub(/\D/,'')
50
+ end
51
+
52
+ def log(message)
53
+ @scheduler.log("#{@name}(#{job_id}): #{message}")
54
+ end
55
+
56
+ end
@@ -0,0 +1,16 @@
1
+ require 'rufus-runner/tracking_scheduler/job_runner'
2
+
3
+ class Rufus::TrackingScheduler::ThreadingJobRunner < Rufus::TrackingScheduler::JobRunner
4
+
5
+ def shutdown
6
+ # nothing to do, threads will die automatically
7
+ end
8
+
9
+
10
+ private
11
+
12
+ def run_block
13
+ @block.call
14
+ end
15
+
16
+ end
@@ -0,0 +1,159 @@
1
+ class Rufus::TrackingScheduler
2
+ end
3
+
4
+ require 'rufus/scheduler'
5
+ require 'eventmachine'
6
+ require 'rufus-runner/tracking_scheduler/threading_job_runner'
7
+ require 'rufus-runner/tracking_scheduler/forking_job_runner'
8
+
9
+ #
10
+ # Wraps Rufus's scheduler class with signal handling,
11
+ # cancellation of jobs, and logging.
12
+ #
13
+ class Rufus::TrackingScheduler
14
+ # wait that long for jobs to complete before quitting (seconds)
15
+ GRACE_DELAY = 10
16
+
17
+ def initialize(options = {})
18
+ @options = DefaultOptions.merge(options)
19
+ @scheduler = Rufus::Scheduler::EmScheduler.start_new
20
+ log('scheduler started')
21
+ end
22
+
23
+ def run(options={}, &block)
24
+ return unless rails_environment_matches?(options.delete(:environments))
25
+ options = @options.merge(options)
26
+
27
+ name = options.delete(:name) || 'noname'
28
+ case options.delete(:fork) || :thread
29
+ when :thread
30
+ job_runner_class = ThreadingJobRunner
31
+ when :process
32
+ job_runner_class = ForkingJobRunner
33
+ else
34
+ fail ArgumentError.new("option :fork needs to be either :thread or :process")
35
+ end
36
+
37
+ schedule(options) do |job|
38
+ job_runner = job_runner_class.new(
39
+ :name => name,
40
+ :job => job,
41
+ :block => block,
42
+ :scheduler => self
43
+ )
44
+ job_runner.run
45
+ end
46
+
47
+ log("scheduled '#{name}'")
48
+ nil
49
+ end
50
+
51
+ def self.start(options = {})
52
+ EM.run do
53
+ scheduler = new(options)
54
+ scheduler.send :setup_traps
55
+ yield scheduler
56
+ end
57
+ end
58
+
59
+ def shutting_down!
60
+ @shutting_down = true
61
+ end
62
+
63
+ def shutting_down?
64
+ !!@shutting_down
65
+ end
66
+
67
+
68
+ def log(string)
69
+ $stdout.write "[#{$PROGRAM_NAME} #{format_time Time.now}] #{string}\n"
70
+ $stdout.flush
71
+ end
72
+
73
+ private
74
+
75
+ def schedule(options, &block)
76
+ if frequency = options.delete(:every)
77
+ scheduling_method = :every
78
+ elsif frequency = options.delete(:cron)
79
+ scheduling_method = :cron
80
+ else
81
+ raise ArgumentError.new('You need to specify either :every or :cron')
82
+ end
83
+
84
+ @scheduler.send(scheduling_method, frequency, options, &block)
85
+ end
86
+
87
+ def setup_traps
88
+ %w(INT TERM).each do |signal|
89
+ Signal.trap(signal) do
90
+ log "SIG#{signal} received"
91
+ # EM uses mutexes, which cannot be used in the context of a trap.
92
+ # Just do the cleanup in a thread
93
+ Thread.new {
94
+ stop_all_jobs
95
+ EM.stop_event_loop
96
+ }.join
97
+ end
98
+ end
99
+ end
100
+
101
+ def stop_all_jobs
102
+ shutting_down!
103
+ @scheduler.jobs.each_pair do |job_id, job|
104
+ job.unschedule
105
+ end
106
+ log "all jobs unscheduled"
107
+ if running_jobs_count == 0
108
+ log "no more jobs running"
109
+ return
110
+ end
111
+
112
+ log "waiting for #{running_jobs_count} still running jobs"
113
+ start_time = Time.now
114
+ while (Time.now <= start_time + GRACE_DELAY) && (running_jobs_count > 0)
115
+ Kernel.sleep(100e-3)
116
+ end
117
+
118
+ if running_jobs_count > 0
119
+ log "#{running_jobs_count} jobs did not complete"
120
+ @scheduler.running_jobs.collect(&:job_runner).each(&:shutdown)
121
+ else
122
+ log "all jobs completed"
123
+ end
124
+ end
125
+
126
+ def running_jobs_count
127
+ @scheduler.running_jobs.length
128
+ end
129
+
130
+ def format_time(time)
131
+ "%s.%03d" % [time.strftime('%F %T'), ((time.to_f - time.to_i) * 1e3).to_i]
132
+ end
133
+
134
+ def rails_environment_matches?(environments)
135
+ return true unless rails_environment
136
+ return true if environments.nil?
137
+
138
+ Array(environments).any? do |environment|
139
+ # environment can be a string or a regexp
140
+ environment === rails_environment
141
+ end
142
+ end
143
+
144
+ def rails_environment
145
+ if defined?(Rails)
146
+ Rails.env
147
+ else
148
+ ENV['RAILS_ENV'] || ENV['RACK_ENV']
149
+ end
150
+ end
151
+
152
+ DefaultOptions = {
153
+ :fork => :process, # safety first
154
+ :mutex => Mutex.new, # because Rails is not thread-safe
155
+ :timeout => 60, # we don't want no long-running jobs, they should be DJ'd (seconds)
156
+ :discard_past => true, # don't catch up with past jobs
157
+ :allow_overlapping => false, # don't try to run the same job twice simultaneously
158
+ }
159
+ end
@@ -0,0 +1,5 @@
1
+ module Rufus
2
+ module Runner
3
+ VERSION = "0.0.3"
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require "rufus-runner/version"
2
+ require "rufus-runner/tracking_scheduler"
@@ -0,0 +1,45 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $:.unshift(lib) unless $:.include?(lib)
5
+ require 'rufus-runner/version'
6
+
7
+ Gem::Specification.new do |gem|
8
+ gem.name = "rufus-runner"
9
+ gem.version = Rufus::Runner::VERSION
10
+ gem.license = 'MIT'
11
+ gem.authors = ["Julien Letessier", "Chuck Callebs"]
12
+ gem.email = ["julien.letessier@gmail.com", "chuck@callebs.io"]
13
+ gem.description = %q{Wrapper process around rufus-scheduler}
14
+ gem.summary = %q{Wrapper process around rufus-scheduler}
15
+ gem.homepage = "http://github.com/ccallebs/rufus-runner"
16
+
17
+ gem.add_runtime_dependency 'eventmachine'
18
+ gem.add_runtime_dependency 'rufus-scheduler', '~> 2.0.23'
19
+
20
+ gem.add_development_dependency 'rake'
21
+ gem.add_development_dependency 'rspec', '~> 2'
22
+ gem.add_development_dependency 'rb-inotify'
23
+ gem.add_development_dependency 'rb-fsevent'
24
+ gem.add_development_dependency 'rb-fchange'
25
+ gem.add_development_dependency 'terminal-notifier-guard'
26
+ gem.add_development_dependency 'pry'
27
+ gem.add_development_dependency 'pry-nav'
28
+ gem.add_development_dependency 'rspec-instafail'
29
+
30
+ gem.files = %w(
31
+ LICENSE.txt
32
+ README.md
33
+ bin/rufus-runner
34
+ lib/rufus-runner.rb
35
+ lib/rufus-runner/tracking_scheduler.rb
36
+ lib/rufus-runner/tracking_scheduler/job_runner.rb
37
+ lib/rufus-runner/tracking_scheduler/threading_job_runner.rb
38
+ lib/rufus-runner/tracking_scheduler/forking_job_runner.rb
39
+ lib/rufus-runner/version.rb
40
+ rufus-runner.gemspec
41
+ )
42
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
43
+ gem.test_files = gem.files.grep(%r{^spec/})
44
+ gem.require_paths = ["lib"]
45
+ end
metadata ADDED
@@ -0,0 +1,211 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rufus-runner
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Julien Letessier
8
+ - Chuck Callebs
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-08-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: eventmachine
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rufus-scheduler
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 2.0.23
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 2.0.23
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rspec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '2'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '2'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rb-inotify
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rb-fsevent
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: rb-fchange
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: terminal-notifier-guard
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: pry
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ - !ruby/object:Gem::Dependency
141
+ name: pry-nav
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ - !ruby/object:Gem::Dependency
155
+ name: rspec-instafail
156
+ requirement: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ type: :development
162
+ prerelease: false
163
+ version_requirements: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ description: Wrapper process around rufus-scheduler
169
+ email:
170
+ - julien.letessier@gmail.com
171
+ - chuck@callebs.io
172
+ executables:
173
+ - rufus-runner
174
+ extensions: []
175
+ extra_rdoc_files: []
176
+ files:
177
+ - LICENSE.txt
178
+ - README.md
179
+ - bin/rufus-runner
180
+ - lib/rufus-runner.rb
181
+ - lib/rufus-runner/tracking_scheduler.rb
182
+ - lib/rufus-runner/tracking_scheduler/forking_job_runner.rb
183
+ - lib/rufus-runner/tracking_scheduler/job_runner.rb
184
+ - lib/rufus-runner/tracking_scheduler/threading_job_runner.rb
185
+ - lib/rufus-runner/version.rb
186
+ - rufus-runner.gemspec
187
+ homepage: http://github.com/ccallebs/rufus-runner
188
+ licenses:
189
+ - MIT
190
+ metadata: {}
191
+ post_install_message:
192
+ rdoc_options: []
193
+ require_paths:
194
+ - lib
195
+ required_ruby_version: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ">="
198
+ - !ruby/object:Gem::Version
199
+ version: '0'
200
+ required_rubygems_version: !ruby/object:Gem::Requirement
201
+ requirements:
202
+ - - ">="
203
+ - !ruby/object:Gem::Version
204
+ version: '0'
205
+ requirements: []
206
+ rubyforge_project:
207
+ rubygems_version: 2.4.8
208
+ signing_key:
209
+ specification_version: 4
210
+ summary: Wrapper process around rufus-scheduler
211
+ test_files: []