igor 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 +4 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +31 -0
- data/Rakefile +2 -0
- data/bin/igorup +4 -0
- data/examples/send.eg.rb +18 -0
- data/igor.gemspec +28 -0
- data/lib/igor.rb +22 -0
- data/lib/igor/builder.rb +49 -0
- data/lib/igor/cli.rb +38 -0
- data/lib/igor/logger.rb +40 -0
- data/lib/igor/master.rb +93 -0
- data/lib/igor/payload_parsers/json.rb +22 -0
- data/lib/igor/version.rb +3 -0
- metadata +145 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
igor (0.0.1)
|
5
|
+
amqp (~> 0.6.7)
|
6
|
+
angry_hash (~> 0.3.2)
|
7
|
+
lachie-tapp (~> 1.1.0)
|
8
|
+
thor (~> 0.14.3)
|
9
|
+
yajl-ruby (~> 0.7.8)
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: http://rubygems.org/
|
13
|
+
specs:
|
14
|
+
amqp (0.6.7)
|
15
|
+
eventmachine (>= 0.12.4)
|
16
|
+
angry_hash (0.3.2)
|
17
|
+
eventmachine (0.12.10)
|
18
|
+
lachie-tapp (1.1.0)
|
19
|
+
thor (0.14.3)
|
20
|
+
yajl-ruby (0.7.8)
|
21
|
+
|
22
|
+
PLATFORMS
|
23
|
+
ruby
|
24
|
+
|
25
|
+
DEPENDENCIES
|
26
|
+
amqp (~> 0.6.7)
|
27
|
+
angry_hash (~> 0.3.2)
|
28
|
+
igor!
|
29
|
+
lachie-tapp (~> 1.1.0)
|
30
|
+
thor (~> 0.14.3)
|
31
|
+
yajl-ruby (~> 0.7.8)
|
data/Rakefile
ADDED
data/bin/igorup
ADDED
data/examples/send.eg.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bunny'
|
3
|
+
require 'yajl'
|
4
|
+
|
5
|
+
b = Bunny.new(:logging => true)
|
6
|
+
|
7
|
+
# start a communication session with the amqp server
|
8
|
+
b.start
|
9
|
+
|
10
|
+
# declare a queue
|
11
|
+
q = b.queue('yoohoo')
|
12
|
+
|
13
|
+
payload = {
|
14
|
+
:hello => 'everybody!'
|
15
|
+
}
|
16
|
+
|
17
|
+
# publish a message to the queue
|
18
|
+
q.publish(Yajl::Encoder.encode payload)
|
data/igor.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "igor/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "igor"
|
7
|
+
s.version = Igor::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Lachie Cox"]
|
10
|
+
s.email = ["lachie.cox@plus2.com.au"]
|
11
|
+
s.homepage = "http://rubygems.org/gems/igor"
|
12
|
+
s.summary = %q{Ugly but effective message queue workers.}
|
13
|
+
s.description = %q{A rack inspired approach to message queue workers.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "igor"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {examples}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_runtime_dependency 'thor' , %w[~>0.14.3]
|
23
|
+
s.add_runtime_dependency 'angry_hash', %w[~>0.3.2]
|
24
|
+
s.add_runtime_dependency 'amqp' , %w[~>0.6.7]
|
25
|
+
s.add_runtime_dependency 'yajl-ruby' , %w[~>0.7.8]
|
26
|
+
s.add_runtime_dependency 'lachie-tapp', %w[~>1.1.0]
|
27
|
+
|
28
|
+
end
|
data/lib/igor.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'tapp'
|
2
|
+
|
3
|
+
module Igor
|
4
|
+
autoload :Builder, "igor/builder"
|
5
|
+
autoload :CLI , "igor/cli"
|
6
|
+
autoload :Master , "igor/master"
|
7
|
+
autoload :Logger , "igor/logger"
|
8
|
+
|
9
|
+
module PayloadParsers
|
10
|
+
autoload :JSON, 'igor/payload_parsers/json'
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def self.start(options={})
|
15
|
+
@config = options
|
16
|
+
Master.new(options).start
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.config
|
20
|
+
@config
|
21
|
+
end
|
22
|
+
end
|
data/lib/igor/builder.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
module Igor
|
3
|
+
class Builder
|
4
|
+
def self.parse_file(config, opts = {})
|
5
|
+
options = {}
|
6
|
+
|
7
|
+
if config =~ /\.iu$/
|
8
|
+
cfgfile = ::File.read(config)
|
9
|
+
|
10
|
+
if cfgfile[/^#\\(.*)/] && opts
|
11
|
+
options = opts.parse! $1
|
12
|
+
end
|
13
|
+
|
14
|
+
cfgfile.sub!(/^__END__\n.*/, '')
|
15
|
+
igor = eval "Igor::Builder.new {( " + cfgfile + "\n )}.to_igor", TOPLEVEL_BINDING, config
|
16
|
+
else
|
17
|
+
require config
|
18
|
+
igor = Object.const_get(::File.basename(config, '.rb').capitalize)
|
19
|
+
end
|
20
|
+
return igor, options
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(&block)
|
24
|
+
@ins = []
|
25
|
+
instance_eval(&block) if block_given?
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.igor(&block)
|
29
|
+
self.new(&block).to_igor
|
30
|
+
end
|
31
|
+
|
32
|
+
def use(igor, *args, &block)
|
33
|
+
@ins << lambda { |inner_igor| igor.new(inner_igor, *args, &block) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def run(igor)
|
37
|
+
@ins << igor #lambda { |nothing| igor }
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_igor
|
41
|
+
inner_igor = @ins.last
|
42
|
+
@ins[0...-1].reverse.inject(inner_igor) { |a, e| e.call(a) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def call(env)
|
46
|
+
to_igor.call(env)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/igor/cli.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'angry_hash'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
$stdout.sync = $stderr.sync = true
|
6
|
+
|
7
|
+
module Igor
|
8
|
+
class CLI < Thor
|
9
|
+
|
10
|
+
default_task :igor
|
11
|
+
|
12
|
+
desc 'igor', 'run igor'
|
13
|
+
method_option :config, :type => :string
|
14
|
+
def igor(config_file='config.iu')
|
15
|
+
config[:config_file] = config_file
|
16
|
+
Igor.start(config)
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
def config
|
21
|
+
@config ||= config!
|
22
|
+
end
|
23
|
+
|
24
|
+
def config!
|
25
|
+
AngryHash[ options ].tap {|config|
|
26
|
+
|
27
|
+
if options.config?
|
28
|
+
cfg_file = Pathname(options.config)
|
29
|
+
|
30
|
+
abort "unable to open config yaml '#{cfg_file}'" unless cfg_file.exist?
|
31
|
+
|
32
|
+
config.deep_update( YAML.load(cfg_file.read) )
|
33
|
+
end
|
34
|
+
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/igor/logger.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Igor
|
2
|
+
class Logger
|
3
|
+
def initialize(igor, logger=nil)
|
4
|
+
@igor = igor
|
5
|
+
@logger = logger
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
log_around(env) { @igor.call(env) }
|
10
|
+
end
|
11
|
+
|
12
|
+
PreFormat = "Processing (%s %s %s) %s"
|
13
|
+
PostFormat = "Done (%s %s %s) in %0.4fs"
|
14
|
+
|
15
|
+
def log_around(env)
|
16
|
+
logger = @logger || env['igor.errors']
|
17
|
+
|
18
|
+
header = env['igor.amqp.header']
|
19
|
+
start_time = Time.now
|
20
|
+
|
21
|
+
logger.puts PreFormat % [
|
22
|
+
header.delivery_tag,
|
23
|
+
header.exchange,
|
24
|
+
header.routing_key,
|
25
|
+
start_time.strftime("%Y-%m-%d %H:%M:%S")
|
26
|
+
]
|
27
|
+
|
28
|
+
yield(env).tap {|response|
|
29
|
+
logger.puts "rsp %s" % response.inspect
|
30
|
+
now = Time.now
|
31
|
+
logger.puts PostFormat % [
|
32
|
+
header.delivery_tag,
|
33
|
+
header.exchange,
|
34
|
+
header.routing_key,
|
35
|
+
now-start_time
|
36
|
+
]
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/igor/master.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'amqp'
|
2
|
+
require 'mq'
|
3
|
+
require 'yajl'
|
4
|
+
|
5
|
+
module Igor
|
6
|
+
class Master
|
7
|
+
attr_reader :options, :logger
|
8
|
+
|
9
|
+
def initialize(options={})
|
10
|
+
@options = AngryHash[options]
|
11
|
+
@options.env ||= {}
|
12
|
+
|
13
|
+
@logger = @options.logger ||= $stderr
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
run
|
18
|
+
end
|
19
|
+
|
20
|
+
def igor
|
21
|
+
@igor ||= begin
|
22
|
+
if !::File.exist? options.config_file
|
23
|
+
abort "configuration #{options.config_file} not found"
|
24
|
+
end
|
25
|
+
|
26
|
+
igor, options = Igor::Builder.parse_file(self.options.config_file, opt_parser)
|
27
|
+
self.options.merge! options
|
28
|
+
igor
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Options
|
33
|
+
def parse!(str)
|
34
|
+
AngryHash[ Yajl::Parser.parse(str) ]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def opt_parser
|
39
|
+
@opt_parser ||= Options.new
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def run
|
44
|
+
logger.puts "#{self.class} starting up"
|
45
|
+
igor # load igor so that its options are parsed if required
|
46
|
+
|
47
|
+
options.tapp(:final_opts)
|
48
|
+
|
49
|
+
cfg = options.amqp!.dup
|
50
|
+
cfg.host ||= 'localhost'
|
51
|
+
|
52
|
+
|
53
|
+
AMQP.start(cfg.to_normal_hash(:symbols)) do
|
54
|
+
options.queues.each {|queue_name| subscribe_to(queue_name)}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def subscribe_to(queue_name)
|
59
|
+
logger.puts "* getting queue '#{queue_name}'"
|
60
|
+
q = MQ.queue(queue_name)
|
61
|
+
|
62
|
+
logger.puts "* listening"
|
63
|
+
|
64
|
+
q.subscribe {|header,body|
|
65
|
+
env = options.env.merge(
|
66
|
+
'igor.errors' => options.logger,
|
67
|
+
'igor.amqp.header' => header,
|
68
|
+
'igor.payload' => body
|
69
|
+
)
|
70
|
+
|
71
|
+
igor.call(env).tap {|response|
|
72
|
+
publish_response(response)
|
73
|
+
}
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def publish_response(response)
|
78
|
+
|
79
|
+
if !@response_queue && options.response_queue?
|
80
|
+
@response_queue = MQ.queue(options.response_queue)
|
81
|
+
end
|
82
|
+
|
83
|
+
if response && @response_queue
|
84
|
+
@response_queue.publish(Yajl::Encoder.encode(response))
|
85
|
+
|
86
|
+
# TODO
|
87
|
+
#kind,exchange_id = *response[0..1]
|
88
|
+
#response.tapp(:app_response)
|
89
|
+
#MQ.send(kind,exchange_id).publish(*response[2..-1])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'yajl'
|
2
|
+
|
3
|
+
module Igor
|
4
|
+
module PayloadParsers
|
5
|
+
class JSON
|
6
|
+
def initialize(igor,options={})
|
7
|
+
@igor = igor
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
begin
|
13
|
+
env['igor.payload'] = Yajl::Parser.parse(env['igor.original_payload'] = env['igor.payload'])
|
14
|
+
rescue Yajl::ParseError
|
15
|
+
env['igor.errors'].puts "[#{self.class}] unable to parse payload: #{$!}"
|
16
|
+
else
|
17
|
+
@igor.call(env)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/igor/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: igor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Lachie Cox
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-10-24 00:00:00 +11:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: thor
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
- 14
|
30
|
+
- 3
|
31
|
+
version: 0.14.3
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: angry_hash
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
- 3
|
44
|
+
- 2
|
45
|
+
version: 0.3.2
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: amqp
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
- 6
|
58
|
+
- 7
|
59
|
+
version: 0.6.7
|
60
|
+
type: :runtime
|
61
|
+
version_requirements: *id003
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: yajl-ruby
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
segments:
|
70
|
+
- 0
|
71
|
+
- 7
|
72
|
+
- 8
|
73
|
+
version: 0.7.8
|
74
|
+
type: :runtime
|
75
|
+
version_requirements: *id004
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: lachie-tapp
|
78
|
+
prerelease: false
|
79
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ~>
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
segments:
|
84
|
+
- 1
|
85
|
+
- 1
|
86
|
+
- 0
|
87
|
+
version: 1.1.0
|
88
|
+
type: :runtime
|
89
|
+
version_requirements: *id005
|
90
|
+
description: A rack inspired approach to message queue workers.
|
91
|
+
email:
|
92
|
+
- lachie.cox@plus2.com.au
|
93
|
+
executables:
|
94
|
+
- igorup
|
95
|
+
extensions: []
|
96
|
+
|
97
|
+
extra_rdoc_files: []
|
98
|
+
|
99
|
+
files:
|
100
|
+
- .gitignore
|
101
|
+
- Gemfile
|
102
|
+
- Gemfile.lock
|
103
|
+
- Rakefile
|
104
|
+
- bin/igorup
|
105
|
+
- examples/send.eg.rb
|
106
|
+
- igor.gemspec
|
107
|
+
- lib/igor.rb
|
108
|
+
- lib/igor/builder.rb
|
109
|
+
- lib/igor/cli.rb
|
110
|
+
- lib/igor/logger.rb
|
111
|
+
- lib/igor/master.rb
|
112
|
+
- lib/igor/payload_parsers/json.rb
|
113
|
+
- lib/igor/version.rb
|
114
|
+
has_rdoc: true
|
115
|
+
homepage: http://rubygems.org/gems/igor
|
116
|
+
licenses: []
|
117
|
+
|
118
|
+
post_install_message:
|
119
|
+
rdoc_options: []
|
120
|
+
|
121
|
+
require_paths:
|
122
|
+
- lib
|
123
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
segments:
|
128
|
+
- 0
|
129
|
+
version: "0"
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
segments:
|
135
|
+
- 0
|
136
|
+
version: "0"
|
137
|
+
requirements: []
|
138
|
+
|
139
|
+
rubyforge_project: igor
|
140
|
+
rubygems_version: 1.3.6
|
141
|
+
signing_key:
|
142
|
+
specification_version: 3
|
143
|
+
summary: Ugly but effective message queue workers.
|
144
|
+
test_files: []
|
145
|
+
|