visfleet-pipeline_toolkit 0.0.1 → 0.1.0
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/Rakefile +3 -3
- data/VERSION +1 -0
- data/bin/{pop.rb → msg_pop.rb} +0 -0
- data/bin/{monitor.rb → msg_probe.rb} +1 -1
- data/bin/msg_push.rb +25 -0
- data/bin/msg_sink.rb +11 -0
- data/bin/{subscribe.rb → msg_subscribe.rb} +5 -6
- data/lib/pipeline_toolkit.rb +9 -0
- data/lib/pipeline_toolkit/message_coder.rb +19 -0
- data/lib/pipeline_toolkit/message_command.rb +96 -0
- data/lib/pipeline_toolkit/message_popper.rb +87 -0
- data/lib/pipeline_toolkit/message_probe.rb +24 -0
- data/lib/pipeline_toolkit/message_pusher.rb +54 -0
- data/lib/pipeline_toolkit/message_sink.rb +8 -0
- data/lib/pipeline_toolkit/message_subscriber.rb +134 -0
- data/lib/pipeline_toolkit/open_hash.rb +24 -0
- data/pipeline_toolkit.gemspec +57 -0
- metadata +23 -6
- data/bin/push.rb +0 -25
data/Rakefile
CHANGED
@@ -5,11 +5,11 @@ begin
|
|
5
5
|
require 'jeweler'
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "pipeline_toolkit"
|
8
|
-
gem.summary = %Q{
|
9
|
-
gem.email = "
|
8
|
+
gem.summary = %Q{Toolkit for building processing pipelines using Unix Pipes and AMQP messages}
|
9
|
+
gem.email = "labs@visfleet.com"
|
10
10
|
gem.homepage = "http://github.com/visfleet/pipeline_toolkit"
|
11
11
|
gem.authors = ["Aisha Fenton"]
|
12
|
-
|
12
|
+
gem.executables = ["msg_probe.rb", "msg_subscribe.rb", "msg_push.rb", "msg_pop.rb", "msg_sink.rb"]
|
13
13
|
end
|
14
14
|
|
15
15
|
rescue LoadError
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bin/{pop.rb → msg_pop.rb}
RENAMED
File without changes
|
data/bin/msg_push.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'trollop'
|
5
|
+
require 'pipeline_toolkit'
|
6
|
+
|
7
|
+
opts = Trollop::options do
|
8
|
+
opt :exchanges, "The destination exchange(s)", :short => "x", :type => :strings
|
9
|
+
opt :key_eval, "A string of ruby code that is evaluated to produce a routing key for a given message.
|
10
|
+
By default each message has no routing key", :short => "e", :type => :string
|
11
|
+
opt :key_file, "A ruby file that gets included which contains a custom route_key method", :short => "f", :type => :string
|
12
|
+
|
13
|
+
# Msg server
|
14
|
+
opt :host, "The AMQP message server host", :default => "localhost"
|
15
|
+
opt :port, "The AMQP message server port", :default => "5672"
|
16
|
+
opt :user, "The AMQP message server username", :default => "guest"
|
17
|
+
opt :pass, "The AMQP message server username", :default => "guest"
|
18
|
+
opt :vhost, "The AMQP message server vhost", :default => "/"
|
19
|
+
end
|
20
|
+
|
21
|
+
mp = MessagePusher.new(opts)
|
22
|
+
# FIXME. Should be in MessageCommand class
|
23
|
+
Signal.trap('INT') { mp.stop }
|
24
|
+
Signal.trap('TERM'){ mp.stop }
|
25
|
+
mp.start
|
data/bin/msg_sink.rb
ADDED
@@ -5,9 +5,11 @@ require 'trollop'
|
|
5
5
|
require 'pipeline_toolkit'
|
6
6
|
|
7
7
|
opts = Trollop::options do
|
8
|
-
opt :
|
8
|
+
opt :exchange, "The exchange to subscribe to", :short => "x", :type => :string
|
9
|
+
opt :queue, "The source queue", :short => "q", :type => :string
|
9
10
|
opt :ack, "Switch that requires that messages are successfully processed before continuing with the next message", :short => "a"
|
10
|
-
opt :
|
11
|
+
opt :topic, "The queue topic to subscribe to", :short => "t", :type => :string
|
12
|
+
opt :max_unackd, "The maximum number of unaknowledged messages to buffer before waiting for them to be acknowledged. Defaults to 100", :default => 100
|
11
13
|
|
12
14
|
# Msg server
|
13
15
|
opt :host, "The AMQP message server host", :default => "localhost"
|
@@ -17,7 +19,4 @@ opts = Trollop::options do
|
|
17
19
|
opt :vhost, "The AMQP message server vhost", :default => "/"
|
18
20
|
end
|
19
21
|
|
20
|
-
ms = MessageSubscriber.new(opts)
|
21
|
-
Signal.trap('INT') { ms.stop }
|
22
|
-
Signal.trap('TERM'){ ms.stop }
|
23
|
-
ms.start
|
22
|
+
ms = MessageSubscriber.new(opts).start
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require "pipeline_toolkit/central_logger"
|
2
|
+
require "pipeline_toolkit/message_coder"
|
3
|
+
require "pipeline_toolkit/message_command"
|
4
|
+
require "pipeline_toolkit/message_probe"
|
5
|
+
require "pipeline_toolkit/message_popper"
|
6
|
+
require "pipeline_toolkit/message_pusher"
|
7
|
+
require "pipeline_toolkit/message_subscriber"
|
8
|
+
require "pipeline_toolkit/message_sink"
|
9
|
+
require "pipeline_toolkit/open_hash"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
# Encodes messages
|
3
|
+
#
|
4
|
+
class MessageCoder
|
5
|
+
|
6
|
+
def self.encode(msg)
|
7
|
+
# NB: Using Marshal here because it's 9-10x faster than to_yaml
|
8
|
+
# See http://gist.github.com/190849
|
9
|
+
str = Marshal.dump(msg)
|
10
|
+
str.gsub!("\n", '--\\n')
|
11
|
+
str
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.decode(str)
|
15
|
+
str.gsub!('--\\n', "\n")
|
16
|
+
Marshal.load(str)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "eventmachine"
|
3
|
+
|
4
|
+
module MessageCommand
|
5
|
+
include DefaultLogging
|
6
|
+
|
7
|
+
attr_reader :sys_pipe
|
8
|
+
|
9
|
+
def start
|
10
|
+
log.info("starting")
|
11
|
+
|
12
|
+
Signal.trap('INT') { EM.stop }
|
13
|
+
Signal.trap('TERM'){ EM.stop }
|
14
|
+
|
15
|
+
@ack_buffer ||= ""
|
16
|
+
|
17
|
+
begin
|
18
|
+
EM.run do
|
19
|
+
self.init_loop
|
20
|
+
EM.attach($stdin, ProcessLine, self)
|
21
|
+
end
|
22
|
+
rescue StandardError => e
|
23
|
+
log.info e
|
24
|
+
raise e
|
25
|
+
ensure
|
26
|
+
self.shutdown
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def shutdown
|
31
|
+
log.info("shutting down")
|
32
|
+
@sys_pipe && @sys_pipe.close
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_line(line)
|
36
|
+
msg = MessageCoder.decode(line)
|
37
|
+
|
38
|
+
case msg[:msg_type]
|
39
|
+
when :system
|
40
|
+
result = process_system(msg)
|
41
|
+
else
|
42
|
+
result = process_message(msg)
|
43
|
+
end
|
44
|
+
|
45
|
+
case result
|
46
|
+
when :ack
|
47
|
+
self.ack_msg(msg)
|
48
|
+
else
|
49
|
+
self.pass_on_msg(result)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def ack_msg(msg)
|
54
|
+
return unless @use_ack
|
55
|
+
@sys_pipe.syswrite(msg.ack_id + "\n")
|
56
|
+
end
|
57
|
+
|
58
|
+
def pass_on_msg(msg)
|
59
|
+
$stdout.syswrite(MessageCoder.encode(msg) << "\n")
|
60
|
+
end
|
61
|
+
|
62
|
+
# Override in included class. Provides a chance to initialize any
|
63
|
+
# code that needs to take place once the EM loop has started.
|
64
|
+
def init_loop
|
65
|
+
# Implemented in class that includes me
|
66
|
+
end
|
67
|
+
|
68
|
+
# Override in included class. Processes a message. This method
|
69
|
+
# must return either a msg object -- which may or may not have been modified -- or the symbol :ack.
|
70
|
+
# Returning :ack mean that the message has been dealt with and can be acknowledged back to the queue
|
71
|
+
# server. All messages must be acknowledged by at least one message_command.
|
72
|
+
def process_message(msg)
|
73
|
+
# Implemented in class that includes me
|
74
|
+
msg
|
75
|
+
end
|
76
|
+
|
77
|
+
def process_system(msg)
|
78
|
+
@sys_pipe = File.open(msg.sys_pipe, "w")
|
79
|
+
@use_ack = msg.use_ack
|
80
|
+
@max_unackd = msg.max_unackd
|
81
|
+
msg
|
82
|
+
end
|
83
|
+
|
84
|
+
module ProcessLine
|
85
|
+
include DefaultLogging
|
86
|
+
|
87
|
+
def initialize(msg_command)
|
88
|
+
@msg_command = msg_command
|
89
|
+
end
|
90
|
+
|
91
|
+
def notify_readable
|
92
|
+
@msg_command.process_line($stdin.gets)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
require "bunny"
|
3
|
+
require "time"
|
4
|
+
|
5
|
+
class MessagePopper
|
6
|
+
include DefaultLogging
|
7
|
+
|
8
|
+
PIPE_PATH = "/tmp"
|
9
|
+
|
10
|
+
def initialize(opts)
|
11
|
+
@cycle = opts[:cycle]
|
12
|
+
@msg_server = Bunny.new({:spec => '08'}.merge!(opts.select_keys(:host, :port, :user, :pass, :vhost)))
|
13
|
+
@msg_server.start
|
14
|
+
@sources = opts[:sources]
|
15
|
+
@use_ack = opts[:ack]
|
16
|
+
end
|
17
|
+
|
18
|
+
def start
|
19
|
+
self.create_sys_pipe
|
20
|
+
EM.run do
|
21
|
+
EM.add_periodic_timer(@cycle) { self.tick }
|
22
|
+
end
|
23
|
+
self.destroy_sys_pipe
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_sys_pipe
|
27
|
+
log.debug("creating sys-pipe")
|
28
|
+
name = File.join(PIPE_PATH, "sys_pipe_#{self.generate_guid}")
|
29
|
+
`mkfifo #{name}`
|
30
|
+
@sys_pipe = File.new(name, "r+")
|
31
|
+
sleep(2)
|
32
|
+
$stdout.puts(MessageCoder.encode({:msg_type => :system, :sys_pipe => name, :use_ack => @use_ack}))
|
33
|
+
$stdout.flush
|
34
|
+
end
|
35
|
+
|
36
|
+
def destroy_sys_pipe
|
37
|
+
`rm #{@sys_pipe}`
|
38
|
+
end
|
39
|
+
|
40
|
+
def generate_guid
|
41
|
+
# TODO maybe better way to do guid
|
42
|
+
"#{Time.now.iso8601}_#{rand(99999)}_#{rand(99999)}_#{rand(99999)}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def tick
|
46
|
+
log.debug("draining queue")
|
47
|
+
loop do
|
48
|
+
q_empty = true
|
49
|
+
@sources.each do |source|
|
50
|
+
# FIXME. @queue for each source, or not needed
|
51
|
+
@queue ||= @msg_server.queue(source)
|
52
|
+
result = @queue.pop(:ack => @use_ack)
|
53
|
+
next if result == :queue_empty
|
54
|
+
# msg = YAML.load(result)
|
55
|
+
q_empty = false
|
56
|
+
# @use_ack ? verify(msg, source) { write_msg(msg) } : write_msg(msg)
|
57
|
+
|
58
|
+
@count ||= 0
|
59
|
+
@count += 1
|
60
|
+
@prev_time ||= Time.now
|
61
|
+
if @count > 100
|
62
|
+
t = Time.now
|
63
|
+
log.debug("per sec #{@count / (t - @prev_time)}")
|
64
|
+
@prev_time = t
|
65
|
+
@count = 0
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
break if q_empty
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def write_msg(msg)
|
74
|
+
$stdout.puts(MessageCoder.encode(msg))
|
75
|
+
$stdout.flush
|
76
|
+
end
|
77
|
+
|
78
|
+
def verify(msg, source)
|
79
|
+
msg.ack_id = generate_guid
|
80
|
+
yield
|
81
|
+
$stdout.flush
|
82
|
+
ack_id = @sys_pipe.gets.chomp!
|
83
|
+
raise Exception.new("ACK failed with msg #{msg.to_yaml}") unless msg.ack_id == ack_id
|
84
|
+
@msg_server.queue(source).ack
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class MessageProbe
|
2
|
+
include MessageCommand
|
3
|
+
|
4
|
+
def initialize(opts)
|
5
|
+
@interval = opts.interval
|
6
|
+
reset
|
7
|
+
end
|
8
|
+
|
9
|
+
def process_message(msg)
|
10
|
+
@count += 1
|
11
|
+
if @count > @interval
|
12
|
+
delta = Time.now - @prev_time
|
13
|
+
log.info("#{@count / delta} msgs per second")
|
14
|
+
reset
|
15
|
+
end
|
16
|
+
msg
|
17
|
+
end
|
18
|
+
|
19
|
+
def reset
|
20
|
+
@count = 0
|
21
|
+
@prev_time = Time.now
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "mq"
|
2
|
+
|
3
|
+
class MessagePusher
|
4
|
+
include MessageCommand
|
5
|
+
|
6
|
+
def initialize(opts)
|
7
|
+
@key_eval = opts.key_eval
|
8
|
+
if opts.key_file
|
9
|
+
@key_file = opts.key_file
|
10
|
+
load_route(@key_file)
|
11
|
+
self.init_route
|
12
|
+
end
|
13
|
+
@exchange_names = opts.exchanges.map { |str| str.split(":") }
|
14
|
+
@msg_server_config = opts.select_keys(:host, :port, :user, :pass, :vhost)
|
15
|
+
end
|
16
|
+
|
17
|
+
def init_loop
|
18
|
+
@msg_server = MQ.new(AMQP.connect(@msg_server_config))
|
19
|
+
self.setup_exchanges
|
20
|
+
end
|
21
|
+
|
22
|
+
def setup_exchanges
|
23
|
+
@exchanges = []
|
24
|
+
@exchange_names.each do |name, type|
|
25
|
+
type ||= :fanout
|
26
|
+
@exchanges << MQ::Exchange.new(@msg_server, type.to_sym, name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def load_route(key_file)
|
31
|
+
require key_file
|
32
|
+
self.extend eval(classify(key_file.gsub(".rb", "")))
|
33
|
+
end
|
34
|
+
|
35
|
+
# Stolen from rails.
|
36
|
+
def classify(str)
|
37
|
+
str.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
38
|
+
end
|
39
|
+
|
40
|
+
# is overriden by included fork_file if specified
|
41
|
+
def route_key(msg)
|
42
|
+
@key_eval ? eval(@key_eval) : nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def process_message(msg)
|
46
|
+
@exchanges.each do |exchange|
|
47
|
+
key = route_key(msg)
|
48
|
+
exchange.publish(MessageCoder.encode(msg), :routing_key => key)
|
49
|
+
# exchange.publish(msg.to_yaml, :routing_key => key)
|
50
|
+
end
|
51
|
+
:ack
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
require "mq"
|
3
|
+
require "time"
|
4
|
+
require "socket"
|
5
|
+
|
6
|
+
class MessageSubscriber
|
7
|
+
include DefaultLogging
|
8
|
+
|
9
|
+
PIPE_PATH = "/tmp"
|
10
|
+
|
11
|
+
def initialize(opts)
|
12
|
+
@exchange_name = opts[:exchange]
|
13
|
+
@queue_name = opts[:queue]
|
14
|
+
@use_ack = opts[:ack]
|
15
|
+
@topic = opts[:topic]
|
16
|
+
@msg_server_opts = opts.select_keys(:host, :port, :user, :pass, :vhost)
|
17
|
+
@unackd_msgs = {}
|
18
|
+
@max_unackd = opts[:max_unackd]
|
19
|
+
end
|
20
|
+
|
21
|
+
def start
|
22
|
+
Signal.trap('INT') { AMQP.stop{ EM.stop } }
|
23
|
+
Signal.trap('TERM'){ AMQP.stop{ EM.stop } }
|
24
|
+
|
25
|
+
begin
|
26
|
+
self.create_sys_pipe
|
27
|
+
AMQP.start(@msg_server_opts) do
|
28
|
+
# For ack to work appropriatly you must shutdown AMQP gracefully,
|
29
|
+
# otherwise all items in your queue will be returned
|
30
|
+
# FIXME. Doesn't shut down cleanly with these commands included. Why?
|
31
|
+
|
32
|
+
self.setup_queue
|
33
|
+
# NB. prefetch limits the amount of unknowledged messages that come down the pipe.
|
34
|
+
MQ.prefetch(@max_unackd)
|
35
|
+
@queue.subscribe(:ack => @use_ack) do |header, body|
|
36
|
+
self.process_msg(header, body)
|
37
|
+
end
|
38
|
+
|
39
|
+
EM.attach(@sys_pipe, HandleAcks, @sys_pipe, @unackd_msgs)
|
40
|
+
end
|
41
|
+
rescue StandardError => e
|
42
|
+
log.info e
|
43
|
+
raise e
|
44
|
+
ensure
|
45
|
+
self.shutdown
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def shutdown
|
50
|
+
log.info "Shutting down"
|
51
|
+
self.destroy_sys_pipe
|
52
|
+
end
|
53
|
+
|
54
|
+
def setup_queue
|
55
|
+
# If a queue_name is given then we treat the queue as fixed, otherwise as temporary
|
56
|
+
@queue = @queue_name ? MQ.queue(@queue_name, :durable => true) :
|
57
|
+
self.generate_temporary_queue
|
58
|
+
if @exchange_name
|
59
|
+
log.info("Binding to exchange:#{@exchange_name} #{@topic ? "using topic:" + @topic : ""}")
|
60
|
+
# OPTIMIZE. Should we be setting up the exchange just incase it hasn't been created yet?
|
61
|
+
@queue.bind(@exchange_name, :key => @topic)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def create_sys_pipe
|
66
|
+
log.debug("creating sys-pipe")
|
67
|
+
name = File.join(PIPE_PATH, "sys_pipe_#{self.generate_guid}")
|
68
|
+
`mkfifo #{name}`
|
69
|
+
@sys_pipe = File.new(name, "r+")
|
70
|
+
$stdout.puts(MessageCoder.encode({:msg_type => :system,
|
71
|
+
:sys_pipe => name,
|
72
|
+
:use_ack => @use_ack,
|
73
|
+
:max_unackd => @max_unackd}))
|
74
|
+
$stdout.flush
|
75
|
+
end
|
76
|
+
|
77
|
+
def destroy_sys_pipe
|
78
|
+
`rm #{@sys_pipe.path}`
|
79
|
+
end
|
80
|
+
|
81
|
+
def generate_temporary_queue
|
82
|
+
qname = "#{Socket.gethostname}_#{self.generate_guid}"
|
83
|
+
log.debug("Binding temporary queue #{qname}")
|
84
|
+
MQ.queue(qname, :auto_delete => true, :durable => false)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Generates a guid. Stolen from EM.
|
88
|
+
def generate_guid
|
89
|
+
# Cache uuidgen seed for better performance
|
90
|
+
if @ix and @ix >= 10_000
|
91
|
+
@ix = nil
|
92
|
+
@seed = nil
|
93
|
+
end
|
94
|
+
|
95
|
+
# NB. This will only work on *nix platforms
|
96
|
+
@seed ||= `uuidgen`.chomp.gsub(/-/,"")
|
97
|
+
@ix ||= 0
|
98
|
+
|
99
|
+
"#{@seed}#{@ix += 1}"
|
100
|
+
end
|
101
|
+
|
102
|
+
def process_msg(header, body)
|
103
|
+
msg = YAML.load(body)
|
104
|
+
store_ack(msg, header) if @use_ack
|
105
|
+
write_msg(msg)
|
106
|
+
end
|
107
|
+
|
108
|
+
def write_msg(msg)
|
109
|
+
$stdout.syswrite(MessageCoder.encode(msg) << "\n")
|
110
|
+
end
|
111
|
+
|
112
|
+
def store_ack(msg, header)
|
113
|
+
msg.ack_id = header.delivery_tag.to_s
|
114
|
+
@unackd_msgs[msg.ack_id] = header
|
115
|
+
end
|
116
|
+
|
117
|
+
# Handles msg acks
|
118
|
+
module HandleAcks
|
119
|
+
include DefaultLogging
|
120
|
+
|
121
|
+
def initialize(sys_pipe, unackd_msgs)
|
122
|
+
@sys_pipe = sys_pipe
|
123
|
+
@unackd_msgs = unackd_msgs
|
124
|
+
end
|
125
|
+
|
126
|
+
def notify_readable
|
127
|
+
ack_id = @sys_pipe.gets.chomp!
|
128
|
+
header = @unackd_msgs.delete(ack_id)
|
129
|
+
header.ack
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Stolen from
|
2
|
+
# http://github.com/karottenreibe/ohash/
|
3
|
+
module OpenHash
|
4
|
+
def method_missing(meth, *args)
|
5
|
+
method = meth.to_s
|
6
|
+
|
7
|
+
if method =~ %r{.+=$}
|
8
|
+
super unless args.length == 1
|
9
|
+
self[method[0...-1].to_sym] = args.first
|
10
|
+
else
|
11
|
+
self[meth]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def select_keys(*keys)
|
16
|
+
h = {}
|
17
|
+
self.each do |key, value|
|
18
|
+
h[key] = value if keys.include?(key)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
Hash.send(:include, OpenHash)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{pipeline_toolkit}
|
5
|
+
s.version = "0.1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Aisha Fenton"]
|
9
|
+
s.date = %q{2009-09-23}
|
10
|
+
s.email = %q{labs@visfleet.com}
|
11
|
+
s.executables = ["msg_probe.rb", "msg_subscribe.rb", "msg_push.rb", "msg_pop.rb", "msg_sink.rb"]
|
12
|
+
s.extra_rdoc_files = [
|
13
|
+
"LICENSE",
|
14
|
+
"README.rdoc"
|
15
|
+
]
|
16
|
+
s.files = [
|
17
|
+
".document",
|
18
|
+
".gitignore",
|
19
|
+
"LICENSE",
|
20
|
+
"README.rdoc",
|
21
|
+
"Rakefile",
|
22
|
+
"VERSION",
|
23
|
+
"bin/msg_pop.rb",
|
24
|
+
"bin/msg_probe.rb",
|
25
|
+
"bin/msg_push.rb",
|
26
|
+
"bin/msg_sink.rb",
|
27
|
+
"bin/msg_subscribe.rb",
|
28
|
+
"lib/pipeline_toolkit.rb",
|
29
|
+
"lib/pipeline_toolkit/message_coder.rb",
|
30
|
+
"lib/pipeline_toolkit/message_command.rb",
|
31
|
+
"lib/pipeline_toolkit/message_popper.rb",
|
32
|
+
"lib/pipeline_toolkit/message_probe.rb",
|
33
|
+
"lib/pipeline_toolkit/message_pusher.rb",
|
34
|
+
"lib/pipeline_toolkit/message_sink.rb",
|
35
|
+
"lib/pipeline_toolkit/message_subscriber.rb",
|
36
|
+
"lib/pipeline_toolkit/open_hash.rb",
|
37
|
+
"perf_test/test_pop.rb",
|
38
|
+
"perf_test/test_pop_async.rb",
|
39
|
+
"perf_test/test_push.rb",
|
40
|
+
"pipeline_toolkit.gemspec"
|
41
|
+
]
|
42
|
+
s.homepage = %q{http://github.com/visfleet/pipeline_toolkit}
|
43
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
44
|
+
s.require_paths = ["lib"]
|
45
|
+
s.rubygems_version = %q{1.3.5}
|
46
|
+
s.summary = %q{Toolkit for building processing pipelines using Unix Pipes and AMQP messages}
|
47
|
+
|
48
|
+
if s.respond_to? :specification_version then
|
49
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
50
|
+
s.specification_version = 3
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
53
|
+
else
|
54
|
+
end
|
55
|
+
else
|
56
|
+
end
|
57
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: visfleet-pipeline_toolkit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aisha Fenton
|
@@ -9,17 +9,18 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-09-
|
12
|
+
date: 2009-09-23 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
16
|
description:
|
17
17
|
email: labs@visfleet.com
|
18
18
|
executables:
|
19
|
-
-
|
20
|
-
-
|
21
|
-
-
|
22
|
-
-
|
19
|
+
- msg_probe.rb
|
20
|
+
- msg_subscribe.rb
|
21
|
+
- msg_push.rb
|
22
|
+
- msg_pop.rb
|
23
|
+
- msg_sink.rb
|
23
24
|
extensions: []
|
24
25
|
|
25
26
|
extra_rdoc_files:
|
@@ -31,9 +32,25 @@ files:
|
|
31
32
|
- LICENSE
|
32
33
|
- README.rdoc
|
33
34
|
- Rakefile
|
35
|
+
- VERSION
|
36
|
+
- bin/msg_pop.rb
|
37
|
+
- bin/msg_probe.rb
|
38
|
+
- bin/msg_push.rb
|
39
|
+
- bin/msg_sink.rb
|
40
|
+
- bin/msg_subscribe.rb
|
41
|
+
- lib/pipeline_toolkit.rb
|
42
|
+
- lib/pipeline_toolkit/message_coder.rb
|
43
|
+
- lib/pipeline_toolkit/message_command.rb
|
44
|
+
- lib/pipeline_toolkit/message_popper.rb
|
45
|
+
- lib/pipeline_toolkit/message_probe.rb
|
46
|
+
- lib/pipeline_toolkit/message_pusher.rb
|
47
|
+
- lib/pipeline_toolkit/message_sink.rb
|
48
|
+
- lib/pipeline_toolkit/message_subscriber.rb
|
49
|
+
- lib/pipeline_toolkit/open_hash.rb
|
34
50
|
- perf_test/test_pop.rb
|
35
51
|
- perf_test/test_pop_async.rb
|
36
52
|
- perf_test/test_push.rb
|
53
|
+
- pipeline_toolkit.gemspec
|
37
54
|
has_rdoc: false
|
38
55
|
homepage: http://github.com/visfleet/pipeline_toolkit
|
39
56
|
licenses:
|
data/bin/push.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'rubygems'
|
4
|
-
require 'trollop'
|
5
|
-
require 'pipeline_toolkit'
|
6
|
-
|
7
|
-
opts = Trollop::options do
|
8
|
-
opt :destinations, "Destinations", :short => "d", :type => :strings
|
9
|
-
opt :route_eval, "A string of ruby code that ", :short => "e", :type => :string
|
10
|
-
opt :route_file, "A string of ruby code that ", :short => "f", :type => :string
|
11
|
-
|
12
|
-
# Msg server
|
13
|
-
opt :host, "The AMQP message server host", :default => "localhost"
|
14
|
-
opt :port, "The AMQP message server port", :default => "5672"
|
15
|
-
opt :user, "The AMQP message server username", :default => "guest"
|
16
|
-
opt :pass, "The AMQP message server username", :default => "guest"
|
17
|
-
opt :vhost, "The AMQP message server vhost", :default => "/"
|
18
|
-
end
|
19
|
-
|
20
|
-
MessagePusher.new(opts).start
|
21
|
-
|
22
|
-
# pop -s raw | thingie | push -d my_queue
|
23
|
-
# pop -s raw | thingie | push -f "(msg.tmu_id.hash % NUMBER_OF_FORKS).to_s"
|
24
|
-
# pop -s raw | thingie | push -f "Socket.gethostname"
|
25
|
-
# pop -s raw | thingie | push -f "require route_to_site"
|