Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
contains implementation of SQSJobQueue
- Loading branch information
David Dawson
committed
Jan 11, 2012
0 parents
commit 50f9077
Showing
17 changed files
with
921 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
gems/ | ||
gems_dev/ | ||
.bundle | ||
*.swp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
= Simple Job | ||
|
||
== Version 0.0.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
source 'http://rubygems.org' | ||
|
||
gem 'activemodel', '~> 3.0', :require => nil | ||
gem 'activesupport', '~> 3.0', :require => nil | ||
gem 'aws-sdk', '~> 1.2', :require => nil | ||
|
||
group :rake do | ||
gem 'simple_gem', :require => 'tasks/simple_gem' | ||
end | ||
|
||
group :test do | ||
gem 'rspec', '~> 2.8' | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
GEM | ||
remote: http://rubygems.org/ | ||
specs: | ||
activemodel (3.1.3) | ||
activesupport (= 3.1.3) | ||
builder (~> 3.0.0) | ||
i18n (~> 0.6) | ||
activesupport (3.1.3) | ||
multi_json (~> 1.0) | ||
aws-sdk (1.2.6) | ||
httparty (~> 0.7) | ||
json (~> 1.4) | ||
nokogiri (>= 1.4.4) | ||
uuidtools (~> 2.1) | ||
builder (3.0.0) | ||
diff-lcs (1.1.3) | ||
httparty (0.8.1) | ||
multi_json | ||
multi_xml | ||
i18n (0.6.0) | ||
json (1.6.4) | ||
multi_json (1.0.4) | ||
multi_xml (0.4.1) | ||
nokogiri (1.5.0) | ||
rake (0.9.2.2) | ||
rspec (2.8.0) | ||
rspec-core (~> 2.8.0) | ||
rspec-expectations (~> 2.8.0) | ||
rspec-mocks (~> 2.8.0) | ||
rspec-core (2.8.0) | ||
rspec-expectations (2.8.0) | ||
diff-lcs (~> 1.1.2) | ||
rspec-mocks (2.8.0) | ||
simple_gem (0.0.1) | ||
activesupport (~> 3.0) | ||
rake (>= 0.8.7) | ||
rspec (~> 2.8) | ||
uuidtools (2.1.2) | ||
|
||
PLATFORMS | ||
ruby | ||
|
||
DEPENDENCIES | ||
activemodel (~> 3.0) | ||
activesupport (~> 3.0) | ||
aws-sdk (~> 1.2) | ||
rspec (~> 2.8) | ||
simple_gem |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Copyright (c) 2012 RevPAR Collective, Inc. | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
= Simple Job | ||
|
||
A gem containing libraries that support running background jobs or tasks. It's designed to make it easy to: | ||
|
||
* Define a job | ||
* Enqueue a job | ||
* Poll for and execute jobs | ||
|
||
It is architected to support multiple types of queue implementations, but currently, it only includes an implementation using AWS SQS (http://aws.amazon.com/sqs/). Alternate queue implementations could be plugged in by the client using lib/simple_job/sqs_job_queue.rb as an example. | ||
|
||
The AWS SQS queue implementation requires the aws-sdk gem, which must be initialized (by calling AWS.config) for this API to be capable of enqueuing or polling for jobs. | ||
|
||
|
||
== Queue Configuration | ||
|
||
Queue configuration must be done by both the client and the server. | ||
|
||
Only the queues that will be used by each must be defined (a client may configure a subset of the queues used by the server, so long as it configures all the queues that it uses). | ||
|
||
SQSJobQueue is the queue implementation used by default. This may be overridden by calling SimpleJob::JobQueue.config :implementation => 'alternate_queue_implementation_identifier' | ||
|
||
=== Minimal configuration - specify queue prefix and define one default queue | ||
|
||
SimpleJob::SQSJobQueue.config :queue_prefix => 'my-job' | ||
SimpleJob::SQSJobQueue.define_queue 'normal', :default => true | ||
|
||
=== Complex configuration with explicit queue implementation, non-rails-defined environment, and multiple queues | ||
|
||
SimpleJob::JobQueue.config :implementation => 'sqs' | ||
SimpleJob::SQSJobQueue.config :queue_prefix => 'stash-job', :environment => 'production' | ||
SimpleJob::SQSJobQueue.define_queue 'normal', :visibility_timeout => 60, :default => true | ||
SimpleJob::SQSJobQueue.define_queue 'high-priority', :visibility_timeout => 10 | ||
SimpleJob::SQSJobQueue.define_queue 'long-running', :visibility_timeout => 3600 | ||
|
||
|
||
== Job Definition | ||
|
||
class FooSender | ||
include SimpleJob::JobDefinition | ||
|
||
simple_job_attribute :target, :foo_content # defines getters/setters for each, and | ||
# adds them to serialized message | ||
|
||
validates :target, :presence => true # standard ActiveModel validation | ||
|
||
def execute | ||
puts "#{foo_content} -> #{target}" | ||
end | ||
end | ||
|
||
|
||
== Job Client Usage | ||
|
||
=== Typical usage of default queue | ||
|
||
You may call #enqueue with no arguments, in which case JobQueue.default will be used. | ||
|
||
f = FooSender.new(:target => 'joe', :foo_content => 'foo!') # can also assign attributes with f.target=, f.foo_content= | ||
if f.enqueue(queue) | ||
puts 'i just sent some foo to joe!' | ||
else | ||
puts "the following errors occurred: #{f.errors.full_messages.join('; ')}" | ||
end | ||
|
||
json = f.to_json # { "type": "foo_sender", "version": "1", "data": { "target": "joe", "foo_content": "foo!" } } | ||
f_copy = FooSender.new.from_json(json) | ||
|
||
=== Simple usage with explicit queue | ||
|
||
To explicitly specify the queue to use, simply specify its type when calling enqueue. | ||
|
||
f = FooSender.new | ||
f.target = 'bob' | ||
f.foo_content = 'foo!' | ||
f.enqueue!('normal') # raises exception if operation fails | ||
|
||
=== Queue configuration for multiple enqueue operations | ||
|
||
Alternatively, the queue may be attached to the job upfront for multiple enqueue operations: | ||
|
||
FooSender.job_queue(SimpleJob::JobQueue['high-priority']) | ||
f1 = FooSender.new(:target => 'cookie monster', :foo_content => 'cookies and milk') | ||
f1.enqueue | ||
f2 = FooSender.new(:target => 'oscar the grouch', :foo_content => 'pizza') | ||
f2.enqueue | ||
|
||
|
||
== Job Server Usage | ||
|
||
JobQueue.default.poll do |message| | ||
FooSender | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
require 'bundler' | ||
Bundler.require(:rake) | ||
|
||
# Configure gem building | ||
require File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'simple_job', 'version')) | ||
SimpleGem.current_version = SimpleJob::VERSION | ||
SimpleGem.current_gemspec = 'simple_job.gemspec' | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module SimpleJob | ||
|
||
autoload :JobDefinition, 'simple_job/job_definition' | ||
autoload :JobQueue, 'simple_job/job_queue' | ||
autoload :SQSJobQueue, 'simple_job/sqs_job_queue' | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
require 'active_model' | ||
require 'active_support/inflector' | ||
|
||
module SimpleJob | ||
module JobDefinition | ||
|
||
RESERVED_ATTRIBUTES = [ :type, :version, :data ] | ||
|
||
def self.included(klass) | ||
|
||
klass.extend(ClassMethods) | ||
|
||
klass.class_eval do | ||
include ::ActiveModel::Validations | ||
include ::ActiveModel::Serializers::JSON | ||
end | ||
|
||
klass.include_root_in_json = false | ||
klass.register_simple_job | ||
|
||
end | ||
|
||
# should be overridden by including classes | ||
if !method_defined?(:execute) | ||
def execute | ||
end | ||
end | ||
|
||
def attributes | ||
{ | ||
'type' => type, | ||
'version' => version, | ||
'data' => data, | ||
} | ||
end | ||
|
||
def attributes=(attributes) | ||
attributes.each do |key, value| | ||
send("#{key}=", value) | ||
end | ||
end | ||
|
||
def data | ||
@data ||= {} | ||
self.class.simple_job_attributes.each do |attribute| | ||
@data[attribute.to_s] ||= nil | ||
end | ||
@data | ||
end | ||
|
||
def data=(data) | ||
self.attributes = data | ||
end | ||
|
||
def type | ||
self.class.definition[:type] | ||
end | ||
|
||
def type=(type) | ||
if type.to_sym != self.type | ||
raise "tried to deserialize object with type #{type}, but this object only " + | ||
"supports type: #{self.type}" | ||
end | ||
end | ||
|
||
def versions | ||
self.class.definition[:versions] | ||
end | ||
|
||
def version | ||
versions.first | ||
end | ||
|
||
def version=(version) | ||
if !versions.include?(version.to_s) | ||
raise "tried to deserialize object with version #{version}, but this object " + | ||
"only supports versions: #{versions.join(", ")}" | ||
end | ||
end | ||
|
||
def enqueue(queue_type = nil) | ||
if valid? | ||
queue = (queue_type && JobQueue[queue_type]) || self.class.job_queue || JobQueue.default | ||
queue.enqueue(self.to_json) | ||
else | ||
false | ||
end | ||
end | ||
|
||
def enqueue!(queue_type = nil) | ||
enqueue(queue_type) || raise("object is not valid: #{errors.full_messages.join('; ')}") | ||
end | ||
|
||
def read_simple_job_attribute(attribute) | ||
data[attribute.to_s] | ||
end | ||
|
||
def write_simple_job_attribute(attribute, value) | ||
data[attribute.to_s] = value | ||
end | ||
|
||
def initialize(attributes = {}) | ||
attributes.each do |key, value| | ||
send("#{key}=", value) | ||
end | ||
end | ||
|
||
def self.job_definition_class_for(type, version) | ||
@job_definitions.each do |definition| | ||
if (definition[:type] == type.to_sym) && (definition[:versions].include?(version)) | ||
return definition[:class] | ||
end | ||
end | ||
nil | ||
end | ||
|
||
def self.job_definitions | ||
@job_definitions ||= [] | ||
end | ||
|
||
private | ||
|
||
module ClassMethods | ||
|
||
def definition | ||
@definition | ||
end | ||
|
||
def register_simple_job(options = {}) | ||
default_type = self.name.underscore.to_sym | ||
|
||
@definition = { | ||
:class => self, | ||
:type => default_type, | ||
:versions => [ '1' ], | ||
}.merge(options) | ||
|
||
@definition[:type] = @definition[:type].to_sym | ||
@definition[:versions] = Array(@definition[:versions]) | ||
@definition[:versions].collect! { |value| value.to_s } | ||
|
||
::SimpleJob::JobDefinition.job_definitions.delete_if { |item| item[:type] == default_type } | ||
::SimpleJob::JobDefinition.job_definitions << @definition | ||
end | ||
|
||
def job_queue(queue_type = nil) | ||
@job_queue = JobQueue[queue_type] if queue_type | ||
@job_queue | ||
end | ||
|
||
def simple_job_attributes | ||
@simple_job_attributes ||= [] | ||
end | ||
|
||
def simple_job_attribute(*attributes) | ||
attributes.each do |attribute| | ||
attribute = attribute.to_sym | ||
|
||
if RESERVED_ATTRIBUTES.include?(attribute) | ||
raise "attempted to declare reserved attribute: #{attribute}" | ||
end | ||
|
||
simple_job_attributes << attribute | ||
|
||
class_eval <<-__EOF__ | ||
def #{attribute} | ||
read_simple_job_attribute(:#{attribute}) | ||
end | ||
def #{attribute}=(value) | ||
write_simple_job_attribute(:#{attribute}, value) | ||
end | ||
__EOF__ | ||
end | ||
end | ||
|
||
end | ||
|
||
end | ||
end |
Oops, something went wrong.