agent_zmq 0.5.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.
- 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 [](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
|
+

|
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
|
+
|