asir_zmq 1.1.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.
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/Changelog +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +35 -0
- data/Rakefile +34 -0
- data/asir_zmq.gemspec +27 -0
- data/example/asir_control.sh +36 -0
- data/example/asir_control_client_zmq.rb +15 -0
- data/example/config/asir_config.rb +62 -0
- data/example/ex01.rb +28 -0
- data/example/ex02.rb +28 -0
- data/example/example_helper.rb +78 -0
- data/example/sample_service.rb +162 -0
- data/example/unsafe_service.rb +12 -0
- data/lib/asir/transport/zmq.rb +110 -0
- data/lib/asir_zmq/version.rb +3 -0
- data/lib/asir_zmq.rb +8 -0
- data/spec/example_spec.rb +88 -0
- data/spec/spec_helper.rb +37 -0
- metadata +154 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color -f d
|
data/Changelog
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Kurt Stephens
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# asir_zmq
|
2
|
+
|
3
|
+
ZMQ Transport for ASIR
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'asir_zmq'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install asir_zmq
|
18
|
+
|
19
|
+
## Dependencies
|
20
|
+
|
21
|
+
* gem install zmq
|
22
|
+
** zmq 2.1.4 needs a zmq 2.x library.
|
23
|
+
** On OS X Macports: sudo port install zmq22.
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
See examples/.
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
1. Fork it
|
32
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
33
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
34
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
35
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
gem 'rspec'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
desc "Run specs"
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
8
|
+
t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
9
|
+
# Put spec opts in a file named .rspec in root
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Generate code coverage"
|
13
|
+
RSpec::Core::RakeTask.new(:coverage) do |t|
|
14
|
+
t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
15
|
+
t.rcov = true
|
16
|
+
t.rcov_opts = ['--exclude', 'spec']
|
17
|
+
end
|
18
|
+
|
19
|
+
######################################################################
|
20
|
+
|
21
|
+
desc "Default => :test"
|
22
|
+
task :default => :test
|
23
|
+
|
24
|
+
desc "Run all tests"
|
25
|
+
task :test => [ :spec ]
|
26
|
+
|
27
|
+
desc "Run examples."
|
28
|
+
task :example do
|
29
|
+
ENV["ASIR_EXAMPLE_SILENT"]="1"
|
30
|
+
Dir["example/ex[0-9]*.rb"].each do | rb |
|
31
|
+
sh %Q{ruby -I example -I lib #{rb}}
|
32
|
+
end
|
33
|
+
ENV.delete("ASIR_EXAMPLE_SILENT")
|
34
|
+
end
|
data/asir_zmq.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# -*- ruby -*-
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'asir_zmq/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |gem|
|
8
|
+
gem.name = "asir_zmq"
|
9
|
+
gem.version = AsirZmq::VERSION
|
10
|
+
gem.authors = ["Kurt Stephens"]
|
11
|
+
gem.email = ["ks.ruby@kurtstephens.com"]
|
12
|
+
gem.description = %q{ZMQ transport for ASIR}
|
13
|
+
gem.summary = %q{Adds ZMQ transport to ASIR}
|
14
|
+
gem.homepage = "http://github.com/kstephens/abstracting_services_in_ruby"
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
|
21
|
+
gem.add_dependency "asir", "~> 1.1.0"
|
22
|
+
gem.add_dependency "zmq", "~> 2.1.4"
|
23
|
+
|
24
|
+
gem.add_development_dependency 'rake', '>= 0.9.0'
|
25
|
+
gem.add_development_dependency 'rspec', '~> 2.12.0'
|
26
|
+
gem.add_development_dependency 'simplecov', '>= 0.1'
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
set -x
|
3
|
+
dir="$(cd "$(dirname $0)" && /bin/pwd)"
|
4
|
+
PATH="$dir/../bin:$PATH"
|
5
|
+
export RUBYLIB="$dir/../example:$dir/../lib"
|
6
|
+
asir="asir verbose=9 config_rb=$dir/config/asir_config.rb"
|
7
|
+
args="$*"
|
8
|
+
args="${args:-ALL}"
|
9
|
+
# set -e
|
10
|
+
|
11
|
+
#############################
|
12
|
+
|
13
|
+
case "$args"
|
14
|
+
in
|
15
|
+
*zmq*|*ALL*)
|
16
|
+
|
17
|
+
$asir start zmq worker
|
18
|
+
sleep 1
|
19
|
+
$asir pid zmq worker
|
20
|
+
if $asir alive zmq worker; then
|
21
|
+
echo "alive"
|
22
|
+
fi
|
23
|
+
|
24
|
+
ruby "$dir/asir_control_client_zmq.rb"
|
25
|
+
sleep 1
|
26
|
+
|
27
|
+
$asir stop zmq worker
|
28
|
+
sleep 1
|
29
|
+
$asir pid zmq worker
|
30
|
+
|
31
|
+
;;
|
32
|
+
esac
|
33
|
+
|
34
|
+
#############################
|
35
|
+
|
36
|
+
exit 0
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'example_helper'
|
2
|
+
require 'asir/transport/zmq'
|
3
|
+
require 'asir/coder/marshal'
|
4
|
+
begin
|
5
|
+
Email.asir.transport = t =
|
6
|
+
ASIR::Transport::Zmq.new(:uri => "tcp://localhost:31000") # "/asir"
|
7
|
+
t.one_way = true
|
8
|
+
t.encoder = ASIR::Coder::Marshal.new
|
9
|
+
pr Email.asir.send_email(:pdf_invoice,
|
10
|
+
:to => "user@email.com",
|
11
|
+
:customer => @customer)
|
12
|
+
ensure
|
13
|
+
t.close rescue nil
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Used by asir/bin/asir.
|
2
|
+
# Configures asir worker transport and error logging.
|
3
|
+
# asir object is bound to ASIR::Environment instance.
|
4
|
+
|
5
|
+
$stderr.puts "asir.phase = #{asir.phase.inspect}" if asir.verbose >= 1
|
6
|
+
case asir.phase
|
7
|
+
when :configure
|
8
|
+
# NOTHING
|
9
|
+
true
|
10
|
+
when :environment
|
11
|
+
require 'rubygems'
|
12
|
+
|
13
|
+
gem 'asir', '~> 1.1.0'
|
14
|
+
|
15
|
+
require 'asir'
|
16
|
+
require 'asir/transport/file'
|
17
|
+
require 'asir/coder/marshal'
|
18
|
+
require 'asir/coder/yaml'
|
19
|
+
|
20
|
+
$:.unshift File.expand_path('..')
|
21
|
+
require 'example_helper'
|
22
|
+
require 'sample_service'
|
23
|
+
require 'unsafe_service'
|
24
|
+
when :start
|
25
|
+
# NOTHING
|
26
|
+
true
|
27
|
+
when :transport
|
28
|
+
# Compose with Marshal for final coding.
|
29
|
+
coder = ASIR::Coder::Marshal.new
|
30
|
+
|
31
|
+
# Logger for worker-side Exceptions.
|
32
|
+
error_log_file = asir.log_file.sub(/\.log$/, '-error.log')
|
33
|
+
error_transport =
|
34
|
+
ASIR::Transport::File.new(:file => error_log_file,
|
35
|
+
:mode => 'a+',
|
36
|
+
:perms => 0666)
|
37
|
+
error_transport.encoder = ASIR::Coder::Yaml.new
|
38
|
+
|
39
|
+
# Setup requested Transport.
|
40
|
+
case asir.adjective
|
41
|
+
when :zmq
|
42
|
+
require 'asir/transport/zmq'
|
43
|
+
transport = ASIR::Transport::Zmq.new
|
44
|
+
transport.one_way = true
|
45
|
+
transport.uri = "tcp://localhost:#{31000 + asir.identifier.to_s.to_i}" # /asir"
|
46
|
+
else
|
47
|
+
raise "Cannot configure Transport for #{asir.adjective}"
|
48
|
+
end
|
49
|
+
|
50
|
+
transport.encoder = coder
|
51
|
+
transport._logger = STDERR
|
52
|
+
transport._log_enabled = true
|
53
|
+
# transport.verbose = 3
|
54
|
+
transport.on_exception =
|
55
|
+
lambda { | transport, exc, phase, message, result |
|
56
|
+
error_transport.send_request(message)
|
57
|
+
}
|
58
|
+
|
59
|
+
transport
|
60
|
+
else
|
61
|
+
$stderr.puts "Warning: unhandled asir.phase: #{asir.phase.inspect}"
|
62
|
+
end
|
data/example/ex01.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# !SLIDE :capture_code_output true
|
2
|
+
# One-way ZMQ service.
|
3
|
+
|
4
|
+
require 'example_helper'
|
5
|
+
require 'asir/transport/zmq'
|
6
|
+
begin
|
7
|
+
zmq = ASIR::Transport::Zmq.new(:port => 31920,
|
8
|
+
:encoder => ASIR::Coder::Marshal.new,
|
9
|
+
:one_way => true)
|
10
|
+
server_process do
|
11
|
+
zmq.prepare_server!
|
12
|
+
zmq.run_server!
|
13
|
+
end
|
14
|
+
UnsafeService.asir.transport = t = zmq
|
15
|
+
pr UnsafeService.asir.do_it(":ok")
|
16
|
+
rescue ::Exception => err
|
17
|
+
$stderr.puts "### #{$$}: ERROR: #{err.inspect}\n #{err.backtrace * "\n "}"
|
18
|
+
raise
|
19
|
+
ensure
|
20
|
+
zmq.close rescue nil; sleep 1; server_kill
|
21
|
+
end
|
22
|
+
|
23
|
+
# !SLIDE END
|
24
|
+
# EXPECT: : client process
|
25
|
+
# EXPECT: : server process
|
26
|
+
# EXPECT: UnsafeService.do_it => :ok
|
27
|
+
# EXPECT: : pr: nil
|
28
|
+
# EXPECT!: ERROR
|
data/example/ex02.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# !SLIDE :capture_code_output true
|
2
|
+
# Bi-directional ZMQ service.
|
3
|
+
|
4
|
+
require 'example_helper'
|
5
|
+
require 'asir/transport/zmq'
|
6
|
+
begin
|
7
|
+
zmq = ASIR::Transport::Zmq.new(:port => 31920,
|
8
|
+
:encoder => ASIR::Coder::Marshal.new,
|
9
|
+
:one_way => false)
|
10
|
+
server_process do
|
11
|
+
zmq.prepare_server!
|
12
|
+
zmq.run_server!
|
13
|
+
end
|
14
|
+
UnsafeService.asir.transport = t = zmq
|
15
|
+
pr UnsafeService.asir.do_it(":ok")
|
16
|
+
rescue ::Exception => err
|
17
|
+
$stderr.puts "### #{$$}: ERROR: #{err.inspect}\n #{err.backtrace * "\n "}"
|
18
|
+
raise
|
19
|
+
ensure
|
20
|
+
zmq.close rescue nil; sleep 1; server_kill
|
21
|
+
end
|
22
|
+
|
23
|
+
# !SLIDE END
|
24
|
+
# EXPECT: : client process
|
25
|
+
# EXPECT: : server process
|
26
|
+
# EXPECT: UnsafeService.do_it => :ok
|
27
|
+
# EXPECT: : pr: :ok
|
28
|
+
# EXPECT!: ERROR
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Sample client support
|
2
|
+
#
|
3
|
+
require 'rubygems'
|
4
|
+
case RUBY_PLATFORM
|
5
|
+
when /java/i
|
6
|
+
gem 'spoon'; require 'spoon'
|
7
|
+
end
|
8
|
+
|
9
|
+
$: << File.expand_path("../../lib", __FILE__)
|
10
|
+
gem 'asir'
|
11
|
+
|
12
|
+
require 'asir'
|
13
|
+
require 'asir/coder/marshal'
|
14
|
+
ASIR::Log.enabled = true unless ENV['ASIR_EXAMPLE_SILENT']
|
15
|
+
require 'sample_service'
|
16
|
+
# require 'delayed_service'
|
17
|
+
require 'unsafe_service'
|
18
|
+
|
19
|
+
require 'pp'
|
20
|
+
|
21
|
+
@customer = 123
|
22
|
+
|
23
|
+
class ::Object
|
24
|
+
|
25
|
+
def pr result
|
26
|
+
$stdout.puts "*** #{$$}: pr: #{PP.pp(result, '')}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def server_process &blk
|
30
|
+
# $stderr.puts " at #{__FILE__}:#{__LINE__}"
|
31
|
+
case RUBY_PLATFORM
|
32
|
+
when /java/i
|
33
|
+
# JRuby cannot fork.
|
34
|
+
# So we must prevent spawn a new jruby and
|
35
|
+
# instruct it to only run the server blk, and not
|
36
|
+
# the subsequent client code.
|
37
|
+
# In other words, we cannot rely on how Process.fork
|
38
|
+
# terminates within the block.
|
39
|
+
if ENV['ASIR_JRUBY_SPAWNED']
|
40
|
+
$stderr.puts " spawned server at #{__FILE__}:#{__LINE__}"
|
41
|
+
puts "*** #{$$}: server process"; $stdout.flush
|
42
|
+
yield
|
43
|
+
Process.exit!(0)
|
44
|
+
# dont do client, client is our parent process.
|
45
|
+
else
|
46
|
+
$stderr.puts " spawning at #{__FILE__}:#{__LINE__}"
|
47
|
+
ENV['ASIR_JRUBY_SPAWNED'] = "1"
|
48
|
+
cmd = "ruby -I #{File.dirname(__FILE__)} -I #{File.expand_path('../../lib', __FILE__)} #{$0} #{ARGV * ' '}"
|
49
|
+
$stderr.puts " cmd = #{cmd}"
|
50
|
+
$server_pid = Spoon.spawnp(cmd)
|
51
|
+
ENV.delete('ASIR_JRUBY_SPAWNED')
|
52
|
+
$stderr.puts " spawned #{$server_pid} at #{__FILE__}:#{__LINE__}"
|
53
|
+
end
|
54
|
+
else
|
55
|
+
# $stderr.puts " at #{__FILE__}:#{__LINE__}"
|
56
|
+
$server_pid = Process.fork do
|
57
|
+
puts "*** #{$$}: server process"; $stdout.flush
|
58
|
+
yield
|
59
|
+
end
|
60
|
+
end
|
61
|
+
sleep 1 # wait for server to be ready.
|
62
|
+
return false # do client.
|
63
|
+
end
|
64
|
+
|
65
|
+
def server_kill
|
66
|
+
if $server_pid
|
67
|
+
Process.kill 9, $server_pid
|
68
|
+
Process.waitpid($server_pid)
|
69
|
+
end
|
70
|
+
rescue Errno::ESRCH
|
71
|
+
ensure
|
72
|
+
$server_pid = nil
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
puts "*** #{$$}: client process"; $stdout.flush
|
78
|
+
|
@@ -0,0 +1,162 @@
|
|
1
|
+
=begin
|
2
|
+
# !SLIDE
|
3
|
+
# Stuff Gets Complicated
|
4
|
+
#
|
5
|
+
# Systems become:
|
6
|
+
# * bigger ->
|
7
|
+
# * complex ->
|
8
|
+
# * slower ->
|
9
|
+
# * distributed ->
|
10
|
+
# * hard to test
|
11
|
+
#
|
12
|
+
# !SLIDE END
|
13
|
+
|
14
|
+
# !SLIDE
|
15
|
+
# Sample Service
|
16
|
+
#
|
17
|
+
module Email
|
18
|
+
def send_email template_name, options
|
19
|
+
$stderr.puts "*** #{$$}: Email.send_mail #{template_name.inspect} #{options.inspect}"
|
20
|
+
:ok
|
21
|
+
end
|
22
|
+
def do_raise msg
|
23
|
+
raise msg
|
24
|
+
end
|
25
|
+
extend self
|
26
|
+
end
|
27
|
+
# !SLIDE END
|
28
|
+
|
29
|
+
# !SLIDE
|
30
|
+
# Back when things were simple...
|
31
|
+
#
|
32
|
+
class CustomersController < ApplicationController
|
33
|
+
def send_invoice
|
34
|
+
@customer = Customer.find(params[:id])
|
35
|
+
Email.send_email(:pdf_invoice,
|
36
|
+
:to => @customer.email,
|
37
|
+
:customer => @customer)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
# !SLIDE END
|
41
|
+
|
42
|
+
# !SLIDE
|
43
|
+
# Trying to improve user's experience...
|
44
|
+
#
|
45
|
+
class CustomersController < ApplicationController
|
46
|
+
def send_invoice
|
47
|
+
@customer = Customer.find(params[:id])
|
48
|
+
Process.fork do
|
49
|
+
Email.send_email(:pdf_invoice,
|
50
|
+
:to = @customer.email,
|
51
|
+
:customer => @customer)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
# !SLIDE END
|
56
|
+
|
57
|
+
# !SLIDE
|
58
|
+
# Use other machines to poll a work table...
|
59
|
+
#
|
60
|
+
class CustomersController < ApplicationController
|
61
|
+
def send_invoice
|
62
|
+
@customer = Customer.find(params[:id])
|
63
|
+
EmailWork.create(:template_name => :pdf_invoice,
|
64
|
+
:options => {
|
65
|
+
:to => @customer.email,
|
66
|
+
:customer => @customer,
|
67
|
+
})
|
68
|
+
end
|
69
|
+
end
|
70
|
+
# !SLIDE END
|
71
|
+
|
72
|
+
# !SLIDE
|
73
|
+
# Use queue infrastructure
|
74
|
+
#
|
75
|
+
class CustomersController < ApplicationController
|
76
|
+
def send_invoice
|
77
|
+
@customer = Customer.find(params[:id])
|
78
|
+
queue_service.put(:queue => :Email,
|
79
|
+
:action => :send_email,
|
80
|
+
:template_name => :pdf_invoice,
|
81
|
+
:options => {
|
82
|
+
:to => @customer.email,
|
83
|
+
:customer => @customer,
|
84
|
+
})
|
85
|
+
end
|
86
|
+
end
|
87
|
+
# !SLIDE END
|
88
|
+
|
89
|
+
# !SLIDE
|
90
|
+
# Example Message
|
91
|
+
#
|
92
|
+
Email.asir.send_email(:pdf_invoice,
|
93
|
+
:to => "user@email.com",
|
94
|
+
:customer => @customer)
|
95
|
+
# ->
|
96
|
+
message = Message.new(...)
|
97
|
+
message.receiver_class == ::Module
|
98
|
+
message.receiver == ::Email
|
99
|
+
message.selector == :send_email
|
100
|
+
message.arguments == [ :pdf_invoice,
|
101
|
+
{ :to => "user@email.com",
|
102
|
+
:customer => ... } ]
|
103
|
+
# !SLIDE END
|
104
|
+
|
105
|
+
# !SLIDE
|
106
|
+
# Using a Client Proxy
|
107
|
+
#
|
108
|
+
Email.send_email(:pdf_invoice,
|
109
|
+
:to => "user@email.com",
|
110
|
+
:customer => @customer)
|
111
|
+
# ->
|
112
|
+
Email.asir.
|
113
|
+
send_email(:pdf_invoice,
|
114
|
+
:to => "user@email.com",
|
115
|
+
:customer => @customer)
|
116
|
+
# !SLIDE END
|
117
|
+
|
118
|
+
# !SLIDE
|
119
|
+
# Example Exception
|
120
|
+
#
|
121
|
+
Email.do_raise("DOH!")
|
122
|
+
#
|
123
|
+
# ->
|
124
|
+
result.exception = ee = EncapsulatedException.new(...)
|
125
|
+
ee.exception_class = "::RuntimeError"
|
126
|
+
ee.exception_message = "DOH!"
|
127
|
+
ee.exception_backtrace = [ ... ]
|
128
|
+
# !SLIDE END
|
129
|
+
=end
|
130
|
+
|
131
|
+
# !SLIDE
|
132
|
+
# Sample Service with Client Support
|
133
|
+
#
|
134
|
+
|
135
|
+
require 'asir'
|
136
|
+
# Added .asir support.
|
137
|
+
module Email
|
138
|
+
include ASIR::Client # Email.asir
|
139
|
+
def send_email template_name, options
|
140
|
+
$stderr.puts "*** #{$$}: Email.send_mail #{template_name.inspect} #{options.inspect}"
|
141
|
+
:ok
|
142
|
+
end
|
143
|
+
def do_raise msg
|
144
|
+
raise msg
|
145
|
+
end
|
146
|
+
extend self
|
147
|
+
end
|
148
|
+
# !SLIDE END
|
149
|
+
|
150
|
+
# !SLIDE
|
151
|
+
# Sample Object Instance Client
|
152
|
+
#
|
153
|
+
class MyClass
|
154
|
+
include ASIR::Client
|
155
|
+
def initialize x
|
156
|
+
@x = x
|
157
|
+
end
|
158
|
+
def method_missing sel, *args
|
159
|
+
@x.send(sel, *args)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
# !SLIDE END
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'asir'
|
2
|
+
|
3
|
+
module UnsafeService
|
4
|
+
include ASIR::Client
|
5
|
+
def self.do_it(expr)
|
6
|
+
result = eval(expr)
|
7
|
+
puts "#{$$}: UnsafeService.do_it(#{expr}) #{result.inspect}"
|
8
|
+
$stderr.puts "#{$$}: UnsafeService.do_it => #{result.inspect}"
|
9
|
+
result
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'asir/transport/connection_oriented'
|
2
|
+
gem 'zmq'
|
3
|
+
require 'zmq'
|
4
|
+
|
5
|
+
module ASIR
|
6
|
+
class Transport
|
7
|
+
# !SLIDE
|
8
|
+
# ZeroMQ Transport
|
9
|
+
class Zmq < ConnectionOriented
|
10
|
+
attr_accessor :queue
|
11
|
+
|
12
|
+
# !SLIDE
|
13
|
+
# 0MQ client.
|
14
|
+
def _client_connect!
|
15
|
+
sock = zmq_context.socket(one_way ? ZMQ::PUB : ZMQ::REQ)
|
16
|
+
sock.connect(zmq_uri)
|
17
|
+
sock
|
18
|
+
rescue ::Exception => exc
|
19
|
+
raise exc.class, "#{self.class} #{zmq_uri}: #{exc.message}", exc.backtrace
|
20
|
+
end
|
21
|
+
|
22
|
+
# !SLIDE
|
23
|
+
# 0MQ server.
|
24
|
+
def _server!
|
25
|
+
sock = zmq_context.socket(one_way ? ZMQ::SUB : ZMQ::REP)
|
26
|
+
sock.setsockopt(ZMQ::SUBSCRIBE, queue) if one_way
|
27
|
+
sock.bind("tcp://*:#{port}") # WTF?: why doesn't tcp://localhost:PORT work?
|
28
|
+
@server = sock
|
29
|
+
rescue ::Exception => exc
|
30
|
+
raise exc.class, "#{self.class} #{zmq_uri}: #{exc.message}", exc.backtrace
|
31
|
+
end
|
32
|
+
|
33
|
+
def _receive_result message, opaque_result
|
34
|
+
return nil if one_way || message.one_way
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def _send_result message, result, result_payload, stream, message_state
|
39
|
+
return nil if one_way || message.one_way
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
def _write payload, stream, context
|
44
|
+
if one_way
|
45
|
+
q = context && (context[:queue] || context[:zmq_queue])
|
46
|
+
payload.insert(0, q || queue_)
|
47
|
+
end
|
48
|
+
stream.send payload, 0
|
49
|
+
stream
|
50
|
+
end
|
51
|
+
|
52
|
+
def _read stream, context
|
53
|
+
stream.recv 0
|
54
|
+
end
|
55
|
+
|
56
|
+
def queue
|
57
|
+
@queue ||=
|
58
|
+
(
|
59
|
+
case
|
60
|
+
when @uri
|
61
|
+
x = URI.parse(@uri).path
|
62
|
+
else
|
63
|
+
x = ""
|
64
|
+
end
|
65
|
+
# x << "\t" unless x.empty?
|
66
|
+
x.freeze
|
67
|
+
)
|
68
|
+
end
|
69
|
+
def queue_
|
70
|
+
@queue_ ||=
|
71
|
+
(queue.empty? ? queue : queue + " ").freeze
|
72
|
+
end
|
73
|
+
|
74
|
+
# server represents a receiving ZMQ endpoint.
|
75
|
+
def _server_accept_connection! server
|
76
|
+
[ server, @one_way ? nil : server ]
|
77
|
+
end
|
78
|
+
|
79
|
+
# ZMQ is message-oriented, process only one message per "connection".
|
80
|
+
alias :_server_serve_stream :serve_message!
|
81
|
+
|
82
|
+
def stream_eof? stream
|
83
|
+
false
|
84
|
+
end
|
85
|
+
|
86
|
+
# Nothing to be closed for ZMQ.
|
87
|
+
def _server_close_connection! in_stream, out_stream
|
88
|
+
# NOTHING
|
89
|
+
end
|
90
|
+
|
91
|
+
def zmq_uri
|
92
|
+
@zmq_uri ||=
|
93
|
+
(
|
94
|
+
u = URI.parse(uri)
|
95
|
+
u.path = ''
|
96
|
+
u.to_s.freeze
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
def zmq_context
|
101
|
+
@@zmq_context ||=
|
102
|
+
ZMQ::Context.new(1)
|
103
|
+
end
|
104
|
+
@@zmq_context ||= nil
|
105
|
+
end
|
106
|
+
# !SLIDE END
|
107
|
+
end # class
|
108
|
+
end # module
|
109
|
+
|
110
|
+
|
data/lib/asir_zmq.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
$:.unshift File.expand_path('../../example', __FILE__)
|
4
|
+
|
5
|
+
describe "ASIR Example" do
|
6
|
+
attr_accessor :file, :expects
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@expects = [ ]
|
10
|
+
end
|
11
|
+
|
12
|
+
after(:each) do
|
13
|
+
@file.should_not == nil
|
14
|
+
File.open(@file) do | fh |
|
15
|
+
until fh.eof?
|
16
|
+
line = fh.readline
|
17
|
+
line.chomp!
|
18
|
+
case
|
19
|
+
when line.sub!(/^\s*#\s*EXPECT\/:\s*/, '')
|
20
|
+
expect Regexp.new(line)
|
21
|
+
when line.sub!(/^\s*#\s*EXPECT!\/:\s*/, '')
|
22
|
+
expect Regexp.new(line), :'!~'
|
23
|
+
when line.sub!(/^\s*#\s*EXPECT:\s*/, '')
|
24
|
+
expect Regexp.new(Regexp.escape(line))
|
25
|
+
when line.sub!(/^\s*#\s*EXPECT!:\s*/, '')
|
26
|
+
expect Regexp.new(Regexp.escape(line)), :'!~'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@output, @exit_code = run_file!(@file)
|
31
|
+
@exit_code.should == 0
|
32
|
+
@expects.empty?.should_not == true
|
33
|
+
@expects.each do | rx, mode |
|
34
|
+
$stderr.puts " Checking #{mode} #{rx.inspect}" if ENV['SPEC_VERBOSE']
|
35
|
+
case mode
|
36
|
+
when :'=~'
|
37
|
+
@output.should =~ rx
|
38
|
+
when :'!~'
|
39
|
+
@output.should_not =~ rx
|
40
|
+
else
|
41
|
+
raise ArgumentError
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def run_file! file, output = StringIO.new('')
|
47
|
+
progname_save, stdout_save, stderr_save = $0, $stdout, $stderr
|
48
|
+
exc = system_exit = nil; exit_code = 0
|
49
|
+
begin
|
50
|
+
if true
|
51
|
+
cmd = "ASIR_EXAMPLE_SILENT=1 ruby -I example -I lib #{file}"
|
52
|
+
$stderr.puts "\n Running #{cmd}:" if ENV['SPEC_VERBOSE']
|
53
|
+
output = `#{cmd} 2>&1 | tee #{file}.out`
|
54
|
+
else
|
55
|
+
$stderr.puts "\n Loading #{file}:" if ENV['SPEC_VERBOSE']
|
56
|
+
$stdout.puts "*** #{$$}: client process"; $stdout.flush
|
57
|
+
$stdout = $stderr = output
|
58
|
+
$0 = file
|
59
|
+
Kernel.load(file, true)
|
60
|
+
output = output.string if StringIO === output
|
61
|
+
end
|
62
|
+
rescue ::SystemExit => system_exit
|
63
|
+
exit_code = 1 # ???
|
64
|
+
rescue ::Exception => exc
|
65
|
+
exit_code = -1
|
66
|
+
end
|
67
|
+
[ output, exit_code ]
|
68
|
+
ensure
|
69
|
+
$0, $stdout, $stderr = progname_save, stdout_save, stderr_save
|
70
|
+
$stderr.write output if ENV['SPEC_VERBOSE']
|
71
|
+
if exc
|
72
|
+
stderr_save.puts "ERROR: #{file}: #{exc.inspect}\n#{exc.backtrace * "\n"}"
|
73
|
+
raise exc
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def expect rx, mode = :'=~'
|
78
|
+
@expects << [ rx, mode ]
|
79
|
+
end
|
80
|
+
|
81
|
+
Dir['example/**/ex[0-9]*.rb'].sort.each do | file |
|
82
|
+
title = File.open(file) { | fh | fh.read(4096) }
|
83
|
+
title = title =~ /#\s+!SLIDE[^\n]*\n\s*#\s*([^\n]+)/ && $1
|
84
|
+
it "#{file} - #{title}" do
|
85
|
+
@file = file
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
3
|
+
|
4
|
+
gem 'asir'
|
5
|
+
require 'asir'
|
6
|
+
|
7
|
+
module ASIR
|
8
|
+
module Test
|
9
|
+
class TestError < ::Exception; end
|
10
|
+
class TestObject
|
11
|
+
include ASIR::Client
|
12
|
+
attr_accessor :spec, :arg, :cls, :msg, :transport, :message
|
13
|
+
def initialize spec
|
14
|
+
@spec = spec
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_data!
|
18
|
+
@transport = ASIR::Transport.current
|
19
|
+
@message = @transport && @transport.message
|
20
|
+
end
|
21
|
+
|
22
|
+
def return_argument arg
|
23
|
+
get_data!
|
24
|
+
@arg = arg
|
25
|
+
arg
|
26
|
+
end
|
27
|
+
|
28
|
+
def raise_exception! cls, msg
|
29
|
+
get_data!
|
30
|
+
@cls = cls
|
31
|
+
@msg = msg
|
32
|
+
raise cls, msg
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
metadata
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: asir_zmq
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Kurt Stephens
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: asir
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.1.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.1.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: zmq
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 2.1.4
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 2.1.4
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.9.0
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.9.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.12.0
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.12.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: simplecov
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0.1'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0.1'
|
94
|
+
description: ZMQ transport for ASIR
|
95
|
+
email:
|
96
|
+
- ks.ruby@kurtstephens.com
|
97
|
+
executables: []
|
98
|
+
extensions: []
|
99
|
+
extra_rdoc_files: []
|
100
|
+
files:
|
101
|
+
- .gitignore
|
102
|
+
- .rspec
|
103
|
+
- Changelog
|
104
|
+
- Gemfile
|
105
|
+
- LICENSE.txt
|
106
|
+
- README.md
|
107
|
+
- Rakefile
|
108
|
+
- asir_zmq.gemspec
|
109
|
+
- example/asir_control.sh
|
110
|
+
- example/asir_control_client_zmq.rb
|
111
|
+
- example/config/asir_config.rb
|
112
|
+
- example/ex01.rb
|
113
|
+
- example/ex02.rb
|
114
|
+
- example/example_helper.rb
|
115
|
+
- example/sample_service.rb
|
116
|
+
- example/unsafe_service.rb
|
117
|
+
- lib/asir/transport/zmq.rb
|
118
|
+
- lib/asir_zmq.rb
|
119
|
+
- lib/asir_zmq/version.rb
|
120
|
+
- spec/example_spec.rb
|
121
|
+
- spec/spec_helper.rb
|
122
|
+
homepage: http://github.com/kstephens/abstracting_services_in_ruby
|
123
|
+
licenses: []
|
124
|
+
post_install_message:
|
125
|
+
rdoc_options: []
|
126
|
+
require_paths:
|
127
|
+
- lib
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
segments:
|
135
|
+
- 0
|
136
|
+
hash: -1294367762507940385
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
segments:
|
144
|
+
- 0
|
145
|
+
hash: -1294367762507940385
|
146
|
+
requirements: []
|
147
|
+
rubyforge_project:
|
148
|
+
rubygems_version: 1.8.24
|
149
|
+
signing_key:
|
150
|
+
specification_version: 3
|
151
|
+
summary: Adds ZMQ transport to ASIR
|
152
|
+
test_files:
|
153
|
+
- spec/example_spec.rb
|
154
|
+
- spec/spec_helper.rb
|