chapman 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in chapman.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ chapman
2
+ =======
3
+
4
+ Like stalker, but can stalk concurrently
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/chapman.gemspec ADDED
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/chapman/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Tyler Flint", 'Lyon Hill']
6
+ gem.email = ["tylerflint@gmail.com", 'lyondhill@gmail.com']
7
+ gem.description = %q{Like stalker, but can stalk concurrently}
8
+ gem.summary = %q{Takes the concept of stalker and introduces a thread pool. It's great for scenarios where tasks are light and mostly IO bound.}
9
+ gem.homepage = ""
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "chapman"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Chapman::VERSION
17
+ end
@@ -0,0 +1,8 @@
1
+ module Chapman
2
+ module Exceptions
3
+ class NoJobsDefined < RuntimeError; end
4
+ class NoSuchJob < RuntimeError; end
5
+ class JobTimeout < RuntimeError; end
6
+ class BadURL < RuntimeError; end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module Chapman
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,162 @@
1
+ require 'beanstalk-client'
2
+ require 'json'
3
+ require 'uri'
4
+ require 'timeout'
5
+
6
+ module Chapman
7
+ class Worker
8
+
9
+ def initialize
10
+ @handlers = Chapman.job_handlers
11
+ @before_handlers = Chapman.before_handlers
12
+ @error_handler = Chapman.error_handler
13
+ end
14
+
15
+ def job_in_progress?
16
+ @in_progress ||= false
17
+ end
18
+
19
+ def prep(jobs=nil)
20
+ raise Chapman::Exceptions::NoJobsDefined unless defined?(@handlers)
21
+ @error_handler = nil unless defined?(@error_handler)
22
+
23
+ jobs ||= all_jobs
24
+
25
+ jobs.each do |job|
26
+ raise(Chapman::Exceptions::NoSuchJob, job) unless @handlers[job]
27
+ end
28
+
29
+ log "Working #{jobs.size} jobs: [ #{jobs.join(' ')} ]"
30
+
31
+ jobs.each { |job| beanstalk.watch(job) }
32
+
33
+ beanstalk.list_tubes_watched.each do |server, tubes|
34
+ tubes.each { |tube| beanstalk.ignore(tube) unless jobs.include?(tube) }
35
+ end
36
+ rescue Beanstalk::NotConnected => e
37
+ failed_connection(e)
38
+ end
39
+
40
+ def work(jobs=nil)
41
+ prep(jobs)
42
+ while not Chapman.soft_quit?
43
+ work_one_job
44
+ end
45
+ end
46
+
47
+ def work_one_job
48
+ return if Chapman.soft_quit? # just to be safe
49
+ job = beanstalk.reserve
50
+
51
+ @in_progress = true
52
+
53
+ name, args = JSON.parse job.body
54
+ log_job_begin(name, args)
55
+
56
+ handler = @handlers[name]
57
+ raise(Chapman::Exceptions::NoSuchJob, name) unless handler
58
+
59
+ begin
60
+ if defined? @before_handlers and @before_handlers.respond_to? :each
61
+ @before_handlers.each do |block|
62
+ block.call(name)
63
+ end
64
+ end
65
+ handler.call(args)
66
+ end
67
+
68
+ job.delete
69
+
70
+ @in_progress = false
71
+ log_job_end(name)
72
+ rescue Beanstalk::NotConnected => e
73
+ failed_connection(e)
74
+ rescue SystemExit
75
+ raise
76
+ rescue => e
77
+ log_error exception_message(e)
78
+ job.bury rescue nil
79
+ log_job_end(name, 'failed') if @job_begun
80
+ if error_handler
81
+ if error_handler.arity == 1
82
+ error_handler.call(e)
83
+ else
84
+ error_handler.call(e, name, args)
85
+ end
86
+ end
87
+ end
88
+
89
+ def failed_connection(e)
90
+ log_error exception_message(e)
91
+ log_error "*** Failed connection to #{beanstalk_url}"
92
+ log_error "*** Check that beanstalkd is running (or set a different BEANSTALK_URL)"
93
+ exit 1
94
+ end
95
+
96
+ def log_job_begin(name, args)
97
+ args_flat = unless args.empty?
98
+ '(' + args.inject([]) do |accum, (key,value)|
99
+ accum << "#{key}=#{value}"
100
+ end.join(' ') + ')'
101
+ else
102
+ ''
103
+ end
104
+
105
+ log [ "Working", name, args_flat ].join(' ')
106
+ @job_begun = Time.now
107
+ end
108
+
109
+ def log_job_end(name, failed=false)
110
+ ellapsed = Time.now - @job_begun
111
+ ms = (ellapsed.to_f * 1000).to_i
112
+ log "Finished #{name} in #{ms}ms #{failed ? ' (failed)' : ''}"
113
+ end
114
+
115
+ def log(msg)
116
+ puts msg
117
+ end
118
+
119
+ def log_error(msg)
120
+ STDERR.puts msg
121
+ end
122
+
123
+ def beanstalk
124
+ @beanstalk ||= Beanstalk::Pool.new(beanstalk_addresses)
125
+ end
126
+
127
+ def beanstalk_url
128
+ Chapman.beanstalk_url
129
+ end
130
+
131
+ def beanstalk_addresses
132
+ uris = beanstalk_url.split(/[\s,]+/)
133
+ uris.map {|uri| beanstalk_host_and_port(uri)}
134
+ end
135
+
136
+ def beanstalk_host_and_port(uri_string)
137
+ uri = URI.parse(uri_string)
138
+ raise(Chapman::Exceptions::BadURL, uri_string) if uri.scheme != 'beanstalk'
139
+ "#{uri.host}:#{uri.port || 11300}"
140
+ end
141
+
142
+ def exception_message(e)
143
+ msg = [ "Exception #{e.class} -> #{e.message}" ]
144
+
145
+ base = File.expand_path(Dir.pwd) + '/'
146
+ e.backtrace.each do |t|
147
+ msg << " #{File.expand_path(t).gsub(/#{base}/, '')}"
148
+ end
149
+
150
+ msg.join("\n")
151
+ end
152
+
153
+ def all_jobs
154
+ @handlers.keys
155
+ end
156
+
157
+ def error_handler
158
+ @error_handler
159
+ end
160
+
161
+ end
162
+ end
data/lib/chapman.rb ADDED
@@ -0,0 +1,123 @@
1
+ require 'chapman/version'
2
+ require 'chapman/exceptions'
3
+ require 'chapman/worker'
4
+
5
+ STDOUT.sync = true
6
+
7
+ module Chapman
8
+ extend self
9
+
10
+ def job(j, &block)
11
+ @@handlers ||= {}
12
+ @@handlers[j] = block
13
+ end
14
+
15
+ def before(&block)
16
+ @@before_handlers ||= []
17
+ @@before_handlers << block
18
+ end
19
+
20
+ def error(&blk)
21
+ @@error_handler = blk
22
+ end
23
+
24
+ def running
25
+ @@running ||= []
26
+ end
27
+
28
+ def soft_quit?
29
+ @@soft_quit ||= false
30
+ end
31
+
32
+ def soft_quit=(soft_quit)
33
+ @@soft_quit = soft_quit
34
+ end
35
+
36
+ def work(jobs=nil, thread_count=1)
37
+
38
+ # start a worker thread
39
+ thread_count.times do
40
+ w = Chapman::Worker.new()
41
+ t = Thread.new { w.work(jobs) }
42
+ running << {thread: t, worker: w}
43
+ end
44
+
45
+ # keep them alive
46
+ while not soft_quit?
47
+ maintain_workers
48
+ sleep 1
49
+ end
50
+
51
+ murder_workers!
52
+
53
+ reap_workers
54
+
55
+ log "SEPPUKU!!"
56
+ end
57
+
58
+ def maintain_workers
59
+ running.each_with_index do |runner, index|
60
+ if not runner[:thread].alive?
61
+ w = Creeper::Worker.new()
62
+ t = Thread.new do
63
+ w.work(jobs)
64
+ end
65
+ running[index] = {thread: t, worker: w}
66
+ end
67
+ end
68
+ end
69
+
70
+ def murder_workers!
71
+ running.each do |runner|
72
+ if runner[:worker].job_in_progress?
73
+ log "Murder [scheduling]"
74
+ else
75
+ log "Murder [now]"
76
+ runner[:thread].kill
77
+ end
78
+ end
79
+ end
80
+
81
+ def reap_workers
82
+ running.each do |runner|
83
+ runner[:thread].join
84
+ end
85
+ end
86
+
87
+ def log(msg)
88
+ puts msg
89
+ end
90
+
91
+ def log_error(msg)
92
+ STDERR.puts msg
93
+ end
94
+
95
+ def beanstalk_url
96
+ return @@url if defined?(@@url) and @@url
97
+ ENV['BEANSTALK_URL'] || 'beanstalk://localhost/'
98
+ end
99
+
100
+ def all_jobs
101
+ @@handlers.keys
102
+ end
103
+
104
+ def job_handlers
105
+ @@handlers ||= {}
106
+ end
107
+
108
+ def before_handlers
109
+ @@before_handlers ||= []
110
+ end
111
+
112
+ def error_handler
113
+ @@error_handler ||= nil
114
+ end
115
+
116
+ def reset!
117
+ @@soft_quit = false
118
+ @@running = []
119
+ @@handlers = nil
120
+ @@before_handlers = nil
121
+ @@error_handler = nil
122
+ end
123
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chapman
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tyler Flint
9
+ - Lyon Hill
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-05-26 00:00:00.000000000 Z
14
+ dependencies: []
15
+ description: Like stalker, but can stalk concurrently
16
+ email:
17
+ - tylerflint@gmail.com
18
+ - lyondhill@gmail.com
19
+ executables: []
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - .gitignore
24
+ - Gemfile
25
+ - README.md
26
+ - Rakefile
27
+ - chapman.gemspec
28
+ - lib/chapman.rb
29
+ - lib/chapman/exceptions.rb
30
+ - lib/chapman/version.rb
31
+ - lib/chapman/worker.rb
32
+ homepage: ''
33
+ licenses: []
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubyforge_project:
52
+ rubygems_version: 1.8.20
53
+ signing_key:
54
+ specification_version: 3
55
+ summary: Takes the concept of stalker and introduces a thread pool. It's great for
56
+ scenarios where tasks are light and mostly IO bound.
57
+ test_files: []
58
+ has_rdoc: