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.
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ .*.sw?
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in igor.gemspec
4
+ gemspec
@@ -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)
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'igor'
4
+ Igor::CLI.start
@@ -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)
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Igor
2
+ VERSION = "0.0.1"
3
+ end
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
+