airbrush 0.0.2
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/History.txt +4 -0
- data/Manifest.txt +55 -0
- data/README.txt +39 -0
- data/Rakefile +4 -0
- data/bin/airbrush +61 -0
- data/bin/airbrush-example-client +60 -0
- data/config/hoe.rb +71 -0
- data/config/requirements.rb +17 -0
- data/lib/airbrush/client.rb +53 -0
- data/lib/airbrush/core_ext/get_args.rb +94 -0
- data/lib/airbrush/core_ext/timestamped_logger.rb +10 -0
- data/lib/airbrush/handler.rb +18 -0
- data/lib/airbrush/listeners/listener.rb +45 -0
- data/lib/airbrush/listeners/memcache.rb +53 -0
- data/lib/airbrush/listeners/socket.rb +0 -0
- data/lib/airbrush/listeners/webservice.rb +0 -0
- data/lib/airbrush/processors/image/image_processor.rb +31 -0
- data/lib/airbrush/processors/image/profiles/cmyk-profile.icc +0 -0
- data/lib/airbrush/processors/image/profiles/srgb-profile.icc +0 -0
- data/lib/airbrush/processors/image/rmagick.rb +116 -0
- data/lib/airbrush/processors/processor.rb +43 -0
- data/lib/airbrush/publishers/http.rb +13 -0
- data/lib/airbrush/publishers/memcache.rb +24 -0
- data/lib/airbrush/publishers/publisher.rb +16 -0
- data/lib/airbrush/server.rb +20 -0
- data/lib/airbrush/version.rb +9 -0
- data/lib/airbrush.rb +30 -0
- data/log/debug.log +0 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/spec/airbrush/client_spec.rb +87 -0
- data/spec/airbrush/core_ext/get_args_spec.rb +0 -0
- data/spec/airbrush/handler_spec.rb +44 -0
- data/spec/airbrush/listeners/listener_spec.rb +18 -0
- data/spec/airbrush/listeners/memcache_spec.rb +131 -0
- data/spec/airbrush/processors/image/image_processor_spec.rb +56 -0
- data/spec/airbrush/processors/image/rmagick_spec.rb +179 -0
- data/spec/airbrush/processors/processor_spec.rb +110 -0
- data/spec/airbrush/publishers/memcache_spec.rb +46 -0
- data/spec/airbrush/publishers/publisher_spec.rb +17 -0
- data/spec/airbrush/server_spec.rb +57 -0
- data/spec/airbrush_spec.rb +9 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/rspec.rake +36 -0
- data/tasks/website.rake +17 -0
- data/website/index.html +11 -0
- data/website/index.txt +39 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +138 -0
- data/website/template.rhtml +48 -0
- metadata +161 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
bin/airbrush
|
6
|
+
bin/airbrush-example-client
|
7
|
+
config/hoe.rb
|
8
|
+
config/requirements.rb
|
9
|
+
lib/airbrush.rb
|
10
|
+
lib/airbrush/client.rb
|
11
|
+
lib/airbrush/core_ext/get_args.rb
|
12
|
+
lib/airbrush/core_ext/timestamped_logger.rb
|
13
|
+
lib/airbrush/handler.rb
|
14
|
+
lib/airbrush/listeners/listener.rb
|
15
|
+
lib/airbrush/listeners/memcache.rb
|
16
|
+
lib/airbrush/listeners/socket.rb
|
17
|
+
lib/airbrush/listeners/webservice.rb
|
18
|
+
lib/airbrush/processors/image/image_processor.rb
|
19
|
+
lib/airbrush/processors/image/profiles/cmyk-profile.icc
|
20
|
+
lib/airbrush/processors/image/profiles/srgb-profile.icc
|
21
|
+
lib/airbrush/processors/image/rmagick.rb
|
22
|
+
lib/airbrush/processors/processor.rb
|
23
|
+
lib/airbrush/publishers/http.rb
|
24
|
+
lib/airbrush/publishers/memcache.rb
|
25
|
+
lib/airbrush/publishers/publisher.rb
|
26
|
+
lib/airbrush/server.rb
|
27
|
+
lib/airbrush/version.rb
|
28
|
+
log/debug.log
|
29
|
+
script/destroy
|
30
|
+
script/generate
|
31
|
+
script/txt2html
|
32
|
+
setup.rb
|
33
|
+
spec/airbrush/client_spec.rb
|
34
|
+
spec/airbrush/core_ext/get_args_spec.rb
|
35
|
+
spec/airbrush/handler_spec.rb
|
36
|
+
spec/airbrush/listeners/listener_spec.rb
|
37
|
+
spec/airbrush/listeners/memcache_spec.rb
|
38
|
+
spec/airbrush/processors/image/image_processor_spec.rb
|
39
|
+
spec/airbrush/processors/image/rmagick_spec.rb
|
40
|
+
spec/airbrush/processors/processor_spec.rb
|
41
|
+
spec/airbrush/publishers/memcache_spec.rb
|
42
|
+
spec/airbrush/publishers/publisher_spec.rb
|
43
|
+
spec/airbrush/server_spec.rb
|
44
|
+
spec/airbrush_spec.rb
|
45
|
+
spec/spec.opts
|
46
|
+
spec/spec_helper.rb
|
47
|
+
tasks/deployment.rake
|
48
|
+
tasks/environment.rake
|
49
|
+
tasks/rspec.rake
|
50
|
+
tasks/website.rake
|
51
|
+
website/index.html
|
52
|
+
website/index.txt
|
53
|
+
website/javascripts/rounded_corners_lite.inc.js
|
54
|
+
website/stylesheets/screen.css
|
55
|
+
website/template.rhtml
|
data/README.txt
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
= AIRBRUSH
|
2
|
+
|
3
|
+
== DESCRIPTION
|
4
|
+
|
5
|
+
Airbrush is a distributed processing host for performing arbitrary tasks on a particular server.
|
6
|
+
Currently it supports using MemCache as the transport queue to communicate between an airbrush
|
7
|
+
server issuing a command and the server processing it.
|
8
|
+
|
9
|
+
Due to the distributed nature of MemCache, multiple Airbrush servers can be started across
|
10
|
+
many hosts, and each will monitor the MemCache incoming queue for results independently and
|
11
|
+
atomically.
|
12
|
+
|
13
|
+
Remote jobs are performed via a 'processor' installed as part of Airbrush, and is currently
|
14
|
+
configured in code in the server.rb source file (look for the handler definition/configuration). A
|
15
|
+
future enhancemnt could support automatic processor registration and/or a plugin architecture.
|
16
|
+
|
17
|
+
Actual communication between client and server via MemCache is done via a hash, that includes
|
18
|
+
a :command value, optional :args value and a uniqie :id value. The :id value is used to uniquely
|
19
|
+
identify this job, :command names the actual command to run on the remote server, and :params
|
20
|
+
is a hash of parameters that are passed to the command being executed.
|
21
|
+
|
22
|
+
Several conventions are used, the name of the command needs to match a method name on the
|
23
|
+
processor. The key names in the params hash need to match parameter names to the method being
|
24
|
+
invoked (and will be automatically assigned). The :id value is used to name a queue that the
|
25
|
+
client can poll for return values from the remote command, if required.
|
26
|
+
|
27
|
+
== USAGE
|
28
|
+
|
29
|
+
To use Airbrush, you will need a starling server installed and running:
|
30
|
+
|
31
|
+
$> starling -v -P /tmp/starling.pid -q /tmp/queue
|
32
|
+
|
33
|
+
and at least one Airbrush instance running, including your processor:
|
34
|
+
|
35
|
+
$> airbrush -v
|
36
|
+
|
37
|
+
To send a remote job to the Airbrush server, use the Airbrush::Client module located
|
38
|
+
in airbrush/client.rb. An example is airbrush-example-client, which sends an image to the
|
39
|
+
remote server to be turned into a set of previews.
|
data/Rakefile
ADDED
data/bin/airbrush
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Created on 2008-1-22.
|
4
|
+
# Copyright (c) 2008. All rights reserved.
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'rubygems'
|
8
|
+
rescue LoadError
|
9
|
+
# no rubygems to load, so we fail silently
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'optparse'
|
13
|
+
|
14
|
+
OPTIONS = {
|
15
|
+
:memcache => '127.0.0.1:22122',
|
16
|
+
:frequency => 2,
|
17
|
+
:verbose => false
|
18
|
+
}
|
19
|
+
MANDATORY_OPTIONS = %w( )
|
20
|
+
|
21
|
+
parser = OptionParser.new do |opts|
|
22
|
+
opts.banner = <<BANNER
|
23
|
+
Airbrush is a dedicated job queue based image processor. Commands are added to the queue via memcache, and results posted back
|
24
|
+
via memcache. This portion of airbrush is the server component, please see the examples are of the gem for how to post and
|
25
|
+
receive results using an example client.
|
26
|
+
|
27
|
+
Usage: #{File.basename($0)} [options]
|
28
|
+
|
29
|
+
Options are:
|
30
|
+
BANNER
|
31
|
+
opts.separator ""
|
32
|
+
opts.on("-m", "--memcache=HOST", String,
|
33
|
+
"The address of the memcache host to connect to",
|
34
|
+
"Default: #{OPTIONS[:memcache]}") { |OPTIONS[:memcache]| }
|
35
|
+
opts.on("-f", "--frequency=SECONDS", String,
|
36
|
+
"How often the remote memcache server is polled for incoming jobs, units of seconds",
|
37
|
+
"Default: #{OPTIONS[:frequency]}") { |OPTIONS[:frequency]| }
|
38
|
+
opts.on("-l", "--log=file", String,
|
39
|
+
"Specify where to send logging output",
|
40
|
+
"Default: stdout") { |OPTIONS[:log_target]| }
|
41
|
+
opts.on("-v", "--verbose",
|
42
|
+
"Verbose output") { |OPTIONS[:verbose]| }
|
43
|
+
opts.on("-h", "--help",
|
44
|
+
"Show this help message.") { puts opts; exit }
|
45
|
+
opts.parse!(ARGV)
|
46
|
+
|
47
|
+
if MANDATORY_OPTIONS && MANDATORY_OPTIONS.find { |option| OPTIONS[option.to_sym].nil? }
|
48
|
+
puts opts; exit
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# all good, lets go
|
53
|
+
|
54
|
+
require 'airbrush'
|
55
|
+
require 'daemons'
|
56
|
+
|
57
|
+
# Become a daemon
|
58
|
+
#Daemons.daemonize
|
59
|
+
|
60
|
+
server = Airbrush::Server.new(OPTIONS)
|
61
|
+
server.start
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Created on 2008-1-22.
|
4
|
+
# Copyright (c) 2008. All rights reserved.
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'rubygems'
|
8
|
+
rescue LoadError
|
9
|
+
# no rubygems to load, so we fail silently
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'optparse'
|
13
|
+
|
14
|
+
OPTIONS = {
|
15
|
+
:memcache => '127.0.0.1:22122',
|
16
|
+
:verbose => false
|
17
|
+
}
|
18
|
+
MANDATORY_OPTIONS = %w( image output )
|
19
|
+
|
20
|
+
parser = OptionParser.new do |opts|
|
21
|
+
opts.banner = <<BANNER
|
22
|
+
Example client for accessing the resize operation on a remote airbrush server, queued via memcache.
|
23
|
+
|
24
|
+
Usage: #{File.basename($0)} [options]
|
25
|
+
|
26
|
+
Options are:
|
27
|
+
BANNER
|
28
|
+
opts.separator ""
|
29
|
+
opts.on("-m", "--memcache=HOST", String,
|
30
|
+
"The address of the memcache host to connect to",
|
31
|
+
"Default: #{OPTIONS[:memcache]}") { |OPTIONS[:memcache]| }
|
32
|
+
opts.on("-i", "--image=FILENAME", String,
|
33
|
+
"Source input image") { |OPTIONS[:image]| }
|
34
|
+
opts.on("-o", "--output=FILENAME", String,
|
35
|
+
"Target output image") { |OPTIONS[:output]| }
|
36
|
+
opts.on("-v", "--verbose",
|
37
|
+
"Verbose output") { |OPTIONS[:verbose]| }
|
38
|
+
opts.on("-h", "--help",
|
39
|
+
"Show this help message.") { puts opts; exit }
|
40
|
+
opts.parse!(ARGV)
|
41
|
+
|
42
|
+
if MANDATORY_OPTIONS && MANDATORY_OPTIONS.find { |option| OPTIONS[option.to_sym].nil? }
|
43
|
+
puts opts; exit
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
raise "Source image does not exist: #{OPTIONS[:image]}" unless File.exists?(OPTIONS[:image])
|
48
|
+
|
49
|
+
# all good, lets go!
|
50
|
+
|
51
|
+
require 'airbrush'
|
52
|
+
|
53
|
+
client = Airbrush::Client.new(OPTIONS[:memcache])
|
54
|
+
puts "Sending #{OPTIONS[:image]} for preview processing"
|
55
|
+
results = client.process('generate-previews', :previews, :image => File.read(OPTIONS[:image]), :filename => OPTIONS[:image], :sizes => {:small => [300], :large => [600]})
|
56
|
+
results.each do |k,v|
|
57
|
+
next if k == :original
|
58
|
+
File.open("#{OPTIONS[:output]}-#{k}.jpg", 'w') { |f| f << v[:image] }
|
59
|
+
puts "Saved preview #{OPTIONS[:output]}-#{k}.jpg (#{v[:width]}x#{v[:height]})" if OPTIONS[:verbose]
|
60
|
+
end
|
data/config/hoe.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'airbrush/version'
|
2
|
+
|
3
|
+
AUTHOR = 'FIXME full name' # can also be an array of Authors
|
4
|
+
EMAIL = "FIXME email"
|
5
|
+
DESCRIPTION = "description of gem"
|
6
|
+
GEM_NAME = 'airbrush' # what ppl will type to install your gem
|
7
|
+
RUBYFORGE_PROJECT = 'airbrush' # The unix name for your project
|
8
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
9
|
+
DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
10
|
+
|
11
|
+
@config_file = "~/.rubyforge/user-config.yml"
|
12
|
+
@config = nil
|
13
|
+
RUBYFORGE_USERNAME = "unknown"
|
14
|
+
def rubyforge_username
|
15
|
+
unless @config
|
16
|
+
begin
|
17
|
+
@config = YAML.load(File.read(File.expand_path(@config_file)))
|
18
|
+
rescue
|
19
|
+
puts <<-EOS
|
20
|
+
ERROR: No rubyforge config file found: #{@config_file}
|
21
|
+
Run 'rubyforge setup' to prepare your env for access to Rubyforge
|
22
|
+
- See http://newgem.rubyforge.org/rubyforge.html for more details
|
23
|
+
EOS
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
end
|
27
|
+
RUBYFORGE_USERNAME.replace @config["username"]
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
REV = nil
|
32
|
+
# UNCOMMENT IF REQUIRED:
|
33
|
+
# REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
|
34
|
+
VERS = Airbrush::VERSION::STRING + (REV ? ".#{REV}" : "")
|
35
|
+
RDOC_OPTS = ['--quiet', '--title', 'airbrush documentation',
|
36
|
+
"--opname", "index.html",
|
37
|
+
"--line-numbers",
|
38
|
+
"--main", "README",
|
39
|
+
"--inline-source"]
|
40
|
+
|
41
|
+
class Hoe
|
42
|
+
def extra_deps
|
43
|
+
@extra_deps.reject! { |x| Array(x).first == 'hoe' }
|
44
|
+
@extra_deps
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Generate all the Rake tasks
|
49
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
50
|
+
hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
51
|
+
p.author = AUTHOR
|
52
|
+
p.description = DESCRIPTION
|
53
|
+
p.email = EMAIL
|
54
|
+
p.summary = DESCRIPTION
|
55
|
+
p.url = HOMEPATH
|
56
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
57
|
+
p.test_globs = ["test/**/test_*.rb"]
|
58
|
+
p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
|
59
|
+
|
60
|
+
# == Optional
|
61
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
62
|
+
p.extra_deps = [ [ 'starling', '>= 0.9.3' ], [ 'activesupport', '>= 2.0.2' ], [ 'ruby2ruby', '>= 1.1.8' ], [ 'daemons', '>= 1.0.9' ] ] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
|
63
|
+
|
64
|
+
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
|
69
|
+
PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
|
70
|
+
hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
|
71
|
+
hoe.rsync_args = '-av --delete --ignore-errors'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
include FileUtils
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
%w[rake hoe newgem rubigen].each do |req_gem|
|
6
|
+
begin
|
7
|
+
require req_gem
|
8
|
+
rescue LoadError
|
9
|
+
puts "This Rakefile requires the '#{req_gem}' RubyGem."
|
10
|
+
puts "Installation: gem install #{req_gem} -y"
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
$:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
|
16
|
+
|
17
|
+
require 'airbrush'
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'starling'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
module Airbrush
|
5
|
+
include Timeout
|
6
|
+
class AirbrushProcessingError < StandardError; end
|
7
|
+
|
8
|
+
class Client
|
9
|
+
DEFAULT_INCOMING_QUEUE = 'airbrush_incoming_queue'
|
10
|
+
DEFAULT_RESPONSE_TIMEOUT = 2.minutes
|
11
|
+
DEFAULT_QUEUE_VALIDITY = 0 # 10.minutes for the moment
|
12
|
+
|
13
|
+
attr_reader :host, :incoming_queue, :response_timeout, :queue_validity
|
14
|
+
|
15
|
+
def initialize(host, incoming_queue = DEFAULT_INCOMING_QUEUE, response_timeout = DEFAULT_RESPONSE_TIMEOUT, queue_validity = DEFAULT_QUEUE_VALIDITY)
|
16
|
+
@host = host
|
17
|
+
@server = Starling.new(@host)
|
18
|
+
@incoming_queue = incoming_queue
|
19
|
+
@response_timeout = response_timeout
|
20
|
+
@queue_validity = queue_validity.to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
def process(id, command, args = {})
|
24
|
+
raise 'No job id specified' unless id
|
25
|
+
raise 'No command specified' unless command
|
26
|
+
raise "Invalid arguments #{args}" unless args.is_a? Hash
|
27
|
+
|
28
|
+
send_and_receive(id, command, args)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def send_and_receive(id, command, args)
|
34
|
+
@server.set(@incoming_queue, { :id => id, :command => command, :args => args }, @queue_validity, false)
|
35
|
+
queue = unique_name(id)
|
36
|
+
Timeout::timeout(@response_timeout) do
|
37
|
+
response = @server.get(queue)
|
38
|
+
raise AirbrushProcessingError, format_error(response) if response.include? :exception
|
39
|
+
return response
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# REVISIT: share implementation with server?
|
44
|
+
def unique_name(id)
|
45
|
+
id.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
def format_error(response)
|
49
|
+
"#{response[:exception]}: #{response[:message]}"
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'ruby2ruby'
|
2
|
+
require 'parse_tree'
|
3
|
+
|
4
|
+
class ParseTreeArray < Array #:nodoc:
|
5
|
+
def self.translate(*args)
|
6
|
+
sexp = ::ParseTree.translate(*args)
|
7
|
+
# ParseTree.translate returns [nil] if called on an inherited method, so walk
|
8
|
+
# up the inheritance chain to find the class that the method was defined in
|
9
|
+
unless sexp.first
|
10
|
+
klass = args.first.ancestors.detect do |klass|
|
11
|
+
klass.public_instance_methods(false).include?(args.last.to_s)
|
12
|
+
end
|
13
|
+
sexp = ::ParseTree.translate(klass, args.last) if klass
|
14
|
+
end
|
15
|
+
self.new(sexp)
|
16
|
+
end
|
17
|
+
|
18
|
+
def deep_array_node(type = nil)
|
19
|
+
each do |node|
|
20
|
+
return ParseTreeArray.new(node) if node.is_a?(Array) && (!type || node[0] == type)
|
21
|
+
next unless node.is_a?(Array)
|
22
|
+
return ParseTreeArray.new(node).deep_array_node(type)
|
23
|
+
end
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def arg_nodes
|
28
|
+
self[1..-1].inject([]) do |sum,item|
|
29
|
+
sum << [item] unless item.is_a?(Array)
|
30
|
+
sum
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_args
|
35
|
+
if arg_node = deep_array_node(:args)
|
36
|
+
# method defined with def keyword
|
37
|
+
args = arg_node.arg_nodes
|
38
|
+
default_node = arg_node.deep_array_node(:block)
|
39
|
+
return args unless default_node
|
40
|
+
else
|
41
|
+
# assuming method defined with Module#define_method
|
42
|
+
return []
|
43
|
+
end
|
44
|
+
|
45
|
+
# if it was defined with def, and we found the default_node,
|
46
|
+
# that should bring us back to regularly scheduled programming..
|
47
|
+
|
48
|
+
lasgns = default_node[1..-1]
|
49
|
+
lasgns.each do |asgn|
|
50
|
+
args.assoc(asgn[1]) << eval(RubyToRuby.new.process(asgn[2]))
|
51
|
+
end
|
52
|
+
args
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
# Used in mapping controller arguments to the params hash.
|
58
|
+
# NOTE: You must have the 'ruby2ruby' gem installed for this to work.
|
59
|
+
#
|
60
|
+
# ==== Examples
|
61
|
+
# # In PostsController
|
62
|
+
# def show(id) #=> id is the same as params[:id]
|
63
|
+
module GetArgs
|
64
|
+
|
65
|
+
# ==== Returns
|
66
|
+
# Array:: Method arguments and their default values.
|
67
|
+
#
|
68
|
+
# ==== Examples
|
69
|
+
# class Example
|
70
|
+
# def hello(one,two="two",three)
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# def goodbye
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# Example.instance_method(:hello).get_args
|
78
|
+
# #=> [[:one], [:two, "two"], [:three, "three"]]
|
79
|
+
# Example.instance_method(:goodbye).get_args #=> nil
|
80
|
+
def get_args
|
81
|
+
klass, meth = self.to_s.split(/ /).to_a[1][0..-2].split("#")
|
82
|
+
# Remove stupidity for #<Method: Class(Object)#foo>
|
83
|
+
klass = $` if klass =~ /\(/
|
84
|
+
ParseTreeArray.translate(Object.const_get(klass), meth).get_args
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class UnboundMethod #:nodoc:
|
89
|
+
include GetArgs
|
90
|
+
end
|
91
|
+
|
92
|
+
class Method #:nodoc:
|
93
|
+
include GetArgs
|
94
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Airbrush
|
2
|
+
class Handler
|
3
|
+
attr_reader :processor, :publisher
|
4
|
+
|
5
|
+
def initialize(processor, publisher)
|
6
|
+
raise ArgumentError, 'no processor specified' unless processor
|
7
|
+
raise ArgumentError, 'no publisher specified' unless publisher
|
8
|
+
|
9
|
+
@processor = processor
|
10
|
+
@publisher = publisher
|
11
|
+
end
|
12
|
+
|
13
|
+
def process(id, command, args)
|
14
|
+
@publisher.publish id, @processor.dispatch(command, args)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Airbrush
|
2
|
+
module Listeners
|
3
|
+
class Listener
|
4
|
+
attr_accessor :handler
|
5
|
+
|
6
|
+
def start
|
7
|
+
raise 'implementations provide concrete listener functionality'
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def process(op)
|
13
|
+
raise 'No operation specified' unless op
|
14
|
+
|
15
|
+
unless valid?(op)
|
16
|
+
log.error "Received invalid job #{op}"
|
17
|
+
return
|
18
|
+
end
|
19
|
+
|
20
|
+
log.debug "Processing #{op[:id]}"
|
21
|
+
start = Time.now
|
22
|
+
|
23
|
+
begin
|
24
|
+
@handler.process op[:id], op[:command], op[:args]
|
25
|
+
rescue Exception => e
|
26
|
+
log.error 'Received error during handler'
|
27
|
+
log.error e
|
28
|
+
ensure
|
29
|
+
log.debug "Processed #{op[:id]}: #{Time.now - start} seconds processing time"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def valid?(op)
|
36
|
+
return false unless op.is_a? Hash
|
37
|
+
return false unless op[:id]
|
38
|
+
return false unless op[:command]
|
39
|
+
return false unless op[:args]
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'memcache'
|
2
|
+
|
3
|
+
module Airbrush
|
4
|
+
module Listeners
|
5
|
+
class Memcache < Listener
|
6
|
+
DEFAULT_POLL_FREQUENCY = 2 # seconds
|
7
|
+
DEFAULT_INCOMING_QUEUE = 'airbrush_incoming_queue'
|
8
|
+
|
9
|
+
attr_reader :host, :poll_frequency
|
10
|
+
|
11
|
+
def initialize(host, frequency = DEFAULT_POLL_FREQUENCY)
|
12
|
+
@host = host
|
13
|
+
@poll_frequency = frequency
|
14
|
+
catch_signals(:int)
|
15
|
+
end
|
16
|
+
|
17
|
+
def start
|
18
|
+
@running = true
|
19
|
+
@starling = MemCache.new(@host)
|
20
|
+
|
21
|
+
log.debug 'Accepting incoming jobs'
|
22
|
+
|
23
|
+
loop do
|
24
|
+
poll do |operation|
|
25
|
+
process operation
|
26
|
+
end
|
27
|
+
|
28
|
+
break unless @running
|
29
|
+
sleep @poll_frequency
|
30
|
+
end
|
31
|
+
|
32
|
+
@starling.reset
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def poll
|
38
|
+
operation = @starling.get(DEFAULT_INCOMING_QUEUE)
|
39
|
+
yield operation if operation and block_given?
|
40
|
+
end
|
41
|
+
|
42
|
+
def catch_signals(*signals)
|
43
|
+
signals.each do |signal|
|
44
|
+
sig = signal.to_s.upcase
|
45
|
+
Signal.trap(sig) do
|
46
|
+
@running = false
|
47
|
+
log.debug "Intercepted SIG#{sig}, exiting"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Airbrush
|
2
|
+
module Processors
|
3
|
+
module Image
|
4
|
+
class ImageProcessor < Processor
|
5
|
+
class_inheritable_accessor :before_filters, :after_filters
|
6
|
+
|
7
|
+
def self.before_filter(*symbols)
|
8
|
+
self.before_filters = symbols
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.after_filter(*symbols)
|
12
|
+
self.after_filters = symbols
|
13
|
+
end
|
14
|
+
|
15
|
+
def dispatch(command, args)
|
16
|
+
self.before_filters.each { |filter| filter_dispatch(filter, args) } if self.before_filters
|
17
|
+
rv = super command, args
|
18
|
+
self.after_filters.each { |filter| filter_dispatch(filter, args) } if self.after_filters
|
19
|
+
rv
|
20
|
+
end
|
21
|
+
|
22
|
+
def filter_dispatch(command, args)
|
23
|
+
raise "Unknown processor operation #{command} (#{args.inspect unless args.blank?})" unless self.respond_to? command
|
24
|
+
params = assign(command, args)
|
25
|
+
self.send command, *params
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
Binary file
|
Binary file
|