rflow-components-amqp 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+ require 'rdoc/task'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ RSpec::Core::RakeTask.new(:spec) do |t|
7
+ t.verbose = true
8
+ t.rspec_opts = '--tty --color'
9
+ end
10
+
11
+ Rake::RDocTask.new do |rd|
12
+ rd.main = "README"
13
+ rd.rdoc_files.include("README", "lib/**/*.rb")
14
+ rd.rdoc_dir = File.join('doc', 'html')
15
+ end
@@ -0,0 +1,27 @@
1
+ class RFlow
2
+ module Components
3
+ module AMQP
4
+
5
+ # The set of extensions to add capability to AMQP data types
6
+ module Extensions
7
+
8
+ module AMQPMessageExtension
9
+ def self.extended(base_data)
10
+ base_data.data_object ||= {'header' => {}, 'payload' => ''}
11
+ end
12
+
13
+ # Default accessors
14
+ ['header', 'payload'].each do |name|
15
+ define_method name do |*args|
16
+ data_object[name]
17
+ end
18
+ define_method :"#{name}=" do |*args|
19
+ data_object[name] = args.first
20
+ end
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,113 @@
1
+ require 'eventmachine'
2
+ require 'amqp'
3
+ require 'rflow'
4
+
5
+ class RFlow
6
+ module Components
7
+ module AMQP
8
+
9
+ # By default will use and exclusive, non-durable, auto-deleting,
10
+ # randomly named queue for subscribing to topic messages with an
11
+ # empty-string pattern
12
+ class Subscriber < RFlow::Component
13
+ output_port :amqp_port
14
+ output_port :raw_port
15
+
16
+ attr_accessor :config, :queue_config
17
+
18
+ DEFAULT_CONFIG = {
19
+ 'server' => '127.0.0.1',
20
+ 'port' => 5672,
21
+ 'username' => 'guest',
22
+ 'password' => 'guest',
23
+ 'vhost' => '/',
24
+
25
+ 'queue_name' => 'asdf',
26
+ 'queue_passive' => false,
27
+ 'queue_durable' => false,
28
+ 'queue_exclusive' => true,
29
+ 'queue_auto_delete' => true,
30
+
31
+ 'binding_pattern' => '',
32
+ }
33
+
34
+ def configure!(config)
35
+ @config = DEFAULT_CONFIG.merge config
36
+ @config['port'] = @config['port'].to_i
37
+
38
+ ['durable', 'passive', 'exclusive', 'auto_delete'].each do |queue_bool_opt|
39
+ @config["queue_#{queue_bool_opt}"] = to_boolean(@config["queue_#{queue_bool_opt}"])
40
+ end
41
+
42
+ # Convert the queue parameters into AMQP-friendly sym-keyed
43
+ # Hash that can be passed directly to underlying AMQP gem
44
+ # methods
45
+ @queue_config = {}
46
+ @config.each do |key, value|
47
+ md = /queue_(.*)/.match(key.to_s)
48
+ unless md.nil?
49
+ @queue_config[md[1].to_sym] = value
50
+ end
51
+ end
52
+ end
53
+
54
+
55
+ def run!
56
+ ::AMQP.connect(:host => @config['server'], :port => @config['port'], :vhost => @config['vhost'], :username => @config['username'], :password => @config['password']) do |conn|
57
+ @amqp_connection = conn
58
+
59
+ ::AMQP::Channel.new(@amqp_connection) do |channel|
60
+ @amqp_channel = channel
61
+ @amqp_exchange = @amqp_channel.topic
62
+
63
+ ::AMQP::Queue.new(@amqp_channel, @config['queue_name'], @queue_config) do |queue|
64
+ @amqp_queue = queue
65
+ @amqp_queue.bind(@amqp_exchange, :routing_key => @config['binding_pattern']).subscribe(:ack => true) do |header, payload|
66
+ RFlow.logger.debug "AMQP message received"
67
+ processing_event = RFlow::Message::ProcessingEvent.new(instance_uuid, Time.now.utc)
68
+
69
+ amqp_message = RFlow::Message.new('RFlow::Message::Data::AMQP::Message')
70
+ header.to_hash.each {|k,v| amqp_message.data.header[k.to_s] = v}
71
+ amqp_message.data.payload = payload
72
+
73
+ # TODO: Optimize out if not connected
74
+ raw_message = RFlow::Message.new('RFlow::Message::Data::Raw')
75
+ raw_message.data.raw = payload
76
+
77
+ processing_event.completed_at = Time.now.utc
78
+ amqp_message.provenance << processing_event
79
+ raw_message.provenance << processing_event
80
+
81
+ amqp_port.send_message amqp_message
82
+ raw_port.send_message raw_message
83
+
84
+ header.ack
85
+ end
86
+ end
87
+
88
+ end
89
+ end
90
+
91
+ # EM.add_timer(2) do
92
+ # EM.add_periodic_timer(0) do
93
+ # @amqp_exchange.publish Array.new(rand(1000)) { rand(256) }.pack('c*'), :routing_key => ""
94
+ # end
95
+ # end
96
+ end
97
+
98
+ def to_boolean(string)
99
+ case string
100
+ when /^true$/i, '1', true
101
+ true
102
+ when /^false/i, '0', false
103
+ false
104
+ else
105
+ raise ArgumentError, "'#{string}' cannot be coerced to a boolean value"
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,7 @@
1
+ class RFlow
2
+ module Components
3
+ module AMQP
4
+ VERSION = "0.0.2"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ require 'rflow/components/amqp/extensions'
2
+ require 'rflow/components/amqp/subscriber'
3
+
4
+ class RFlow
5
+ module Components
6
+ module AMQP
7
+ # Load the schemas
8
+ SCHEMA_DIRECTORY = ::File.expand_path(::File.join(::File.dirname(__FILE__), '..', '..', '..', 'schema'))
9
+
10
+ SCHEMA_FILES = {
11
+ 'amqp_message.avsc' => 'RFlow::Message::Data::AMQP::Message',
12
+ }
13
+
14
+ SCHEMA_FILES.each do |file_name, data_type_name|
15
+ schema_string = ::File.read(::File.join(SCHEMA_DIRECTORY, file_name))
16
+ RFlow::Configuration.add_available_data_type data_type_name, 'avro', schema_string
17
+ end
18
+
19
+ # Load the data extensions
20
+ RFlow::Configuration.add_available_data_extension('RFlow::Message::Data::AMQP::Message',
21
+ RFlow::Components::AMQP::Extensions::AMQPMessageExtension)
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,2 @@
1
+ require 'rflow'
2
+ require 'rflow/components/amqp'
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rflow/components/amqp/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rflow-components-amqp"
7
+ s.version = RFlow::Components::AMQP::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.required_ruby_version = '~> 1.9'
10
+ s.authors = ["Michael L. Artz"]
11
+ s.email = ["michael.artz@redjack.com"]
12
+ s.homepage = "https://github.com/redjack/rflow-components-amqp"
13
+ s.summary = %q{AMQP publisher and subscriber component for the RFlow FBP framework}
14
+ s.description = %q{AMQP publisher and subscriber component for the RFlow FBP framework. Also includes the necessary AMQP::Message message type}
15
+
16
+ s.rubyforge_project = "rflow-components-amqp"
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+
23
+ s.add_dependency 'rflow', '~> 0.0.1'
24
+ s.add_dependency 'amqp', '>= 0.8.0.rc12'
25
+
26
+ s.add_development_dependency 'rspec', '~> 2.5.0'
27
+ s.add_development_dependency 'rake', '~> 0.8.7'
28
+ end
@@ -0,0 +1,15 @@
1
+ {
2
+ "type": "record",
3
+ "name": "Message",
4
+ "namespace": "org.rflow.message.data.amqp",
5
+ "aliases": [],
6
+ "fields": [
7
+ {"name": "client_ip", "type": ["string", "null"]},
8
+ {"name": "client_port", "type": ["int", "null"]},
9
+ {"name": "server_ip", "type": ["string", "null"]},
10
+ {"name": "server_port", "type": ["int", "null"]},
11
+
12
+ {"name": "header", "type": {"type": "map", "values": ["string", "int", "null"]}},
13
+ {"name": "payload", "type": "bytes"}
14
+ ]
15
+ }
@@ -0,0 +1,47 @@
1
+ require 'spec_helper.rb'
2
+
3
+ # describe RFlow::Components::File::Extensions::FileExtension do
4
+ # before(:each) do
5
+ # @schema_string = RFlow::Configuration.available_data_types['RFlow::Message::Data::File']['avro']
6
+ # end
7
+ #
8
+ # it "should add the extension to RFlow::Configuration" do
9
+ # RFlow::Configuration.available_data_extensions['RFlow::Message::Data::File'].should include(described_class)
10
+ # end
11
+ #
12
+ # it "should set the defaults" do
13
+ # file = RFlow::Message.new('RFlow::Message::Data::File')
14
+ #
15
+ # file.data.path.should == '/'
16
+ # file.data.size.should == 0
17
+ # file.data.content.should == ''
18
+ # file.data.creation_timestamp.should == nil
19
+ # file.data.modification_timestamp.should == nil
20
+ # file.data.accessed_timestamp.should == nil
21
+ # end
22
+ #
23
+ # it "should correctly use integers or strings for size field" do
24
+ # file = RFlow::Message.new('RFlow::Message::Data::File')
25
+ #
26
+ # file.data.size.should == 0
27
+ # file.data.size = 10
28
+ # file.data.size.should == 10
29
+ # file.data.size = '20'
30
+ # file.data.size == 20
31
+ # end
32
+ #
33
+ # it "should correctly use Time or xmlschema strings for timestamp fields" do
34
+ # file = RFlow::Message.new('RFlow::Message::Data::File')
35
+ #
36
+ # file.data.creation_timestamp.should == nil
37
+ # now = Time.now
38
+ #
39
+ # file.data.creation_timestamp = now
40
+ # file.data.creation_timestamp.should == Time.xmlschema(now.xmlschema(9))
41
+ #
42
+ # file.data.creation_timestamp = now.xmlschema
43
+ # file.data.creation_timestamp.should == Time.xmlschema(now.xmlschema)
44
+ # end
45
+ #
46
+ #
47
+ # end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe 'RFlow::Message::Data::AMQP::Message Avro Schema' do
4
+ before(:each) do
5
+ @schema_string = RFlow::Configuration.available_data_types['RFlow::Message::Data::AMQP::Message']['avro']
6
+ end
7
+
8
+ it "should encode and decode an object" do
9
+ amqp_message = {
10
+ 'header' => {
11
+ 'content_type' => 'application/octet-stream',
12
+ 'delivery_mode' => 2,
13
+ 'priority' => 0,
14
+ },
15
+ 'payload' =>'PAYLOAD',
16
+ }
17
+
18
+ expect {encode_avro(@schema_string, amqp_message)}.to_not raise_error
19
+ avro_encoded_amqp_message = encode_avro(@schema_string, amqp_message)
20
+
21
+ expect {decode_avro(@schema_string, avro_encoded_amqp_message)}.to_not raise_error
22
+ decoded_amqp_message = decode_avro(@schema_string, avro_encoded_amqp_message)
23
+
24
+ decoded_amqp_message['payload'].should == amqp_message['payload']
25
+ decoded_amqp_message['header'].should == amqp_message['header']
26
+
27
+ end
28
+ end
29
+
@@ -0,0 +1,21 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'rflow-components-amqp'))
2
+
3
+ require 'logger'
4
+
5
+ RFlow.logger = Logger.new STDOUT
6
+
7
+ def decode_avro(schema_string, serialized_object)
8
+ schema = Avro::Schema.parse(schema_string)
9
+ serialized_object.force_encoding 'BINARY'
10
+ sio = StringIO.new(serialized_object)
11
+ Avro::IO::DatumReader.new(schema, schema).read Avro::IO::BinaryDecoder.new(sio)
12
+ end
13
+
14
+ def encode_avro(schema_string, object)
15
+ encoded_string = ''
16
+ encoded_string.force_encoding 'BINARY'
17
+ schema = Avro::Schema.parse(schema_string)
18
+ sio = StringIO.new(encoded_string)
19
+ Avro::IO::DatumWriter.new(schema).write object, Avro::IO::BinaryEncoder.new(sio)
20
+ encoded_string
21
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rflow-components-amqp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael L. Artz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-03-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rflow
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.0.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.0.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: amqp
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.8.0.rc12
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.8.0.rc12
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 2.5.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.5.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.8.7
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 0.8.7
78
+ description: AMQP publisher and subscriber component for the RFlow FBP framework. Also
79
+ includes the necessary AMQP::Message message type
80
+ email:
81
+ - michael.artz@redjack.com
82
+ executables: []
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - Rakefile
89
+ - lib/rflow-components-amqp.rb
90
+ - lib/rflow/components/amqp.rb
91
+ - lib/rflow/components/amqp/extensions.rb
92
+ - lib/rflow/components/amqp/subscriber.rb
93
+ - lib/rflow/components/amqp/version.rb
94
+ - rflow-components-amqp.gemspec
95
+ - schema/amqp_message.avsc
96
+ - spec/extensions_spec.rb
97
+ - spec/schema_spec.rb
98
+ - spec/spec_helper.rb
99
+ homepage: https://github.com/redjack/rflow-components-amqp
100
+ licenses: []
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: '1.9'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubyforge_project: rflow-components-amqp
119
+ rubygems_version: 1.8.24
120
+ signing_key:
121
+ specification_version: 3
122
+ summary: AMQP publisher and subscriber component for the RFlow FBP framework
123
+ test_files:
124
+ - spec/extensions_spec.rb
125
+ - spec/schema_spec.rb
126
+ - spec/spec_helper.rb