agent_zmq 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,6 @@
1
+ before_install: sudo apt-get install libzmq3-dev
2
+ language: ruby
3
+ rvm:
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - 2.0.0
@@ -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
@@ -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)
@@ -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/>.
@@ -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.
@@ -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
@@ -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"
@@ -0,0 +1,7 @@
1
+ Given /^publisher "([^"]*)" sends the following:$/ do |publisher, message|
2
+ AgentZMQ.agents_hash[publisher.to_sym].publish message.raw.first
3
+ end
4
+
5
+ Given /^I sleep (\d+) seconds$/ do |seconds|
6
+ sleep(seconds.to_i)
7
+ end
@@ -0,0 +1,8 @@
1
+ $: << File.expand_path("../../../lib", __FILE__)
2
+
3
+ require 'agent_zmq/cucumber'
4
+
5
+ AgentZMQ.start
6
+ at_exit {
7
+ AgentZMQ.stop
8
+ }
@@ -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,4 @@
1
+ require 'agent_zmq/agents/sub_agent.rb'
2
+ require 'agent_zmq/agents/pub_agent.rb'
3
+ require 'agent_zmq/agents/req_agent.rb'
4
+ require 'agent_zmq/agents/rep_agent.rb'
@@ -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
@@ -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
+