agent_zmq 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.rspec +1 -0
- data/.travis.yml +6 -0
- data/CONTRIBUTION_GUIDELINES.md +22 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +76 -0
- data/LICENSE.txt +14 -0
- data/README.md +182 -0
- data/Rakefile +35 -0
- data/VERSION +1 -0
- data/agent_zmq.gemspec +78 -0
- data/config/zmq_agents.rb +14 -0
- data/features/MessageInspection.feature +13 -0
- data/features/step_definitions/steps.rb +7 -0
- data/features/support/env.rb +8 -0
- data/lib/agent_zmq.rb +71 -0
- data/lib/agent_zmq/agent.rb +4 -0
- data/lib/agent_zmq/agents/base_agent.rb +31 -0
- data/lib/agent_zmq/agents/pub_agent.rb +31 -0
- data/lib/agent_zmq/agents/rep_agent.rb +65 -0
- data/lib/agent_zmq/agents/req_agent.rb +32 -0
- data/lib/agent_zmq/agents/sub_agent.rb +66 -0
- data/lib/agent_zmq/cucumber.rb +32 -0
- data/lib/agent_zmq/helpers.rb +32 -0
- data/lib/agent_zmq/message_cache.rb +36 -0
- data/spec/agent_zmq/message_cache_spec.rb +47 -0
- data/spec/spec_helper.rb +12 -0
- metadata +128 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c1810d095c41ef4752fbde11dfcdd2cc1b156c7a
|
4
|
+
data.tar.gz: b95f4154d6e47fabd960273cf02fb1f8e0997785
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7f9b59119a7a03c1a998fb39545f33ffa6d36aa9ec9d4fd0f9ecbc38dea42f58dc3491516e7066d3cd46fab0b988130efb924f087134f3a16f50e219c104abcb
|
7
|
+
data.tar.gz: 7922703be2d5ac0b9267dbdffe5ae62581263745aa2fc1b866f0796677e0510235f03fa90ed94412e2fac5471969a1cf071d5b5364d63df6ad024c2450b6c823
|
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
If you come across any issues, please [tell us](https://github.com/connamara/agent_zmq/issues).
|
2
|
+
Pull requests (with tests) are appreciated. No pull request is too small. Please help with:
|
3
|
+
|
4
|
+
* Reporting bugs
|
5
|
+
* Suggesting features
|
6
|
+
* Writing or improving documentation
|
7
|
+
* Fixing typos
|
8
|
+
* Cleaning whitespace
|
9
|
+
* Refactoring code
|
10
|
+
* Adding tests
|
11
|
+
* Closing [issues](https://github.com/connamara/agent_zmq/issues)
|
12
|
+
|
13
|
+
Contributing to agent_zmq:
|
14
|
+
|
15
|
+
1. Fork the [official repository](https://github.com/connamara/agent_zmq/tree/master).
|
16
|
+
2. Make your changes in a topic branch.
|
17
|
+
3. Send a pull request.
|
18
|
+
|
19
|
+
Notes:
|
20
|
+
* If you report a bug and don't include a fix, please include a failing test.
|
21
|
+
* Contributions without tests won't be accepted.
|
22
|
+
* Please don't update the Gem version
|
data/Gemfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem 'ffi-rzmq', "~> 0.9.3"
|
4
|
+
gem "rspec", "~> 2.14"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "jeweler", "~> 1.8"
|
10
|
+
gem "cucumber", "~> 1.3"
|
11
|
+
gem "rake", "~> 10.1"
|
12
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.3.5)
|
5
|
+
builder (3.2.2)
|
6
|
+
cucumber (1.3.6)
|
7
|
+
builder (>= 2.1.2)
|
8
|
+
diff-lcs (>= 1.1.3)
|
9
|
+
gherkin (~> 2.12.0)
|
10
|
+
multi_json (~> 1.7.5)
|
11
|
+
multi_test (>= 0.0.2)
|
12
|
+
diff-lcs (1.2.4)
|
13
|
+
faraday (0.8.8)
|
14
|
+
multipart-post (~> 1.2.0)
|
15
|
+
ffi (1.9.0)
|
16
|
+
ffi-rzmq (0.9.7)
|
17
|
+
ffi
|
18
|
+
gherkin (2.12.1)
|
19
|
+
multi_json (~> 1.3)
|
20
|
+
git (1.2.6)
|
21
|
+
github_api (0.10.1)
|
22
|
+
addressable
|
23
|
+
faraday (~> 0.8.1)
|
24
|
+
hashie (>= 1.2)
|
25
|
+
multi_json (~> 1.4)
|
26
|
+
nokogiri (~> 1.5.2)
|
27
|
+
oauth2
|
28
|
+
hashie (2.0.5)
|
29
|
+
highline (1.6.19)
|
30
|
+
httpauth (0.2.0)
|
31
|
+
jeweler (1.8.7)
|
32
|
+
builder
|
33
|
+
bundler (~> 1.0)
|
34
|
+
git (>= 1.2.5)
|
35
|
+
github_api (= 0.10.1)
|
36
|
+
highline (>= 1.6.15)
|
37
|
+
nokogiri (= 1.5.10)
|
38
|
+
rake
|
39
|
+
rdoc
|
40
|
+
json (1.8.0)
|
41
|
+
jwt (0.1.8)
|
42
|
+
multi_json (>= 1.5)
|
43
|
+
multi_json (1.7.9)
|
44
|
+
multi_test (0.0.2)
|
45
|
+
multi_xml (0.5.5)
|
46
|
+
multipart-post (1.2.0)
|
47
|
+
nokogiri (1.5.10)
|
48
|
+
oauth2 (0.9.2)
|
49
|
+
faraday (~> 0.8)
|
50
|
+
httpauth (~> 0.2)
|
51
|
+
jwt (~> 0.1.4)
|
52
|
+
multi_json (~> 1.0)
|
53
|
+
multi_xml (~> 0.5)
|
54
|
+
rack (~> 1.2)
|
55
|
+
rack (1.5.2)
|
56
|
+
rake (10.1.0)
|
57
|
+
rdoc (4.0.1)
|
58
|
+
json (~> 1.4)
|
59
|
+
rspec (2.14.1)
|
60
|
+
rspec-core (~> 2.14.0)
|
61
|
+
rspec-expectations (~> 2.14.0)
|
62
|
+
rspec-mocks (~> 2.14.0)
|
63
|
+
rspec-core (2.14.5)
|
64
|
+
rspec-expectations (2.14.2)
|
65
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
66
|
+
rspec-mocks (2.14.3)
|
67
|
+
|
68
|
+
PLATFORMS
|
69
|
+
ruby
|
70
|
+
|
71
|
+
DEPENDENCIES
|
72
|
+
cucumber (~> 1.3)
|
73
|
+
ffi-rzmq (~> 0.9.3)
|
74
|
+
jeweler (~> 1.8)
|
75
|
+
rake (~> 10.1)
|
76
|
+
rspec (~> 2.14)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Copyright (C) 2013 Connamara Systems, llc
|
2
|
+
|
3
|
+
This program is free software: you can redistribute it and/or modify
|
4
|
+
it under the terms of the GNU General Public License as published by
|
5
|
+
the Free Software Foundation, either version 3 of the License, or
|
6
|
+
(at your option) any later version.
|
7
|
+
|
8
|
+
This program is distributed in the hope that it will be useful,
|
9
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
GNU General Public License for more details.
|
12
|
+
|
13
|
+
You should have received a copy of the GNU General Public License
|
14
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
data/README.md
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
agent\_zmq [![Build Status](https://travis-ci.org/connamara/agent_zmq.png?branch=master)](https://travis-ci.org/connamara/agent_zmq)
|
2
|
+
============
|
3
|
+
|
4
|
+
Agent framework designed for testing [ZeroMQ](http://zeromq.org/) Applications
|
5
|
+
|
6
|
+
Usage
|
7
|
+
-----
|
8
|
+
|
9
|
+
|
10
|
+
### Agent Types
|
11
|
+
|
12
|
+
The agent types correspond to underlying ZMQ Socket type under test
|
13
|
+
|
14
|
+
#### ZMQ\_SUB
|
15
|
+
|
16
|
+
* Connects or Binds to local address
|
17
|
+
* Includes message caching for inspection
|
18
|
+
|
19
|
+
#### ZMQ\_PUB
|
20
|
+
|
21
|
+
* Connects or Binds to local address
|
22
|
+
* Publishes
|
23
|
+
|
24
|
+
#### ZMQ\_REQ
|
25
|
+
|
26
|
+
* Connects or Binds to local address
|
27
|
+
* Publishes request, returns response
|
28
|
+
|
29
|
+
#### ZMQ\_REP
|
30
|
+
|
31
|
+
* Connects or Binds to local address
|
32
|
+
* Listens for request, sends response
|
33
|
+
|
34
|
+
|
35
|
+
### Configuration
|
36
|
+
|
37
|
+
Inside of your project, declare your agents inside of ```config/zmq_agents.rb``` like this:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
require 'agent_zmq'
|
41
|
+
|
42
|
+
AgentZMQ.define_ZMQ_SUB :my_sub_agent do |a|
|
43
|
+
a.socket_opts << {ZMQ::SUBSCRIBE=>'com.connamara.BODPosition'}
|
44
|
+
a.end_point_type=:bind
|
45
|
+
a.end_point='tcp://*:5556'
|
46
|
+
end
|
47
|
+
|
48
|
+
AgentZMQ.define_ZMQ_PUB :my_pub_agent do |a|
|
49
|
+
a.end_point_type=:connect
|
50
|
+
a.end_point='tcp://127.0.0.1:5558'
|
51
|
+
end
|
52
|
+
|
53
|
+
AgentZMQ.define_ZMQ_REQ :my_req_agent do |a|
|
54
|
+
a.end_point_type=:connect
|
55
|
+
a.end_point='tcp://127.0.0.1:5552'
|
56
|
+
end
|
57
|
+
|
58
|
+
AgentZMQ.define_ZMQ_REP :my_rep_agent do |a|
|
59
|
+
a.reply = Proc.new {|msg| "ok"}
|
60
|
+
a.end_point_type=:bind
|
61
|
+
a.end_point='tcp://*:5552'
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
### Starting, Stopping and Resetting
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
require 'agent_zmq'
|
69
|
+
AgentZMQ.start
|
70
|
+
at_exit { AgentZMQ.stop }
|
71
|
+
```
|
72
|
+
|
73
|
+
You may want to reset the agent states between tests without stopping and starting. This may be done with ```AgentZMQ.reset```
|
74
|
+
|
75
|
+
### Getting your agent
|
76
|
+
|
77
|
+
Grab the agent by the name given in the config file
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
my_agent = AgentZMQ.agents_hash[:my_sub_agent]
|
81
|
+
```
|
82
|
+
|
83
|
+
### Agent Interfaces
|
84
|
+
|
85
|
+
|
86
|
+
#### ZMQ\_SUB
|
87
|
+
|
88
|
+
This agent provides a message cache
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
all_messages_received = my_sub_agent.messages_received
|
92
|
+
|
93
|
+
# returns and removes the last message received from the cache
|
94
|
+
last_message_received = my_sub_agent.pop
|
95
|
+
```
|
96
|
+
|
97
|
+
On `reset`, the sub agent cache is cleared
|
98
|
+
|
99
|
+
#### ZMQ\_PUB
|
100
|
+
|
101
|
+
The ```publish``` method takes a single message of one or more parts
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
my_pub_agent.publish "single part message"
|
105
|
+
my_pub_agent.publish ["part 1", "part 2"]
|
106
|
+
```
|
107
|
+
|
108
|
+
#### ZMQ\_REQ
|
109
|
+
|
110
|
+
The ```publish``` method takes a single message of one or more parts. The agent blocks until a response is received and returned as an array of message parts
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
response = my_req_agent.publish "single part message"
|
114
|
+
response = my_pub_agent.publish ["part 1", "part 2"]
|
115
|
+
```
|
116
|
+
|
117
|
+
#### ZMQ\_REP
|
118
|
+
|
119
|
+
Like the ZMQ_SUB agent, ZMQ_REP provides a message cache
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
all_messages_received = my_rep_agent.messages_received
|
123
|
+
|
124
|
+
# returns and removes the last message received from the cache
|
125
|
+
last_message_received = my_rep_agent.pop
|
126
|
+
```
|
127
|
+
|
128
|
+
When receiving requests, the agent will reply with the output of the ```reply``` Proc. The return value of this proc may be in the form of a multi-part message.
|
129
|
+
|
130
|
+
### Cucumber
|
131
|
+
|
132
|
+
There is some support for cucumber. See [features](https://github.com/connamara/agent_zmq/blob/master/features) for example usage.
|
133
|
+
|
134
|
+
### More
|
135
|
+
|
136
|
+
Check out [specs](https://github.com/connamara/agent_zmq/blob/master/spec) and [features](https://github.com/connamara/agent_zmq/blob/master/features) to see all the ways you can use agent_zmq.
|
137
|
+
|
138
|
+
Install
|
139
|
+
-------
|
140
|
+
|
141
|
+
```shell
|
142
|
+
gem install agent_zmq
|
143
|
+
```
|
144
|
+
|
145
|
+
or add the following to Gemfile:
|
146
|
+
```ruby
|
147
|
+
gem 'agent_zmq'
|
148
|
+
```
|
149
|
+
and run `bundle install` from your shell.
|
150
|
+
|
151
|
+
More Information
|
152
|
+
----------------
|
153
|
+
|
154
|
+
* [Rubygems](https://rubygems.org/gems/agent_zmq)
|
155
|
+
* [Issues](https://github.com/connamara/agent_zmq/issues)
|
156
|
+
* [Connamara Systems](http://connamara.com)
|
157
|
+
|
158
|
+
Contributing
|
159
|
+
------------
|
160
|
+
|
161
|
+
Please see the [contribution guidelines](https://github.com/connamara/agent_zmq/blob/master/CONTRIBUTION_GUIDELINES.md).
|
162
|
+
|
163
|
+
Credits
|
164
|
+
-------
|
165
|
+
|
166
|
+
Contributers:
|
167
|
+
|
168
|
+
* Chris Busbey
|
169
|
+
* Brad Haan
|
170
|
+
|
171
|
+
![Connamara Systems](http://www.connamara.com/images/home-connamara-logo-lg.png)
|
172
|
+
|
173
|
+
agent_zmq is maintained and funded by [Connamara Systems, llc](http://connamara.com).
|
174
|
+
|
175
|
+
The names and logos for Connamara Systems are trademarks of Connamara Systems, llc.
|
176
|
+
|
177
|
+
Licensing
|
178
|
+
---------
|
179
|
+
|
180
|
+
agent_zmq is Copyright © 2013 Connamara Systems, llc.
|
181
|
+
|
182
|
+
This software is available under the GPL and a commercial license. Please see the [LICENSE](https://github.com/connamara/agent_zmq/blob/master/LICENSE.txt) file for the terms specified by the GPL license. The commercial license offers more flexible licensing terms compared to the GPL, and includes support services. [Contact us](mailto:info@connamara.com) for more information on the Connamara commercial license, what it enables, and how you can start developing with it.
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'rspec/core/rake_task'
|
14
|
+
require 'cucumber/rake/task'
|
15
|
+
|
16
|
+
RSpec::Core::RakeTask.new(:spec)
|
17
|
+
Cucumber::Rake::Task.new(:cucumber)
|
18
|
+
|
19
|
+
task :test => [:spec, :cucumber]
|
20
|
+
task :default => :test
|
21
|
+
|
22
|
+
|
23
|
+
require 'jeweler'
|
24
|
+
Jeweler::Tasks.new do |gem|
|
25
|
+
gem.name = "agent_zmq"
|
26
|
+
gem.homepage = "https://github.com/connamara/agent_zmq"
|
27
|
+
gem.license = "GPL"
|
28
|
+
gem.summary = %Q{Acceptance test framework for ZeroMQ applications}
|
29
|
+
gem.description = %Q{Acceptance test framework for ZeroMQ applications. Includes some cucumber helpers.}
|
30
|
+
gem.email = "info@connamara.com"
|
31
|
+
gem.authors = ["Chris Busbey"]
|
32
|
+
# dependencies defined in Gemfile
|
33
|
+
end
|
34
|
+
|
35
|
+
Jeweler::RubygemsDotOrgTasks.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.5.0
|
data/agent_zmq.gemspec
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "agent_zmq"
|
8
|
+
s.version = "0.5.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Chris Busbey"]
|
12
|
+
s.date = "2013-08-29"
|
13
|
+
s.description = "Acceptance test framework for ZeroMQ applications. Includes some cucumber helpers."
|
14
|
+
s.email = "info@connamara.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rspec",
|
22
|
+
".travis.yml",
|
23
|
+
"CONTRIBUTION_GUIDELINES.md",
|
24
|
+
"Gemfile",
|
25
|
+
"Gemfile.lock",
|
26
|
+
"LICENSE.txt",
|
27
|
+
"README.md",
|
28
|
+
"Rakefile",
|
29
|
+
"VERSION",
|
30
|
+
"agent_zmq.gemspec",
|
31
|
+
"config/zmq_agents.rb",
|
32
|
+
"features/MessageInspection.feature",
|
33
|
+
"features/step_definitions/steps.rb",
|
34
|
+
"features/support/env.rb",
|
35
|
+
"lib/agent_zmq.rb",
|
36
|
+
"lib/agent_zmq/agent.rb",
|
37
|
+
"lib/agent_zmq/agents/base_agent.rb",
|
38
|
+
"lib/agent_zmq/agents/pub_agent.rb",
|
39
|
+
"lib/agent_zmq/agents/rep_agent.rb",
|
40
|
+
"lib/agent_zmq/agents/req_agent.rb",
|
41
|
+
"lib/agent_zmq/agents/sub_agent.rb",
|
42
|
+
"lib/agent_zmq/cucumber.rb",
|
43
|
+
"lib/agent_zmq/helpers.rb",
|
44
|
+
"lib/agent_zmq/message_cache.rb",
|
45
|
+
"spec/agent_zmq/message_cache_spec.rb",
|
46
|
+
"spec/spec_helper.rb"
|
47
|
+
]
|
48
|
+
s.homepage = "https://github.com/connamara/agent_zmq"
|
49
|
+
s.licenses = ["GPL"]
|
50
|
+
s.require_paths = ["lib"]
|
51
|
+
s.rubygems_version = "2.0.7"
|
52
|
+
s.summary = "Acceptance test framework for ZeroMQ applications"
|
53
|
+
|
54
|
+
if s.respond_to? :specification_version then
|
55
|
+
s.specification_version = 4
|
56
|
+
|
57
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
58
|
+
s.add_runtime_dependency(%q<ffi-rzmq>, ["~> 0.9.3"])
|
59
|
+
s.add_runtime_dependency(%q<rspec>, ["~> 2.14"])
|
60
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8"])
|
61
|
+
s.add_development_dependency(%q<cucumber>, ["~> 1.3"])
|
62
|
+
s.add_development_dependency(%q<rake>, ["~> 10.1"])
|
63
|
+
else
|
64
|
+
s.add_dependency(%q<ffi-rzmq>, ["~> 0.9.3"])
|
65
|
+
s.add_dependency(%q<rspec>, ["~> 2.14"])
|
66
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8"])
|
67
|
+
s.add_dependency(%q<cucumber>, ["~> 1.3"])
|
68
|
+
s.add_dependency(%q<rake>, ["~> 10.1"])
|
69
|
+
end
|
70
|
+
else
|
71
|
+
s.add_dependency(%q<ffi-rzmq>, ["~> 0.9.3"])
|
72
|
+
s.add_dependency(%q<rspec>, ["~> 2.14"])
|
73
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8"])
|
74
|
+
s.add_dependency(%q<cucumber>, ["~> 1.3"])
|
75
|
+
s.add_dependency(%q<rake>, ["~> 10.1"])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
AgentZMQ.define_ZMQ_SUB :my_subscriber do |a|
|
2
|
+
a.socket_opts << {ZMQ::SUBSCRIBE=>""}
|
3
|
+
|
4
|
+
a.end_point_type=:connect
|
5
|
+
a.end_point='tcp://127.0.0.1:5560'
|
6
|
+
end
|
7
|
+
|
8
|
+
AgentZMQ.define_ZMQ_PUB :my_publisher do |a|
|
9
|
+
a.end_point_type=:bind
|
10
|
+
a.end_point='tcp://*:5560'
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
Feature: The Cucumber interface allows message inspection
|
2
|
+
|
3
|
+
Scenario: A message is received
|
4
|
+
|
5
|
+
Given publisher "my_publisher" sends the following:
|
6
|
+
|hello|world|
|
7
|
+
|
8
|
+
And I sleep 2 seconds
|
9
|
+
|
10
|
+
Then I should receive a message on ZeroMQ with agent "my_subscriber"
|
11
|
+
And the ZeroMQ message should have 2 parts
|
12
|
+
And part 1 of the ZeroMQ message should be "hello"
|
13
|
+
And part 2 of the ZeroMQ message should be "world"
|
data/lib/agent_zmq.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module AgentZMQ
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def agent_path
|
5
|
+
"./config/zmq_agents.rb"
|
6
|
+
end
|
7
|
+
|
8
|
+
def agents
|
9
|
+
return @agents if @agents
|
10
|
+
|
11
|
+
(@agents=[]).tap do
|
12
|
+
load_agents if agent_files_loaded.empty?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def agent_files_loaded
|
17
|
+
@agent_files_loaded ||=[]
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_agents path=nil
|
21
|
+
path = File.expand_path(path || agent_path, Dir.pwd)
|
22
|
+
return if agent_files_loaded.include? path
|
23
|
+
agent_files_loaded << path
|
24
|
+
load path
|
25
|
+
end
|
26
|
+
|
27
|
+
def define_agent(agent, &blk)
|
28
|
+
yield agent
|
29
|
+
agents << agent
|
30
|
+
end
|
31
|
+
|
32
|
+
def define_ZMQ_SUB(name, &blk)
|
33
|
+
define_agent(AgentZMQ::SubAgent.new(name), &blk)
|
34
|
+
end
|
35
|
+
|
36
|
+
def define_ZMQ_PUB(name, &blk)
|
37
|
+
define_agent(AgentZMQ::PubAgent.new(name), &blk)
|
38
|
+
end
|
39
|
+
|
40
|
+
def define_ZMQ_REQ(name, &blk)
|
41
|
+
define_agent(AgentZMQ::ReqAgent.new(name), &blk)
|
42
|
+
end
|
43
|
+
|
44
|
+
def define_ZMQ_REP(name, &blk)
|
45
|
+
define_agent(AgentZMQ::RepAgent.new(name), &blk)
|
46
|
+
end
|
47
|
+
|
48
|
+
#starts all configured agents
|
49
|
+
def start
|
50
|
+
raise RuntimeError, "No ZMQ Agents Defined" if agents.empty?
|
51
|
+
|
52
|
+
agents.each do |a|
|
53
|
+
a.start
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def stop
|
58
|
+
agents.each {|a| a.stop}
|
59
|
+
end
|
60
|
+
|
61
|
+
def reset
|
62
|
+
agents.each {|a| a.reset}
|
63
|
+
end
|
64
|
+
|
65
|
+
def agents_hash
|
66
|
+
Hash[agents.map { |a| [a.name.to_sym, a]}]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
require 'agent_zmq/helpers'
|
71
|
+
require 'agent_zmq/agent'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module AgentZMQ::BaseAgent
|
2
|
+
attr_reader :name
|
3
|
+
|
4
|
+
attr_accessor :end_point_type, :end_point, :socket_opts
|
5
|
+
|
6
|
+
def zmq_context
|
7
|
+
@ctx ||= ZMQ::Context.new 1
|
8
|
+
end
|
9
|
+
|
10
|
+
def zmq_socket
|
11
|
+
return @sub_socket unless @sub_socket.nil?
|
12
|
+
|
13
|
+
(@sub_socket = sock_type).tap do
|
14
|
+
|
15
|
+
case @end_point_type
|
16
|
+
when :connect
|
17
|
+
@sub_socket.connect(@end_point)
|
18
|
+
else
|
19
|
+
@sub_socket.bind(@end_point)
|
20
|
+
end
|
21
|
+
|
22
|
+
if @socket_opts.is_a? Array
|
23
|
+
@socket_opts.each do |opts|
|
24
|
+
opts.each_pair do |opt_name, opt_val|
|
25
|
+
@sub_socket.setsockopt(opt_name,opt_val)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'agent_zmq/agents/base_agent'
|
2
|
+
|
3
|
+
class AgentZMQ::PubAgent
|
4
|
+
include AgentZMQ::BaseAgent
|
5
|
+
|
6
|
+
def initialize name
|
7
|
+
@name=name
|
8
|
+
@socket_opts=[]
|
9
|
+
end
|
10
|
+
|
11
|
+
def sock_type
|
12
|
+
zmq_context.socket(ZMQ::PUB)
|
13
|
+
end
|
14
|
+
|
15
|
+
def start
|
16
|
+
zmq_socket
|
17
|
+
sleep 0.8 # slow joiner
|
18
|
+
end
|
19
|
+
|
20
|
+
def stop
|
21
|
+
zmq_socket.close
|
22
|
+
end
|
23
|
+
|
24
|
+
def publish msg
|
25
|
+
AgentZMQ::Helpers.publish zmq_socket, msg
|
26
|
+
end
|
27
|
+
|
28
|
+
def reset
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'agent_zmq/agents/base_agent'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
class AgentZMQ::RepAgent
|
5
|
+
include AgentZMQ::BaseAgent
|
6
|
+
include AgentZMQ::MessageCache
|
7
|
+
|
8
|
+
attr_accessor :reply
|
9
|
+
|
10
|
+
def initialize name
|
11
|
+
@name=name
|
12
|
+
@socket_opts=[]
|
13
|
+
|
14
|
+
@read_thread=nil
|
15
|
+
@mutex = Mutex.new
|
16
|
+
@do_run_read_thread = true
|
17
|
+
end
|
18
|
+
|
19
|
+
def do_read
|
20
|
+
while true
|
21
|
+
@mutex.synchronize do
|
22
|
+
return unless @do_run_read_thread
|
23
|
+
end
|
24
|
+
@zmq_poller.poll(1000)
|
25
|
+
|
26
|
+
@zmq_poller.readables.each do |sock|
|
27
|
+
request=AgentZMQ::Helpers.read_msg sock
|
28
|
+
add_msg request
|
29
|
+
|
30
|
+
unless @reply.nil?
|
31
|
+
AgentZMQ::Helpers.publish(sock, @reply.call(request))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
zmq_socket.close
|
37
|
+
end
|
38
|
+
|
39
|
+
def sock_type
|
40
|
+
zmq_context.socket(ZMQ::REP)
|
41
|
+
end
|
42
|
+
|
43
|
+
def start_read_thread
|
44
|
+
@zmq_poller = ZMQ::Poller.new
|
45
|
+
@zmq_poller.register(zmq_socket, ZMQ::POLLIN)
|
46
|
+
|
47
|
+
@read_thread = Thread.new {do_read}
|
48
|
+
end
|
49
|
+
|
50
|
+
def start
|
51
|
+
start_read_thread
|
52
|
+
end
|
53
|
+
|
54
|
+
def stop
|
55
|
+
@mutex.synchronize do
|
56
|
+
@do_run_read_thread=false
|
57
|
+
end
|
58
|
+
@read_thread.join
|
59
|
+
zmq_socket.close
|
60
|
+
end
|
61
|
+
|
62
|
+
def reset
|
63
|
+
clear
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'agent_zmq/agents/base_agent'
|
2
|
+
|
3
|
+
class AgentZMQ::ReqAgent
|
4
|
+
include AgentZMQ::BaseAgent
|
5
|
+
|
6
|
+
def initialize name
|
7
|
+
@name=name
|
8
|
+
@socket_opts=[]
|
9
|
+
end
|
10
|
+
|
11
|
+
def sock_type
|
12
|
+
zmq_context.socket(ZMQ::REQ)
|
13
|
+
end
|
14
|
+
|
15
|
+
def start
|
16
|
+
zmq_socket
|
17
|
+
sleep 0.8 # slow joiner
|
18
|
+
end
|
19
|
+
|
20
|
+
def stop
|
21
|
+
zmq_socket.close
|
22
|
+
end
|
23
|
+
|
24
|
+
def publish msg
|
25
|
+
AgentZMQ::Helpers.publish(zmq_socket, msg)
|
26
|
+
AgentZMQ::Helpers.read_msg zmq_socket
|
27
|
+
end
|
28
|
+
|
29
|
+
def reset
|
30
|
+
#no-op
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'ffi-rzmq'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
require 'agent_zmq/message_cache'
|
5
|
+
require 'agent_zmq/agents/base_agent'
|
6
|
+
|
7
|
+
class AgentZMQ::SubAgent
|
8
|
+
include AgentZMQ::MessageCache
|
9
|
+
include AgentZMQ::BaseAgent
|
10
|
+
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
def initialize name
|
14
|
+
@name=name
|
15
|
+
@socket_opts=[]
|
16
|
+
|
17
|
+
@mutex=Mutex.new
|
18
|
+
@read_thread=nil
|
19
|
+
@do_run_read_thread = true
|
20
|
+
end
|
21
|
+
|
22
|
+
def do_read
|
23
|
+
while true
|
24
|
+
@mutex.synchronize do
|
25
|
+
return unless @do_run_read_thread
|
26
|
+
end
|
27
|
+
|
28
|
+
@zmq_poller.poll(1000)
|
29
|
+
|
30
|
+
@zmq_poller.readables.each do |sock|
|
31
|
+
add_msg AgentZMQ::Helpers.read_msg sock
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
zmq_socket.close
|
36
|
+
end
|
37
|
+
|
38
|
+
def sock_type
|
39
|
+
zmq_context.socket(ZMQ::SUB)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def start_read_thread
|
44
|
+
@zmq_poller = ZMQ::Poller.new
|
45
|
+
@zmq_poller.register(zmq_socket, ZMQ::POLLIN)
|
46
|
+
|
47
|
+
@read_thread = Thread.new {do_read}
|
48
|
+
end
|
49
|
+
|
50
|
+
def start
|
51
|
+
start_read_thread
|
52
|
+
end
|
53
|
+
|
54
|
+
def stop
|
55
|
+
@mutex.synchronize do
|
56
|
+
@do_run_read_thread = false
|
57
|
+
end
|
58
|
+
@read_thread.join
|
59
|
+
zmq_socket.close
|
60
|
+
end
|
61
|
+
|
62
|
+
def reset
|
63
|
+
clear
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.expand_path("../../agent_zmq", __FILE__)
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
|
5
|
+
module ZMQMessageCache
|
6
|
+
def last_zmq_message
|
7
|
+
@message
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
World(ZMQMessageCache)
|
12
|
+
|
13
|
+
Then /^I should receive a (?:request|response|message) on ZeroMQ with agent "([^"]*)"$/ do |subscriber|
|
14
|
+
throw "Unknown agent #{subscriber}" unless AgentZMQ.agents_hash.has_key?(subscriber.to_sym)
|
15
|
+
throw "#{subscriber} is not an agent type that receives messages" unless AgentZMQ.agents_hash[subscriber.to_sym].respond_to?(:pop)
|
16
|
+
|
17
|
+
@message=AgentZMQ.agents_hash[subscriber.to_sym].pop
|
18
|
+
@message.should_not be_nil
|
19
|
+
end
|
20
|
+
|
21
|
+
Then /^the ZeroMQ (?:request|response|message) should have (\d+) parts$/ do |length|
|
22
|
+
throw "last_zmq_message is nil" if last_zmq_message.nil?
|
23
|
+
|
24
|
+
last_zmq_message.length == length.to_i
|
25
|
+
end
|
26
|
+
|
27
|
+
Then /^part (\d+) of the ZeroMQ (?:request|response|message) should be "([^"]*)"$/ do |index, value|
|
28
|
+
throw "last_zmq_message is nil" if last_zmq_message.nil?
|
29
|
+
|
30
|
+
last_zmq_message.length.should >= index.to_i
|
31
|
+
last_zmq_message[(index.to_i - 1)].should == value
|
32
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module AgentZMQ
|
2
|
+
module Helpers
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def read_msg socket
|
6
|
+
msg_parts = []
|
7
|
+
|
8
|
+
begin
|
9
|
+
part=""
|
10
|
+
socket.recv_string part
|
11
|
+
msg_parts << part
|
12
|
+
end while socket.more_parts?
|
13
|
+
|
14
|
+
msg_parts
|
15
|
+
end
|
16
|
+
|
17
|
+
def publish socket, msg
|
18
|
+
msg = [msg] unless msg.is_a? Array
|
19
|
+
|
20
|
+
while true do
|
21
|
+
msg_part=msg.shift
|
22
|
+
|
23
|
+
if msg.empty?
|
24
|
+
socket.send_string msg_part
|
25
|
+
break
|
26
|
+
else
|
27
|
+
socket.send_string msg_part, ZMQ::SNDMORE
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'thread'
|
2
|
+
module AgentZMQ::MessageCache
|
3
|
+
def messages_received
|
4
|
+
lock.synchronize do
|
5
|
+
return messages.dup
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def pop
|
10
|
+
lock.synchronize do
|
11
|
+
return messages.pop
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_msg msg
|
16
|
+
lock.synchronize do
|
17
|
+
messages << msg
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def clear
|
22
|
+
lock.synchronize do
|
23
|
+
messages.clear
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def messages
|
30
|
+
@messages||=[]
|
31
|
+
end
|
32
|
+
|
33
|
+
def lock
|
34
|
+
@lock||=Mutex.new
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class DummyClass
|
4
|
+
end
|
5
|
+
|
6
|
+
describe AgentZMQ::MessageCache do
|
7
|
+
before(:each) do
|
8
|
+
@cache = DummyClass.new
|
9
|
+
@cache.extend(AgentZMQ::MessageCache)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "pop" do
|
13
|
+
it "returns nil if empty" do
|
14
|
+
@cache.pop.should be_nil
|
15
|
+
end
|
16
|
+
|
17
|
+
it "returns pops messages in lifo" do
|
18
|
+
@cache.add_msg "hello"
|
19
|
+
@cache.add_msg "world"
|
20
|
+
|
21
|
+
@cache.pop.should ==("world")
|
22
|
+
@cache.pop.should ==("hello")
|
23
|
+
@cache.pop.should be_nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "clear" do
|
28
|
+
it "removes all cached messages" do
|
29
|
+
@cache.add_msg "hello"
|
30
|
+
@cache.add_msg "world"
|
31
|
+
|
32
|
+
@cache.clear
|
33
|
+
@cache.pop.should be_nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "messages_received" do
|
38
|
+
it "duplicates the internal message cache" do
|
39
|
+
@cache.add_msg "hello"
|
40
|
+
@cache.add_msg "world"
|
41
|
+
|
42
|
+
msgs_received = @cache.messages_received
|
43
|
+
@cache.clear
|
44
|
+
msgs_received.should ==["hello", "world"]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'agent_zmq'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: agent_zmq
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Busbey
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2013-08-29 00:00:00 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ffi-rzmq
|
16
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ~>
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 0.9.3
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: *id001
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: rspec
|
26
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: "2.14"
|
31
|
+
type: :runtime
|
32
|
+
prerelease: false
|
33
|
+
version_requirements: *id002
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: jeweler
|
36
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: "1.8"
|
41
|
+
type: :development
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: *id003
|
44
|
+
- !ruby/object:Gem::Dependency
|
45
|
+
name: cucumber
|
46
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ~>
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: "1.3"
|
51
|
+
type: :development
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: *id004
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: rake
|
56
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "10.1"
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: *id005
|
64
|
+
description: Acceptance test framework for ZeroMQ applications. Includes some cucumber helpers.
|
65
|
+
email: info@connamara.com
|
66
|
+
executables: []
|
67
|
+
|
68
|
+
extensions: []
|
69
|
+
|
70
|
+
extra_rdoc_files:
|
71
|
+
- LICENSE.txt
|
72
|
+
- README.md
|
73
|
+
files:
|
74
|
+
- .document
|
75
|
+
- .rspec
|
76
|
+
- .travis.yml
|
77
|
+
- CONTRIBUTION_GUIDELINES.md
|
78
|
+
- Gemfile
|
79
|
+
- Gemfile.lock
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- VERSION
|
84
|
+
- agent_zmq.gemspec
|
85
|
+
- config/zmq_agents.rb
|
86
|
+
- features/MessageInspection.feature
|
87
|
+
- features/step_definitions/steps.rb
|
88
|
+
- features/support/env.rb
|
89
|
+
- lib/agent_zmq.rb
|
90
|
+
- lib/agent_zmq/agent.rb
|
91
|
+
- lib/agent_zmq/agents/base_agent.rb
|
92
|
+
- lib/agent_zmq/agents/pub_agent.rb
|
93
|
+
- lib/agent_zmq/agents/rep_agent.rb
|
94
|
+
- lib/agent_zmq/agents/req_agent.rb
|
95
|
+
- lib/agent_zmq/agents/sub_agent.rb
|
96
|
+
- lib/agent_zmq/cucumber.rb
|
97
|
+
- lib/agent_zmq/helpers.rb
|
98
|
+
- lib/agent_zmq/message_cache.rb
|
99
|
+
- spec/agent_zmq/message_cache_spec.rb
|
100
|
+
- spec/spec_helper.rb
|
101
|
+
homepage: https://github.com/connamara/agent_zmq
|
102
|
+
licenses:
|
103
|
+
- GPL
|
104
|
+
metadata: {}
|
105
|
+
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options: []
|
108
|
+
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- &id006
|
114
|
+
- ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: "0"
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- *id006
|
120
|
+
requirements: []
|
121
|
+
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 2.0.7
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: Acceptance test framework for ZeroMQ applications
|
127
|
+
test_files: []
|
128
|
+
|