labor 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) Brett Buddin
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # Labor
2
+
3
+ A wrapper for "gearman-ruby" which provides an easy mechanism for managing your jobs. Jobs are just Ruby classes/modules which respond to the `perform` method.
4
+
5
+ ## Get Started
6
+
7
+ Basic `Rakefile`:
8
+
9
+ require 'labor'
10
+ require 'labor/tasks'
11
+
12
+ class TestJob
13
+ def self.perform
14
+ puts "Hello, world!"
15
+
16
+ true
17
+ end
18
+ end
19
+
20
+ class AnotherTest
21
+ def self.perform
22
+ puts "Hello, world... again!"
23
+
24
+ true
25
+ end
26
+ end
27
+
28
+ Labor.servers = ["10.0.0.1:4370", "10.0.0.2:4370"]
29
+
30
+ Let's run the worker:
31
+
32
+ $ export ABILITIES=test-job,another-test
33
+ $ rake labor:work
34
+
35
+ The `ABILITIES` environment variable can accept multiple job names; each seperated by a comma.
36
+
37
+ ## Configuration
38
+
39
+ Labor also allows you to load in a configuration file along with your worker. This configuration file is written in Ruby and offers a simple DSL (similar to the one present in Capistrano) for setting variables.
40
+
41
+ Configs look like this:
42
+
43
+ # settings.rb
44
+
45
+ set :foo, "bar"
46
+ set :i_am, "at the #{bar}"
47
+
48
+ group :credentials do
49
+ set :username, "murkturgler"
50
+ set :password, "supersecret"
51
+ end
52
+
53
+
54
+ The config file would then be loaded in like this:
55
+
56
+ Labor.config File.join(File.dirname(__FILE__), 'settings.rb')
57
+
58
+ When the worker is started up, Labor will load this config into the `@config` instance variable of your job class/module. You'll then be able to retrieve keys from the config within your job.
59
+
60
+ class TestJob
61
+ def self.perform
62
+ puts "Hey! I'm #{@config[:i_am]}"
63
+ #=> "Hey! I'm at the bar"
64
+
65
+ puts "Here's my username: #{@config.credentials.username}"
66
+ #=> "Here's my username: murkturgler"
67
+
68
+ true
69
+ end
70
+ end
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ begin
2
+ require "jeweler"
3
+
4
+ Jeweler::Tasks.new do |gem|
5
+ gem.name = "labor"
6
+ gem.version = "0.1"
7
+ gem.summary = "More portable jobs for Gearman workers."
8
+ gem.description = <<-desc
9
+ Wrapper for gearman-ruby that provides a different, and more portable, way of defining jobs.
10
+ desc
11
+ gem.email = "brett@intraspirit.net"
12
+ gem.homepage = "http://github.com/brettbuddin/labor"
13
+ gem.date = Time.now.strftime('%Y-%m-%d')
14
+ gem.authors = ["Brett Buddin"]
15
+ gem.files = %w( README.md Rakefile LICENSE )
16
+ gem.files += Dir["*", "{lib}/**/*"]
17
+ gem.add_dependency "gearman-ruby"
18
+ gem.add_dependency "json"
19
+
20
+ gem.has_rdoc = false
21
+ end
22
+
23
+ Jeweler::GemcutterTasks.new
24
+ rescue LoadError
25
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
26
+ end
27
+
28
+ begin
29
+ require 'rspec/core/rake_task'
30
+
31
+ RSpec::Core::RakeTask.new('spec') do |t|
32
+ t.pattern = FileList['spec/**/*_spec.rb']
33
+ end
34
+
35
+ task :test do
36
+ Rake::Task['spec'].invoke
37
+ end
38
+ end
data/labor.gemspec ADDED
@@ -0,0 +1,61 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{labor}
8
+ s.version = "0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Brett Buddin"]
12
+ s.date = %q{2011-02-19}
13
+ s.description = %q{ Wrapper for gearman-ruby that provides a different, and more portable, way of defining jobs.
14
+ }
15
+ s.email = %q{brett@intraspirit.net}
16
+ s.extra_rdoc_files = [
17
+ "LICENSE",
18
+ "README.md"
19
+ ]
20
+ s.files = [
21
+ "LICENSE",
22
+ "README.md",
23
+ "Rakefile",
24
+ "labor.gemspec",
25
+ "lib/labor.rb",
26
+ "lib/labor/config.rb",
27
+ "lib/labor/core_ext.rb",
28
+ "lib/labor/helpers.rb",
29
+ "lib/labor/tasks.rb",
30
+ "lib/labor/version.rb",
31
+ "lib/labor/worker.rb"
32
+ ]
33
+ s.homepage = %q{http://github.com/brettbuddin/labor}
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.7}
36
+ s.summary = %q{More portable jobs for Gearman workers.}
37
+ s.test_files = [
38
+ "spec/config_spec.rb",
39
+ "spec/files/sample_config.rb",
40
+ "spec/labor_spec.rb",
41
+ "spec/spec_helper.rb",
42
+ "spec/worker_spec.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
+ s.add_runtime_dependency(%q<gearman-ruby>, [">= 0"])
51
+ s.add_runtime_dependency(%q<json>, [">= 0"])
52
+ else
53
+ s.add_dependency(%q<gearman-ruby>, [">= 0"])
54
+ s.add_dependency(%q<json>, [">= 0"])
55
+ end
56
+ else
57
+ s.add_dependency(%q<gearman-ruby>, [">= 0"])
58
+ s.add_dependency(%q<json>, [">= 0"])
59
+ end
60
+ end
61
+
data/lib/labor.rb ADDED
@@ -0,0 +1,58 @@
1
+ require 'gearman'
2
+ require 'json'
3
+
4
+ require 'labor/version'
5
+ require 'labor/core_ext'
6
+ require 'labor/helpers'
7
+ require 'labor/worker'
8
+ require 'labor/config'
9
+
10
+ module Labor
11
+ # Sets the server addresses that we'd like to connect to.
12
+ #
13
+ # servers - The String address of the server or an Array
14
+ # of server addresses.
15
+ #
16
+ # Examples
17
+ #
18
+ # Labor.servers '10.3.4.19:4730'
19
+ # Labor.servers ['10.3.4.19:4730', '10.3.4.20:4730']
20
+ #
21
+ # Returns the Array of server addresses or String if only one was given.
22
+ def self.servers=(servers)
23
+ @servers = servers
24
+ end
25
+
26
+ # Returns the list of server addresses we'd like to connect to.
27
+ #
28
+ # Returns the Array of server addresses. Defaults to localhost:4730
29
+ # if nothing has been set.
30
+ def self.servers
31
+ @servers || ["localhost:#{Gearman::Util::DEFAULT_PORT}"]
32
+ end
33
+
34
+ # Loads in a config file.
35
+ #
36
+ # file_name - The String path to the file.
37
+ #
38
+ # Examples
39
+ #
40
+ # Labor.config 'settings.rb'
41
+ #
42
+ # Returns the Labor::Config instance.
43
+ def self.config=(file_name)
44
+ @config = Labor::Config.new.load file_name
45
+ end
46
+
47
+ # Returns the configuration object for our application.
48
+ #
49
+ # Returns the Labor::Config instance. Defaults to a new config
50
+ # if nothing has been set.
51
+ def self.config
52
+ @config ||= Labor::Config.new
53
+ end
54
+
55
+ class << self
56
+ attr_accessor :verbose
57
+ end
58
+ end
@@ -0,0 +1,103 @@
1
+ # Configuration manager for the worker. Treat it like a Hash.
2
+ # A hash that has a tiny DSL for setting values to it and
3
+ # supports grouping.
4
+ #
5
+ # For example:
6
+ #
7
+ # config = Config.new
8
+ # config[:foo] = "bar"
9
+ # config.set :hello, "world"
10
+ # config.group :a_group do
11
+ # set :three_two_one, "let's jam!"
12
+ # end
13
+ #
14
+ # config.foo #=> "bar"
15
+ # config.hello #=> "world"
16
+ # config.a_group.three_two_one #=> "let's jam"
17
+ #
18
+ module Labor
19
+ class Config
20
+ include Enumerable
21
+
22
+ def initialize
23
+ @config = {}
24
+ end
25
+
26
+ # Loads a config file.
27
+ #
28
+ # file_name - The String path to the file.
29
+ #
30
+ # Returns Config instance.
31
+ def load(file_name)
32
+ instance_eval(File.read(file_name))
33
+ self
34
+ end
35
+
36
+ # Assigns a value to a key.
37
+ #
38
+ # key - The Symbol key name.
39
+ # value - Anything you'd like to assign to the key.
40
+ #
41
+ # Returns the value of the key.
42
+ def set(key, value)
43
+ @config[key.to_sym] = value
44
+ end
45
+ alias_method :[]=, :set
46
+
47
+ # Retreives the value of a key.
48
+ #
49
+ # key - The Symbol key name.
50
+ #
51
+ # Returns the value of the key.
52
+ def get(key)
53
+ @config[key.to_sym]
54
+ end
55
+ alias_method :[], :get
56
+
57
+ # Deletes a key from the config.
58
+ #
59
+ # key - The Symbol key name.
60
+ #
61
+ # Returns the value of the key.
62
+ def delete(key)
63
+ @config.delete key.to_sym
64
+ end
65
+
66
+ # Checks if a key is set in the config.
67
+ #
68
+ # key - The Symbol key name.
69
+ #
70
+ # Returns a Boolean of whether or not the key exists.
71
+ def exists?(key)
72
+ @config.has_key? key.to_sym
73
+ end
74
+
75
+ # Call a block for each key in the config.
76
+ #
77
+ # block - The Block to execute for each element.
78
+ #
79
+ # Returns a Boolean of whether or not the key exists.
80
+ def each(&block)
81
+ @config.each(&block)
82
+ end
83
+
84
+
85
+ # Checks if a key is set in the config.
86
+ #
87
+ # key - The Symbol key name
88
+ #
89
+ # Returns a Boolean of whether or not the key exists.
90
+ def group(key, &block)
91
+ @config[key.to_sym] ||= Config.new
92
+ @config[key.to_sym].instance_eval(&block) if block_given?
93
+ end
94
+
95
+ private
96
+
97
+ def method_missing(sym, *args)
98
+ if args.length == 0 && @config.has_key?(sym)
99
+ get(sym)
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].each do |path|
2
+ require "labor/core_ext/#{File.basename(path, '.rb')}"
3
+ end
@@ -0,0 +1,30 @@
1
+ module Labor
2
+ module Helpers
3
+ def classify(word)
4
+ word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
5
+ end
6
+
7
+ def constantize(camel_cased_word)
8
+ camel_cased_word = camel_cased_word.to_s
9
+
10
+ if camel_cased_word.include?('-')
11
+ camel_cased_word = classify(camel_cased_word)
12
+ end
13
+
14
+ names = camel_cased_word.split('::')
15
+ names.shift if names.empty? || names.first.empty?
16
+
17
+ constant = Object
18
+ names.each do |name|
19
+ constant = constant.const_get(name) || constant.const_missing(name)
20
+ end
21
+ constant
22
+ end
23
+
24
+ def log(str)
25
+ if Labor.verbose
26
+ puts "#{Time.now.strftime('%I:%M:%S %d-%m-%Y')}: #{str}"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ # Rake tasks for Labor
2
+ #
3
+ # require 'labor/tasks'
4
+
5
+ namespace :labor do
6
+ task :work do
7
+ require 'labor'
8
+
9
+ abilities = ENV['ABILITIES'].to_s.split(',')
10
+
11
+ unless abilities.empty?
12
+ worker = Labor::Worker.new *abilities
13
+ else
14
+ abort "Please set ABILITIES environment variable (e.g. ABILITIES=say-hai,say-hello)"
15
+ end
16
+
17
+ if ENV['PIDFILE']
18
+ File.open(ENV['PIDFILE'], 'w') { |f| f << Process.pid.to_s }
19
+ end
20
+ Labor.verbose = ENV['VERBOSE']
21
+
22
+ worker.work
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module Labor
2
+ VERSION = Version = 0.1
3
+ end
@@ -0,0 +1,78 @@
1
+ module Labor
2
+ class Worker
3
+ include Helpers
4
+
5
+ def initialize(*abilities)
6
+ @worker = Gearman::Worker.new(Labor.servers)
7
+ add_abilities(abilities)
8
+ end
9
+
10
+ # Starts the work loop.
11
+ #
12
+ # Returns nothing.
13
+ def work
14
+ register_signals
15
+ loop { @worker.work or break }
16
+ end
17
+
18
+ private
19
+
20
+ # Registers abilities with the worker and announces them to the server.
21
+ #
22
+ # abilities - The Array of ability strings.
23
+ #
24
+ # Returns nothing.
25
+ def add_abilities(abilities)
26
+ abilities.each do |ability|
27
+ klass = constantize(classify(ability))
28
+ klass.instance_variable_set(:@config, Labor.config)
29
+
30
+ @worker.add_ability(ability) do |data, job|
31
+ begin
32
+ payload = JSON.parse data
33
+ klass.perform(payload, job)
34
+ rescue Exception => e
35
+ log "Job failed: #{e.inspect}"
36
+ return false
37
+ end
38
+ end
39
+
40
+ if klass.respond_to?(:after_perform)
41
+ @worker.after_ability(ability) do |result, data|
42
+ begin
43
+ payload = JSON.parse data
44
+ klass.after_perform(payload, result)
45
+ rescue Exception => e
46
+ log "After job failed: #{e.inspect}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ # Sets the procline of the program. You can observe it via `top`.
54
+ #
55
+ # str - The String to be assigned to the procline.
56
+ #
57
+ # Returns the String assigned to the procline.
58
+ def procline=(str)
59
+ $0 = "labor: #{str}"
60
+ end
61
+
62
+ # Registers trapping signals with the worker.
63
+ #
64
+ # Returns nothing.
65
+ def register_signals
66
+ %w(INT TERM).each do |signal|
67
+ trap signal do
68
+ @worker.worker_enabled = false
69
+ if @worker.status == :waiting
70
+ trap signal, "DEFAULT"
71
+ exit
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,80 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Labor::Config do
4
+ before(:each) do
5
+ @config = Labor::Config.new
6
+ end
7
+
8
+ it "loads config from a file" do
9
+ @config.load File.join(File.dirname(__FILE__), 'files', 'sample_config.rb')
10
+ @config.get(:foo).should == "bar"
11
+ end
12
+
13
+ it "allows write access to a key" do
14
+ @config.set(:foo, "bar")
15
+ @config.get(:foo).should == "bar"
16
+ end
17
+
18
+ it "allows read access to a key" do
19
+ @config.set(:foo, "bar")
20
+ @config.get(:foo).should == "bar"
21
+ end
22
+
23
+ it "allows read access to keys via bracket method" do
24
+ @config.set(:foo, "bar")
25
+ @config[:foo].should == "bar"
26
+ end
27
+
28
+ it "provides access to keys using methods" do
29
+ @config.set(:foo, "bar")
30
+ @config.foo.should == "bar"
31
+ end
32
+
33
+ it "allows write access to keys via bracket method" do
34
+ @config[:foo] = "bar"
35
+ @config.get(:foo).should == "bar"
36
+ end
37
+
38
+ it "allows deletion of a key" do
39
+ @config.set(:foo, "a noob")
40
+ @config.delete(:foo)
41
+ @config.get(:foo).should be_nil
42
+ end
43
+
44
+ it "allows checking existence of a key" do
45
+ @config.set(:foo, "bar")
46
+ @config.exists?(:foo)
47
+ end
48
+
49
+ it "have each defined" do
50
+ @config.respond_to?(:each)
51
+ end
52
+
53
+ it "allow configs to use keys as in-line variables" do
54
+ @config.instance_eval <<-CONFIG
55
+ set :foo, "bar"
56
+ set :i_am, "at the \#{foo}"
57
+ CONFIG
58
+ @config.get(:i_am).should == "at the bar"
59
+ end
60
+
61
+ describe :grouping do
62
+ before(:each) do
63
+ @config.instance_eval <<-CONFIG
64
+ group :test do
65
+ set :foo, "bar"
66
+ set :i_am, "at the \#{foo}"
67
+ end
68
+ CONFIG
69
+ end
70
+
71
+ it "can retreive a key from a group" do
72
+ @config.get(:test).get(:foo).should == "bar"
73
+ end
74
+
75
+ it "can assign a key to a group" do
76
+ @config.test[:omg] = "wtf"
77
+ @config.get(:test).get(:omg).should == "wtf"
78
+ end
79
+ end
80
+ end
@@ -0,0 +1 @@
1
+ set :foo, "bar"
@@ -0,0 +1,15 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Labor do
4
+ describe :servers do
5
+ it "assigns a single server" do
6
+ Labor.servers = "10.0.0.1:4370"
7
+ Labor.servers.should == "10.0.0.1:4370"
8
+ end
9
+
10
+ it "assigns a list of servers" do
11
+ Labor.servers = ["10.0.0.1:4370", "10.0.0.2:4370"]
12
+ Labor.servers.should == ["10.0.0.1:4370", "10.0.0.2:4370"]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift File.expand_path("../lib", File.dirname(__FILE__))
2
+
3
+ require 'rspec'
4
+ require 'labor'
@@ -0,0 +1,18 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ class Test
4
+ def self.perform
5
+ puts "Hello"
6
+ end
7
+ end
8
+
9
+ describe Labor::Worker do
10
+ before(:each) do
11
+ @worker = Labor::Worker.new 'test'
12
+ end
13
+
14
+ it "assigns abilities" do
15
+ real_worker = @worker.instance_variable_get(:@worker)
16
+ real_worker.instance_variable_get(:@abilities).should have_key("test")
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: labor
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ version: "0.1"
9
+ platform: ruby
10
+ authors:
11
+ - Brett Buddin
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+
16
+ date: 2011-02-19 00:00:00 -05:00
17
+ default_executable:
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: gearman-ruby
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: json
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ version: "0"
43
+ type: :runtime
44
+ version_requirements: *id002
45
+ description: " Wrapper for gearman-ruby that provides a different, and more portable, way of defining jobs.\n"
46
+ email: brett@intraspirit.net
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - LICENSE
53
+ - README.md
54
+ files:
55
+ - LICENSE
56
+ - README.md
57
+ - Rakefile
58
+ - labor.gemspec
59
+ - lib/labor.rb
60
+ - lib/labor/config.rb
61
+ - lib/labor/core_ext.rb
62
+ - lib/labor/helpers.rb
63
+ - lib/labor/tasks.rb
64
+ - lib/labor/version.rb
65
+ - lib/labor/worker.rb
66
+ - spec/config_spec.rb
67
+ - spec/files/sample_config.rb
68
+ - spec/labor_spec.rb
69
+ - spec/spec_helper.rb
70
+ - spec/worker_spec.rb
71
+ has_rdoc: true
72
+ homepage: http://github.com/brettbuddin/labor
73
+ licenses: []
74
+
75
+ post_install_message:
76
+ rdoc_options: []
77
+
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ segments:
94
+ - 0
95
+ version: "0"
96
+ requirements: []
97
+
98
+ rubyforge_project:
99
+ rubygems_version: 1.3.7
100
+ signing_key:
101
+ specification_version: 3
102
+ summary: More portable jobs for Gearman workers.
103
+ test_files:
104
+ - spec/config_spec.rb
105
+ - spec/files/sample_config.rb
106
+ - spec/labor_spec.rb
107
+ - spec/spec_helper.rb
108
+ - spec/worker_spec.rb