ZDevice 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in zdevice.gemspec
4
+ gemspec
@@ -0,0 +1,39 @@
1
+ # ZDevice
2
+
3
+ ZDevice is a Ruby DSL for assembling arbitrary ZeroMQ routing devices, with support for the ZDCF configuration syntax.
4
+
5
+ ## Example: PUB/SUB queue device
6
+
7
+ b = ZMQ::Device::Builder.new(
8
+ context: { iothreads: 5 },
9
+ main: {
10
+ type: :queue,
11
+ frontend: { type: :SUB, option: { hwm: 1, swap: 25}, connect: ["tcp://127.0.0.1:5555"]},
12
+ backend: { type: :PUB, bind: ["tcp://127.0.0.1:5556"] }
13
+ }
14
+ )
15
+
16
+ # relay all incoming messages to all subscribers
17
+ b.main.start do
18
+ loop do
19
+ msg = ZMQ::Message.new
20
+ frontend.recv msg
21
+ backend.send msg
22
+ end
23
+ end
24
+
25
+ ## Resources
26
+
27
+ * [ZDCF JSON specification](http://rfc.zeromq.org/spec:3)
28
+
29
+ ## License
30
+
31
+ (The MIT License)
32
+
33
+ Copyright (c) 2010 Ilya Grigorik
34
+
35
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
36
+
37
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
38
+
39
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1 @@
1
+ Autotest.add_discovery { 'rspec2' }
@@ -0,0 +1,119 @@
1
+ require 'ffi-rzmq'
2
+
3
+ module ZMQ
4
+ module Device
5
+
6
+ class Builder
7
+ def initialize(conf = {})
8
+ conf = symbolize_keys(conf)
9
+
10
+ @context = Context.new(conf.delete(:context))
11
+ @devices = {}
12
+
13
+ conf.each do |name, c|
14
+ @devices[name] = Device.new(name, @context.ctx, c)
15
+ (class << self; self; end).class_eval do
16
+ define_method "#{name}" do
17
+ @devices[name]
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ def symbolize_keys(conf)
24
+ recursive = Proc.new do |h, sh|
25
+ h.each do |k,v|
26
+ sh[k.to_sym] = v.is_a?(Hash) ? recursive.call(v, {}) : v
27
+ end
28
+ sh
29
+ end
30
+
31
+ recursive.call(conf, {})
32
+ end
33
+
34
+ def close
35
+ @devices.values.map { |d| d.close }
36
+ end
37
+ end
38
+
39
+ class Context
40
+ attr_reader :iothreads, :verbose, :ctx
41
+ def initialize(conf = {})
42
+ @iothreads = conf[:iothreads] || 1
43
+ @verbose = conf[:verbose] || false
44
+
45
+ @ctx = ZMQ::Context.new(@iothreads)
46
+ end
47
+ end
48
+
49
+ class Device
50
+ attr_reader :name, :type
51
+ def initialize(name, ctx, conf = {}, &blk)
52
+ raise 'invalid name' if name == 'context'
53
+ raise 'missing type' if !conf.key? :type
54
+
55
+ @name = name
56
+ @type = conf.delete(:type)
57
+ @sockets = {}
58
+
59
+ conf.each do |name, c|
60
+ @sockets[name] = ZSocket.new(name, ctx, c)
61
+ (class << self; self; end).class_eval do
62
+ define_method "#{name}" do
63
+ @sockets[name].socket
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ def start(&blk)
70
+ instance_eval &blk
71
+ end
72
+
73
+ def close
74
+ @sockets.values.map {|s| s.close }
75
+ end
76
+ end
77
+
78
+ class ZSocket
79
+ attr_reader :name, :type, :socket
80
+ def initialize(name, ctx = nil, conf = {})
81
+ raise 'invalid name' if name == 'type'
82
+ raise 'missing type' if !conf.key? :type
83
+
84
+ @name = name
85
+ conf[:option] ||= {}
86
+ @type = case conf.delete(:type).downcase
87
+ when :pub then ZMQ::PUB
88
+ when :sub then ZMQ::SUB
89
+ else 1
90
+ end
91
+
92
+ # if no filter is specified, then accept all messages by default
93
+ if @type == ZMQ::SUB
94
+ conf[:option][:subscribe] = '' if !conf[:option][:subscribe]
95
+ end
96
+
97
+ @socket = ctx.socket @type
98
+ (conf[:bind] || []).each { |addr| @socket.bind addr }
99
+ (conf[:connect] || []).each { |addr| @socket.connect(addr) }
100
+
101
+ conf[:option].each do |k, v|
102
+ flag = case k
103
+ when :subscribe then ZMQ::SUBSCRIBE
104
+ when :hwm then ZMQ::HWM
105
+ when :swap then ZMQ::SWAP
106
+ end
107
+
108
+ [v].flatten.map {|val| @socket.setsockopt(flag, val) }
109
+ end
110
+ end
111
+
112
+ def close
113
+ @socket.close
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ end
@@ -0,0 +1,3 @@
1
+ module Zdevice
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,84 @@
1
+ require 'lib/zdevice'
2
+
3
+ include ZMQ::Device
4
+
5
+ describe "relay device" do
6
+
7
+ it "should assemble a one-time relay ZMQ Device" do
8
+ b = Builder.new(
9
+ context: { iothreads: 5 },
10
+ main: {
11
+ type: :queue,
12
+
13
+ frontend: {
14
+ type: :SUB,
15
+ option: { hwm: 1, swap: 25},
16
+ connect: ["tcp://127.0.0.1:5555"]
17
+ },
18
+
19
+ backend: {
20
+ type: :PUB,
21
+ bind: ["tcp://127.0.0.1:5556"] }
22
+ }
23
+ )
24
+
25
+ b.main.class.should == Device
26
+ b.main.type.should == :queue
27
+
28
+ # Create producer socket
29
+ sctx = ZMQ::Context.new
30
+ pub = sctx.socket(ZMQ::PUB)
31
+ pub.bind("tcp://127.0.0.1:5555")
32
+
33
+ # Start relay device
34
+ Thread.new do
35
+ b.main.start do
36
+ msg = ZMQ::Message.new
37
+ frontend.recv msg
38
+ backend.send msg
39
+ end
40
+ end
41
+
42
+ # Start producer
43
+ Thread.new do
44
+ loop do
45
+ pub.send ZMQ::Message.new("queue test")
46
+ end
47
+ end
48
+
49
+ # Consume re-routed message from producer
50
+ sub = sctx.socket(ZMQ::SUB)
51
+ sub.setsockopt(ZMQ::SUBSCRIBE, '')
52
+ sub.connect("tcp://127.0.0.1:5556")
53
+
54
+ rmsg = ZMQ::Message.new
55
+ sub.recv rmsg
56
+
57
+ rmsg.copy_out_string.should == "queue test"
58
+
59
+ pub.close
60
+ sub.close
61
+ b.close
62
+ end
63
+
64
+ it "should have more Ruby friendly syntax" do
65
+ pending "maybe"
66
+
67
+ d = Device.new do
68
+ context iothreads: 5, verbose: false
69
+
70
+ device :queue do
71
+ socket :frontend do
72
+ type ZMQ::SUB
73
+ option hwm: 1, swap: 25
74
+ connect ['']
75
+ bind ['']
76
+ end
77
+ end
78
+ end
79
+
80
+ d.queue.start do
81
+ end
82
+ end
83
+
84
+ end
@@ -0,0 +1,63 @@
1
+ require 'lib/zdevice'
2
+
3
+ include ZMQ::Device
4
+
5
+ describe ZMQ::Device do
6
+
7
+ let(:ctx) { ZMQ::Context.new }
8
+
9
+ context Builder do
10
+ it "should symbolize configuration keys" do
11
+ b = Builder.new(
12
+ 'context' => { 'iothreads' => 5 },
13
+ 'main' => {
14
+ 'type' => :queue,
15
+ }
16
+ )
17
+
18
+ b.main.class.should == Device
19
+ b.main.type.should == :queue
20
+ end
21
+ end
22
+
23
+ context Context do
24
+ it "should accept optional iothread count" do
25
+ c = Context.new(iothreads: 5)
26
+ c.iothreads.should == 5
27
+ end
28
+
29
+ it "should accept optional verbose flag" do
30
+ c = Context.new(verbose: true)
31
+ c.verbose.should be_true
32
+ end
33
+ end
34
+
35
+ context Device do
36
+ it "should validate type" do
37
+ lambda { Device.new('context') }.should raise_error
38
+ end
39
+
40
+ it "should validate name" do
41
+ lambda { Device.new('context', ctx, type: :a) }.should raise_error
42
+ d = Device.new('valid', nil, type: :a)
43
+ d.name.should == 'valid'
44
+ end
45
+ end
46
+
47
+ context ZSocket do
48
+ it "should have any name except 'type'" do
49
+ lambda { ZSocket.new('type') }.should raise_error('invalid name')
50
+
51
+ s = ZSocket.new('valid', ctx, type: :queue)
52
+ s.name.should == 'valid'
53
+ s.close
54
+ end
55
+
56
+ it "should have a type" do
57
+ s = ZSocket.new('valid', ctx, type: :queue)
58
+ s.type.should >= 0
59
+ s.close
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "zdevice/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ZDevice"
7
+ s.version = Zdevice::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ilya Grigorik"]
10
+ s.email = ["ilya@igvita.com"]
11
+ s.homepage = "http://github.com/igrigorik/zdevice"
12
+ s.summary = %q{ZDevice is a Ruby DSL for assembling arbitrary ZeroMQ routing devices, with support for the ZDCF configuration syntax.}
13
+ s.description = s.summary
14
+
15
+ s.rubyforge_project = "zdevice"
16
+ s.add_dependency "ffi"
17
+ s.add_dependency "ffi-rzmq"
18
+ s.add_development_dependency "rspec"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ZDevice
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Ilya Grigorik
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-11-17 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: ffi
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: ffi-rzmq
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ type: :runtime
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ prerelease: false
49
+ requirement: &id003 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 0
56
+ version: "0"
57
+ type: :development
58
+ version_requirements: *id003
59
+ description: ZDevice is a Ruby DSL for assembling arbitrary ZeroMQ routing devices, with support for the ZDCF configuration syntax.
60
+ email:
61
+ - ilya@igvita.com
62
+ executables: []
63
+
64
+ extensions: []
65
+
66
+ extra_rdoc_files: []
67
+
68
+ files:
69
+ - .gitignore
70
+ - Gemfile
71
+ - README.md
72
+ - Rakefile
73
+ - autotest/discover.rb
74
+ - lib/zdevice.rb
75
+ - lib/zdevice/version.rb
76
+ - spec/relay_spec.rb
77
+ - spec/zdevice_spec.rb
78
+ - zdevice.gemspec
79
+ has_rdoc: true
80
+ homepage: http://github.com/igrigorik/zdevice
81
+ licenses: []
82
+
83
+ post_install_message:
84
+ rdoc_options: []
85
+
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ segments:
94
+ - 0
95
+ version: "0"
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ requirements: []
105
+
106
+ rubyforge_project: zdevice
107
+ rubygems_version: 1.3.7
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: ZDevice is a Ruby DSL for assembling arbitrary ZeroMQ routing devices, with support for the ZDCF configuration syntax.
111
+ test_files:
112
+ - spec/relay_spec.rb
113
+ - spec/zdevice_spec.rb