Stuff Gets Complicated

Systems become:

  • bigger →
  • complex →
  • slower →
  • distributed →
  • hard to test

Back when things were simple…

class CustomersController < ApplicationController
  def send_invoice
    @customer = Customer.find(params[:id])
    Email.send_email(:pdf_invoice,
                     :to => @customer.email,
                     :customer => @customer)
  end
end

Trying to improve user’s experience…

class CustomersController < ApplicationController
  def send_invoice
    @customer = Customer.find(params[:id])
    Process.fork do 
      Email.send_email(:pdf_invoice,
                       :to = @customer.email,
                       :customer => @customer)
    end
  end
end

Use other machines to poll a work table…

class CustomersController < ApplicationController
  def send_invoice
    @customer = Customer.find(params[:id])
    EmailWork.create(:template_name => :pdf_invoice, 
                     :options => { 
                       :to => @customer.email,
                       :customer => @customer,
                     })
  end
end

Use queue infrastructure

class CustomersController < ApplicationController
  def send_invoice
    @customer = Customer.find(params[:id])
    queue_service.put(:queue => :Email, 
                      :action => :send_email,
                      :template_name => :pdf_invoice, 
                      :options => { 
                        :to => @customer.email,
                        :customer => @customer,
                      })
  end
end

Issues

  • Problem Domain, Solution Domain
  • Service Middleware Semantics
  • Testing, Debugging, Diagnostics
  • Productivity

Problem Domain, Solution Domain

  • Client knows too much about infrastructure.
  • Evaluating and switching infrastructures.

Service Middleware Semantics

  • Directionality: One-way, Two-way
  • Synchronicity: Synchronous, Asynchronous
  • Distribution: Local Process, Local Thread, Distributed
  • Robustness: Retry, Replay, Fallback

Testing, Debugging, Diagnostics

  • Configuration for testing and QA is more complex.
  • Measuring test coverage of remote services.
  • Debugging the root cause of remote service errors.
  • Diagnostic hooks.

Objectives

  • Simplify service/client definitions and interfaces.
  • Anticipate new encoding, delivery and security requirements.
  • Compose encoding and transport concerns.
  • Elide deployment decisions.
  • Integrate diagnostics and logging.
  • Simplify testing.

Design

  • Nouns → Objects → Classes
  • Verbs → Responsibilities → Methods

Book: “Designing Object-Oriented Software”

  • Wirfs-Brock, Wilkerson, Wiener

Design: Nouns

  • Service → Module
  • Client → Just a Ruby caller
  • Proxy
  • Request
  • Response, Exception (two-way)
  • Transport → (file, pipe, http, queue, ActiveResource)
  • Encoder, Decoder → Coder (Marshal, XML, JSON, ActiveResource)
  • Logging

Design: Verbs

  • Intercept Request → Proxy
  • Invoke Request → Request
  • Invoke Exception
  • Send Request, Recieve Request → Transport
  • Encode Object, Decode Object → Coder

Simple

Client-Side Request

Server-Side

Client-Side Response

Modules and Classes

module ASIR
  # Reusable constants to avoid unnecessary garbage.
  EMPTY_ARRAY = [ ].freeze; EMPTY_HASH =  { }.freeze; EMPTY_STRING = ''.freeze

  # Generic API error.
  class Error < ::Exception; end

  # Object Initialization
  module Initialization; ...; end 
  # Diagnostic Logging
  module Log; ...; end 
  # Request
  class Request; ...; end 
  # Response
  class Response; ...; end 
  # Encapsulated Exception
  class EncapsulatedException; ...; end 
  # Coder 
  class Coder; ...; end 

  # Transport
  class Transport; ...; end 

  # Mixin Client support to any Module
  module Client; ...; end 
end

Sample Service

module Email
  def send_email template_name, options
    $stderr.puts "*** #{$$}: Email.send_mail #{template_name.inspect} #{options.inspect}"
    :ok
  end

  def do_raise msg
    raise msg
  end

  extend self
end

Using a Client Proxy

Email.send_email(:pdf_invoice, 
                 :to => "user@email.com",
                 :customer => @customer)


   
Email.client.send_email(:pdf_invoice, 
                        :to => "user@email.com",
                        :customer => @customer)

Mixin Client support to any Module

Extend Module with #client proxy support.

module ASIR
  module Client
    def self.included target
      super
      target.extend ModuleMethods if Module === target
    end
    
    module ModuleMethods
      def client
        @client ||=
          ASIR::Client::Proxy.new(:receiver => self)
      end
    end

    # Client Proxy
    class Proxy; ...; end 
  end
end

Client Proxy

Provide client interface proxy to a service.

module ASIR
  module Client
    class Proxy
      include Log, Initialization
      
      attr_accessor :receiver, :transport
      
      def transport
        @transport ||=
          Transport::Local.new
      end
      
      # Accept all other messages to be encoded and transported to a service.
      def method_missing selector, *arguments
        raise ArgumentError, "block given" if block_given?
        _log { "method_missing #{selector.inspect} #{arguments.inspect}" }
        request = Request.new(receiver, selector, arguments)
        result = transport.send_request(request)
        result
      end
    end
  end
end

Request

Encapsulate the request message from the Client to be handled by the Service.

module ASIR
  class Request
    attr_accessor :receiver, :receiver_class, :selector, :arguments, :result
    attr_accessor :identifier, :client, :timestamp # optional

    def initialize r, s, a
      @receiver, @selector, @arguments = r, s, a
      @receiver_class = @receiver.class
    end

    def invoke!
      Response.new(self, @result = @receiver.__send__(@selector, *@arguments))
    rescue Exception => exc
      Response.new(self, nil, EncapsulatedException.new(exc))
    end

    # Request Identifier
    def create_identifier!; ...; end 
    # Help encode/decode receiver
    def encode_receiver!; ...; end 
  end
end

Example Request

Email.client.send_email(:pdf_invoice, 
                        :to => "user@email.com",
                        :customer => @customer)


  
request = Request.new(...)
request.receiver_class == ::Module
request.receiver == ::Email
request.selector == :send_email
request.arguments == [ :pdf_invoice,
                       { :to => "user@email.com", :customer => ... } ]

Help encode/decode receiver

module ASIR
  class Request
    def encode_receiver!
      unless String === @receiver_class
        case @receiver
        when Module
          obj = self.dup
          obj.receiver = @receiver.name
          obj.receiver_class = @receiver_class.name
          return obj
        end
      end
      self
    end

    def decode_receiver!
      if String === @receiver_class
        @receiver_class = eval("::#{@receiver_class}")
        @receiver = eval("::#{@receiver}")
        unless @receiver_class === @receiver
          raise Error, "receiver #{@receiver.class.name} is not a #{@receiver_class}" 
        end
      end
      self
    end
  end
end

Response

Encapsulate the response returned to the Client.

module ASIR
  class Response
    attr_accessor :request, :result, :exception
    attr_accessor :identifier, :server, :timestamp # optional

    def initialize req, res = nil, exc = nil
      @request, @result, @exception = req, res, exc
    end
  end
end

Coder

Define encoding and decoding for Requests and Responses along a Transport.

module ASIR
  class Coder
    include Log, Initialization

    def encode obj
      _log_result [ :encode, obj ] do
        _encode obj
      end
    end

    def decode obj
      _log_result [ :decode, obj ] do
        obj and _decode obj
      end
    end

    def _subclass_responsibility *args
      raise "subclass responsibility"
    end
    alias :_encode :_subclass_responsibility
    alias :_decode :_subclass_responsibility

    # Coder subclasses.
    # ...
  end
end

Identity Coder

Perform no encode/decode.

module ASIR
  class Coder
    class Identity < self
      def _encode obj
        obj
      end

      def _decode obj
        obj
      end
    end
  end
end

Transport

Client: Send the Request to the Service.
Service: Receive the Request from the Client.
Service: Invoke the Request.
Service: Send the Response to the Client.
Client: Receive the Response from the Service.

module ASIR
  class Transport
    include Log, Initialization

    attr_accessor :encoder, :decoder

    # Transport#send_request 
    def send_request request; ...; end 
    # Transport#receive_request
    def receive_request stream; ...; end 

    # Transport#send_response
    def send_response response, stream; ...; end 

    # Transport#receive_response
    def receive_response opaque; ...; end 

    def _subclass_responsibility *args
      raise "subclass responsibility"
    end
    alias :_send_request :_subclass_responsibility
    alias :_receive_request :_subclass_responsibility
    alias :_send_response :_subclass_responsibility
    alias :_receive_response :_subclass_responsibility

    # Transport subclasses.
    # ...
  end
end

Transport#send_request

  • Encode Request.
  • Send encoded Request.
  • Decode Response.
  • Extract result or exception.
module ASIR
  class Transport
    def send_request request
      request.create_identifier! if needs_request_identifier?
      _log_result [ :send_request, :request, request ] do
        request_payload = encoder.encode(request)
        opaque = _send_request(request_payload)
        response = receive_response opaque
        _log { [ :send_request, :response, response ] }
        if response
          if exc = response.exception
            exc.invoke!
          else
            response.result
          end
        else
          response
        end
      end
    end
  end
end

Null Transport

Never send Request.

module ASIR
  class Transport
    class Null < self
      def _send_request request
        nil
      end
    end
  end
end

Transport Support

module ASIR
  class Transport
    def needs_request_identifier?
      false
    end

    def encoder
      @encoder ||=
        Coder::Identity.new
    end

    def decoder
      @decoder ||= 
        encoder
    end

    def invoke_request! request
      _log_result [ :invoke_request!, request ] do
        @response = request.invoke!
      end
    end
  end
end

Request Identifier

module ASIR
  class Request
    def create_identifier!
      @identifier ||= 
        [
          @@counter += 1,
          $$,
          Thread.current.object_id,
          @@uuid ||= File.read("/proc/sys/kernel/random/uuid").chomp!
        ] * '-'
    end
    @@counter ||= 0; @@uuid ||= nil
  end
end

Call service directly

require 'example_helper'
pr Email.send_email(:pdf_invoice, 
                    :to => "user@email.com",
                    :customer => @customer)

Call service directly – Output

*** 69196: Email.send_mail :pdf_invoice {:to=>"user@email.com", :customer=>123}
*** 69196: pr: :ok

					

In-core, in-process service

require 'example_helper'
pr Email.client.send_email(:pdf_invoice,
                           :to => "user@email.com",
                           :customer => @customer)
#

Sample Service with Client Support

require 'asir'

# Added .client support.
module Email
  include ASIR::Client # Email.client

  def send_email template_name, options
    $stderr.puts "*** #{$$}: Email.send_mail #{template_name.inspect} #{options.inspect}"
    :ok
  end

  def do_raise msg
    raise msg
  end

  extend self
end

Local Transport

Send Request to same process.

module ASIR
  class Transport
    class Local < self
      # Returns Response object.
      def _send_request request
        invoke_request!(request)
      end

      # Returns Response object from #_send_request.
      def _receive_response opaque
        opaque
      end
    end
  end
end

In-core, in-process service – Output

  69198 ASIR::Client::Proxy method_missing :send_email [:pdf_invoice, {:to=>"use\
r@email.com", :customer=>123}]
  69198 ASIR::Transport::Local :send_request, :request, #<ASIR::Request:0x100481\
e20 @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :c\
ustomer=>123}], @selector=:send_email, @receiver=Email> => ...
  69198 ASIR::Coder::Identity :encode, #<ASIR::Request:0x100481e20 @receiver_cla\
ss=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}], @\
selector=:send_email, @receiver=Email> => ...
  69198 ASIR::Coder::Identity :encode, #<ASIR::Request:0x100481e20 @receiver_cla\
ss=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}], @\
selector=:send_email, @receiver=Email> => 
    #<ASIR::Request:0x100481e20 @receiver_class=Module, @arguments=[:pdf_invoice\
, {:to=>"user@email.com", :customer=>123}], @selector=:send_email, @receiver=Ema\
il>
  69198 ASIR::Transport::Local :invoke_request!, #<ASIR::Request:0x100481e20 @re\

MORE

In-core, in-process service – Output – Page 2

ceiver_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer\
=>123}], @selector=:send_email, @receiver=Email> => ...
*** 69198: Email.send_mail :pdf_invoice {:to=>"user@email.com", :customer=>123}
  69198 ASIR::Transport::Local :invoke_request!, #<ASIR::Request:0x100481e20 @re\
ceiver_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer\
=>123}], @selector=:send_email, @receiver=Email> => 
    #<ASIR::Response:0x100480d40 @result=:ok, @exception=nil, @request=#<ASIR::R\
equest:0x100481e20 @receiver_class=Module, @result=:ok, @arguments=[:pdf_invoice\
, {:to=>"user@email.com", :customer=>123}], @selector=:send_email, @receiver=Ema\
il>>
  69198 ASIR::Transport::Local :receive_response => ...
  69198 ASIR::Coder::Identity :decode, #<ASIR::Response:0x100480d40 @result=:ok,\
 @exception=nil, @request=#<ASIR::Request:0x100481e20 @receiver_class=Module, @r\
esult=:ok, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}], @\
selector=:send_email, @receiver=Email>> => ...

MORE

In-core, in-process service – Output – Page 3

  69198 ASIR::Coder::Identity :decode, #<ASIR::Response:0x100480d40 @result=:ok,\
 @exception=nil, @request=#<ASIR::Request:0x100481e20 @receiver_class=Module, @r\
esult=:ok, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}], @\
selector=:send_email, @receiver=Email>> => 
    #<ASIR::Response:0x100480d40 @result=:ok, @exception=nil, @request=#<ASIR::R\
equest:0x100481e20 @receiver_class=Module, @result=:ok, @arguments=[:pdf_invoice\
, {:to=>"user@email.com", :customer=>123}], @selector=:send_email, @receiver=Ema\
il>>
  69198 ASIR::Transport::Local :receive_response => 
    #<ASIR::Response:0x100480d40 @result=:ok, @exception=nil, @request=#<ASIR::R\
equest:0x100481e20 @receiver_class=Module, @result=:ok, @arguments=[:pdf_invoice\
, {:to=>"user@email.com", :customer=>123}], @selector=:send_email, @receiver=Ema\
il>>
  69198 ASIR::Transport::Local :send_request, :response, #<ASIR::Response:0x1004\
80d40 @result=:ok, @exception=nil, @request=#<ASIR::Request:0x100481e20 @receive\

MORE

In-core, in-process service – Output – Page 4

r_class=Module, @result=:ok, @arguments=[:pdf_invoice, {:to=>"user@email.com", :\
customer=>123}], @selector=:send_email, @receiver=Email>>
  69198 ASIR::Transport::Local :send_request, :request, #<ASIR::Request:0x100481\
e20 @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :c\
ustomer=>123}], @selector=:send_email, @receiver=Email> => 
    :ok
*** 69198: pr: :ok

					

One-way, asynchronous subprocess service

require 'example_helper'
begin
  Email.client.transport = t =
    ASIR::Transport::Subprocess.new

  pr Email.client.send_email(:pdf_invoice,
                             :to => "user@email.com",
                             :customer => @customer)
end

Subprocess Transport

Send one-way Request to a forked subprocess.

module ASIR
  class Transport
    class Subprocess < Local
      def _send_request request
        Process.fork do 
          super
        end
        nil # opaque
      end

      # one-way; no Response
      def _receive_response opaque
        nil
      end
    end
  end
end

One-way, asynchronous subprocess service – Output

  69200 ASIR::Client::Proxy method_missing :send_email [:pdf_invoice, {:to=>"use\
r@email.com", :customer=>123}]
  69200 ASIR::Transport::Subprocess :send_request, :request, #<ASIR::Request:0x1\
004819e8 @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to\
=>"user@email.com", :customer=>123}], @selector=:send_email> => ...
  69200 ASIR::Coder::Identity :encode, #<ASIR::Request:0x1004819e8 @receiver=Ema\
il, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :c\
ustomer=>123}], @selector=:send_email> => ...
  69200 ASIR::Coder::Identity :encode, #<ASIR::Request:0x1004819e8 @receiver=Ema\
il, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :c\
ustomer=>123}], @selector=:send_email> => 
    #<ASIR::Request:0x1004819e8 @receiver=Email, @receiver_class=Module, @argume\
nts=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}], @selector=:send_ema\
il>
  69200 ASIR::Transport::Subprocess :receive_response => ...

MORE

One-way, asynchronous subprocess service – Output – Page 2

  69200 ASIR::Coder::Identity :decode, nil => ...
  69200 ASIR::Coder::Identity :decode, nil => 
    nil
  69200 ASIR::Transport::Subprocess :receive_response => 
    nil
  69200 ASIR::Transport::Subprocess :send_request, :response, nil
  69200 ASIR::Transport::Subprocess :send_request, :request, #<ASIR::Request:0x1\
004819e8 @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to\
=>"user@email.com", :customer=>123}], @selector=:send_email> => 
    nil
*** 69200: pr: nil
  69201 ASIR::Transport::Subprocess :invoke_request!, #<ASIR::Request:0x1004819e\
8 @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"user\
@email.com", :customer=>123}], @selector=:send_email> => ...
*** 69201: Email.send_mail :pdf_invoice {:to=>"user@email.com", :customer=>123}

MORE

One-way, asynchronous subprocess service – Output – Page 3

  69201 ASIR::Transport::Subprocess :invoke_request!, #<ASIR::Request:0x1004819e\
8 @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"user\
@email.com", :customer=>123}], @selector=:send_email> => 
    #<ASIR::Response:0x100480958 @result=:ok, @exception=nil, @request=#<ASIR::R\
equest:0x1004819e8 @receiver=Email, @receiver_class=Module, @result=:ok, @argume\
nts=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}], @selector=:send_ema\
il>>

One-way, file log service

require 'example_helper'
begin
  File.unlink(service_log = "service.log") rescue nil

  Email.client.transport = t =
    ASIR::Transport::File.new(:file => service_log)
  t.encoder = 
    ASIR::Coder::Yaml.new

  pr Email.client.send_email(:pdf_invoice,
                             :to => "user@email.com",
                             :customer => @customer)
ensure
  t.close
  puts "\x1a\n#{service_log.inspect} contents:"
  puts File.read(service_log)
end

YAML Coder

Use YAML for encode/decode.

module ASIR
  class Coder
    class Yaml < self
      def _encode obj
        case obj
        when Request
          obj = obj.encode_receiver!
        end
        ::YAML::dump(obj)
      end

      def _decode obj
        case obj = ::YAML::load(obj)
        when Request
          obj.decode_receiver!
        else
          obj
        end
      end
    end
  end
end

File Transport

Send Request one-way to a file.
Can be used as a log or named pipe service.

module ASIR
  class Transport
    class File < Stream
      include PayloadIO # _write, _read

      attr_accessor :file, :stream

      def _send_request request
        _write request, stream
      ensure
        close if ::File.pipe?(file)
      end

      def _receive_request stream
        _read stream
      end

      # one-way; no Response
      def _send_response stream
        nil
      end

      # one-way; no Response
      def _receive_response opaque
        nil
      end

      # File Transport Support
      def stream; ...; end 
      # Process (receive) requests from a file.
      def serve_file!; ...; end 
      # Named Pipe Server
      def prepare_pipe_server!; ...; end 
    end
  end
end

File Transport Support

module ASIR
  class Transport
    class File < Stream
      def stream
        @stream ||=
          ::File.open(file, "w+")
      end
    end
  end
end

Payload IO for Transport

Framing

  • Line containing the number of bytes in the payload.
  • The payload bytes.
  • Blank line.
module ASIR
  class Transport
    module PayloadIO
      def _write payload, stream
        _log { "  _write size = #{payload.size}" }
        stream.puts payload.size
        _log { "  _write #{payload.inspect}" }
        stream.write payload
        stream.puts EMPTY_STRING
        stream.flush
      end

      def _read stream
        size = stream.readline.chomp.to_i
        _log { "  _read  size = #{size.inspect}" }
        payload = stream.read(size)
        _log { "  _read  #{payload.inspect}" }
        stream.readline
        payload
      end

    end
  end
end

One-way, file log service – Output

  69203 ASIR::Client::Proxy method_missing :send_email [:pdf_invoice, {:to=>"use\
r@email.com", :customer=>123}]
  69203 ASIR::Transport::File :send_request, :request, #<ASIR::Request:0x100480d\
18 @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"use\
r@email.com", :customer=>123}], @selector=:send_email> => ...
  69203 ASIR::Coder::Yaml :encode, #<ASIR::Request:0x100480d18 @receiver=Email, \
@receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :custo\
mer=>123}], @selector=:send_email> => ...
  69203 ASIR::Coder::Yaml :encode, #<ASIR::Request:0x100480d18 @receiver=Email, \
@receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :custo\
mer=>123}], @selector=:send_email> => 
    "--- !ruby/object:ASIR::Request \narguments: \n- :pdf_invoice\n- :to: user@e\
mail.com\n  :customer: 123\nreceiver: Email\nreceiver_class: Module\nselector: :\
send_email\n"
  69203 ASIR::Transport::File   _write size = 159

MORE

One-way, file log service – Output – Page 2

  69203 ASIR::Transport::File   _write "--- !ruby/object:ASIR::Request \nargumen\
ts: \n- :pdf_invoice\n- :to: user@email.com\n  :customer: 123\nreceiver: Email\n\
receiver_class: Module\nselector: :send_email\n"
  69203 ASIR::Transport::File :receive_response => ...
  69203 ASIR::Coder::Yaml :decode, nil => ...
  69203 ASIR::Coder::Yaml :decode, nil => 
    nil
  69203 ASIR::Transport::File :receive_response => 
    nil
  69203 ASIR::Transport::File :send_request, :response, nil
  69203 ASIR::Transport::File :send_request, :request, #<ASIR::Request:0x100480d\
18 @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"use\
r@email.com", :customer=>123}], @selector=:send_email> => 
    nil
*** 69203: pr: nil

MORE

One-way, file log service – Output – Page 3


MORE

One-way, file log service – Output – Page 4

"service.log" contents:
159
--- !ruby/object:ASIR::Request 
arguments: 
- :pdf_invoice
- :to: user@email.com
  :customer: 123
receiver: Email
receiver_class: Module
selector: :send_email

Replay file log

require 'example_helper'
begin
  service_log = "service.log"
  Email.client.transport = t =
    ASIR::Transport::File.new(:file => service_log)
  t.encoder = 
    ASIR::Coder::Yaml.new

  t.serve_file!
ensure
  File.unlink(service_log) rescue nil
end

Transport#receive_request

Receive Request payload from stream.

module ASIR
  class Transport
    def receive_request stream
      _log_result [ :receive_request, :stream, stream ] do
        request_payload = _receive_request(stream)
        encoder.decode(request_payload)
      end
    end
  end
end

Process (receive) requests from a file.

module ASIR
  class Transport
    class File < Stream
      def serve_file!
        ::File.open(file, "r") do | stream |
          serve_stream! stream, nil
        end
      end
    end
  end
end

Stream Transport

Base class handles Requests on stream.

module ASIR
  class Transport
    class Stream < self

      # Serve all Requests from a stream.
      def serve_stream! in_stream, out_stream; ...; end 
      # Serve a Request from a stream.
      def serve_stream_request! in_stream, out_stream; ...; end 
  end
end

Serve all Requests from a stream.

module ASIR
  class Transport
    class Stream < self
      def serve_stream! in_stream, out_stream
        until in_stream.eof?
          begin
            serve_stream_request! in_stream, out_stream
          rescue Exception => err
            _log [ :serve_stream_error, err ]
          end
        end
      end
    end
  end
end

Serve a Request from a stream.

module ASIR
  class Transport
    class Stream < self
      def serve_stream_request! in_stream, out_stream
        request = request_ok = result = result_ok = exception = nil
        request = receive_request(in_stream)
        request_ok = true
        result = invoke_request!(request)
        result_ok = true
      rescue Exception => exc
        exception = exc
        _log [ :request_error, exc ]
      ensure
        if out_stream
          begin
            if request_ok 
              if exception && ! result_ok
                result = EncapsulatedException.new(exception)
              end
              send_response(result, out_stream)
            end
          rescue Exception => exc
            _log [ :response_error, exc ]
          end
        else
          raise exception if exception
        end
      end
    end
    end
  end
end

Replay file log – Output

  69205 ASIR::Transport::File :receive_request, :stream, #<File:service.log> => \
...
  69205 ASIR::Transport::File   _read  size = 159
  69205 ASIR::Transport::File   _read  "--- !ruby/object:ASIR::Request \nargumen\
ts: \n- :pdf_invoice\n- :to: user@email.com\n  :customer: 123\nreceiver: Email\n\
receiver_class: Module\nselector: :send_email\n"
  69205 ASIR::Coder::Yaml :decode, "--- !ruby/object:ASIR::Request \narguments: \
\n- :pdf_invoice\n- :to: user@email.com\n  :customer: 123\nreceiver: Email\nrece\
iver_class: Module\nselector: :send_email\n" => ...
  69205 ASIR::Coder::Yaml :decode, "--- !ruby/object:ASIR::Request \narguments: \
\n- :pdf_invoice\n- :to: user@email.com\n  :customer: 123\nreceiver: Email\nrece\
iver_class: Module\nselector: :send_email\n" => 
    #<ASIR::Request:0x100480318 @receiver=Email, @receiver_class=Module, @argume\
nts=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}], @selector=:send_ema\
il>

MORE

Replay file log – Output – Page 2

  69205 ASIR::Transport::File :receive_request, :stream, #<File:service.log> => 
    #<ASIR::Request:0x100480318 @receiver=Email, @receiver_class=Module, @argume\
nts=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}], @selector=:send_ema\
il>
  69205 ASIR::Transport::File :invoke_request!, #<ASIR::Request:0x100480318 @rec\
eiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email\
.com", :customer=>123}], @selector=:send_email> => ...
*** 69205: Email.send_mail :pdf_invoice {:to=>"user@email.com", :customer=>123}
  69205 ASIR::Transport::File :invoke_request!, #<ASIR::Request:0x100480318 @rec\
eiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email\
.com", :customer=>123}], @selector=:send_email> => 
    #<ASIR::Response:0x10047f968 @result=:ok, @exception=nil, @request=#<ASIR::R\
equest:0x100480318 @receiver=Email, @receiver_class=Module, @result=:ok, @argume\
nts=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}], @selector=:send_ema\
il>>

One-way, named pipe service

require 'example_helper'
begin
  File.unlink(service_pipe = "service.pipe") rescue nil

  Email.client.transport = t =
    ASIR::Transport::File.new(:file => service_pipe)
  t.encoder = 
    ASIR::Coder::Yaml.new

  t.prepare_pipe_server!
  child_pid = Process.fork do 
    t.run_pipe_server!
  end

  pr Email.client.send_email(:pdf_invoice, :to => "user@email.com", :customer => @customer)
ensure
  t.close
  sleep 1
  Process.kill 9, child_pid
  File.unlink(service_pipe) rescue nil
end

Named Pipe Server

module ASIR
  class Transport
    class File < Stream
      def prepare_pipe_server!
        _log :prepare_pipe_server!
        unless ::File.exist? file
          system(cmd = "mkfifo #{file.inspect}") or raise "cannot run #{cmd.inspect}"
        end
      end

      def run_pipe_server!
        _log :run_pipe_server!
        @running = true
        while @running
          serve_file!
        end
      end
    end
  end
end

One-way, named pipe service – Output

  69207 ASIR::Transport::File :prepare_pipe_server!
  69207 ASIR::Client::Proxy method_missing :send_email [:pdf_invoice, {:to=>"use\
r@email.com", :customer=>123}]
  69207 ASIR::Transport::File :send_request, :request, #<ASIR::Request:0x1004805\
48 @selector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:\
pdf_invoice, {:to=>"user@email.com", :customer=>123}]> => ...
  69207 ASIR::Coder::Yaml :encode, #<ASIR::Request:0x100480548 @selector=:send_e\
mail, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"\
user@email.com", :customer=>123}]> => ...
					

One-way, named pipe service – Output – Page 2

send_email\n"
  69207 ASIR::Transport::File   _write size = 159
  69207 ASIR::Transport::File   _write "--- !ruby/object:ASIR::Request \nargumen\
ts: \n- :pdf_invoice\n- :to: user@email.com\n  :customer: 123\nreceiver: Email\n\
receiver_class: Module\nselector: :send_email\n"
  69207 ASIR::Transport::File :receive_response => ...
  69207 ASIR::Coder::Yaml :decode, nil => ...
  69207 ASIR::Coder::Yaml :decode, nil => 
    nil
  69207 ASIR::Transport::File :receive_response => 
    nil
  69207 ASIR::Transport::File :send_request, :response, nilFile:service.pipe> =>\
 ...
					

One-way, named pipe service – Output – Page 3

:pdf_invoice, {:to=>"user@email.com", :customer=>123}]> => 
    nil
  69209 ASIR::Transport::File   _read  size = 159
  69209 ASIR::Transport::File   _read  "--- !ruby/object:ASIR::Request \nargumen\
ts: \n- :pdf_invoice\n- :to: user@email.com\n  :customer: 123\nreceiver: Email\n\
receiver_class: Module\nselector: :send_email\n"
  69209 ASIR::Coder::Yaml :decode, "--- !ruby/object:ASIR::Request \narguments: \
\n- :pdf_invoice\n- :to: user@email.com\n  :customer: 123\nreceiver: Email\nrece\
iver_class: Module\nselector: :send_email\n" => ...
  69209 ASIR::Coder::Yaml :decode, "--- !ruby/object:ASIR::Request \narguments: \
\n- :pdf_invoice\n- :to: user@email.com\n  :customer: 123\nreceiver: Email\nrece\
iver_class: Module\nselector: :send_email\n" => 
    #<ASIR::Request:0x10047f0f8 @selector=:send_email, @receiver=Email, @receive\
r_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>123\
}]>

MORE

One-way, named pipe service – Output – Page 4

  69209 ASIR::Transport::File :receive_request, :stream, #<File:service.pipe> =>\
 
    #<ASIR::Request:0x10047f0f8 @selector=:send_email, @receiver=Email, @receive\
r_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>123\
}]>
  69209 ASIR::Transport::File :invoke_request!, #<ASIR::Request:0x10047f0f8 @sel\
ector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_inv\
oice, {:to=>"user@email.com", :customer=>123}]> => ...
*** 69209: Email.send_mail :pdf_invoice {:to=>"user@email.com", :customer=>123}
  69209 ASIR::Transport::File :invoke_request!, #<ASIR::Request:0x10047f0f8 @sel\
ector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_inv\
oice, {:to=>"user@email.com", :customer=>123}]> => 
    #<ASIR::Response:0x10047e748 @request=#<ASIR::Request:0x10047f0f8 @selector=\
:send_email, @receiver=Email, @receiver_class=Module, @result=:ok, @arguments=[:\
pdf_invoice, {:to=>"user@email.com", :customer=>123}]>, @result=:ok, @exception=\

MORE

One-way, named pipe service – Output – Page 5

nil>
*** 69207: pr: nil

					

One-way, named pipe service with signature

require 'example_helper'
begin
  File.unlink(service_pipe = "service.pipe") rescue nil
  Email.client.transport = t =
    ASIR::Transport::File.new(:file => service_pipe)
  t.encoder = 
    ASIR::Coder::Chain.new(:encoders =>
      [ ASIR::Coder::Marshal.new,
        s = ASIR::Coder::Sign.new(:secret => 'abc123'),
        ASIR::Coder::Yaml.new,
      ])

  t.prepare_pipe_server!
  child_pid = Process.fork do 
    t.run_pipe_server!
  end

  pr Email.client.send_email(:pdf_invoice, :to => "user@email.com", :customer => @customer)
ensure
  t.close; sleep 1
  Process.kill 9, child_pid
  File.unlink(service_pipe) rescue nil
end

Chain Coder

Chain multiple Coders as one.

  request  --> | e1 | --> | e2 | --> | eN | --> 
  response <-- | d1 | <-- | d2 | <-- | dN | <--
module ASIR
  class Coder
    class Chain < self
      attr_accessor :encoders

      def _encode obj
        encoders.each do | e |
          obj = e.encode(obj)
        end
        obj
      end

      def _decode obj
        encoders.reverse_each do | e |
          obj = e.decode(obj)
        end
        obj
      end
    end
  end
end

Marshal Coder

Use Ruby Marshal for encode/decode.

module ASIR
  class Coder
    class Marshal < self
      def _encode obj
        ::Marshal.dump(obj)
      end

      def _decode obj
        ::Marshal.load(obj)
      end
    end
  end
end

Sign Coder

Sign payload during encode, check signature during decode.

Signature is the digest of secret + payload.

Encode payload as Hash containing the digest function name, signature and payload.
Decode and validate Hash containing the digest function name, signature and payload.

module ASIR
  class Coder
    class Sign < self
      attr_accessor :secret, :function

      def _encode obj
        payload = obj.to_s
        { 
          :function => function,
          :signature => ::Digest.const_get(function).
                          new.hexdigest(secret + payload),
          :payload => payload,
        }
      end

      def _decode obj
        raise SignatureError, "expected Hash, given #{obj.class}" unless Hash === obj
        payload = obj[:payload]
        raise SignatureError, "signature invalid" unless obj == _encode(payload)
        payload
      end

      # Sign Coder Support
      class SignatureError < Error; end; ...; end 
    end
  end
end

Sign Coder Support

Signature Error.

module ASIR
  class Coder
    class Sign < self
      class SignatureError < Error; end

      def initialize_before_opts
        @function = :SHA1
      end
    end
  end
end

One-way, named pipe service with signature – Output

  69211 ASIR::Transport::File :prepare_pipe_server!
  69211 ASIR::Client::Proxy method_missing :send_email [:pdf_invoice, {:to=>"use\
r@email.com", :customer=>123}]
  69211 ASIR::Transport::File :send_request, :request, #<ASIR::Request:0x10047fe\
18 @selector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:\
pdf_invoice, {:to=>"user@email.com", :customer=>123}]> => ...
  69211 ASIR::Coder::Chain :encode, #<ASIR::Request:0x10047fe18 @selector=:send_\
email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>\
"user@email.com", :customer=>123}]> => ...
  69211 ASIR::Coder::Marshal :encode, #<ASIR::Request:0x10047fe18 @selector=:sen\
d_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to\
=>"user@email.com", :customer=>123}]> => ...
  69213 ASIR::Transport::File :run_pipe_server!
  69211 ASIR::Coder::Marshal :encode, #<ASIR::Request:0x10047fe18 @selector=:sen\
d_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to\

MORE

One-way, named pipe service with signature – Output – Page 2

=>"user@email.com", :customer=>123}]> => 
    "\004\bo:\022ASIR::Request\t:\016@selector:\017send_email:\016@receiverm\nEm\
ail:\024@receiver_classc\vModule:\017@arguments[\a:\020pdf_invoice{\a:\ato\"\023\
user@email.com:\rcustomeri\001{"
  69211 ASIR::Coder::Sign :encode, "\004\bo:\022ASIR::Request\t:\016@selector:\0\
17send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@arguments[\\
a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{" => ...
  69211 ASIR::Coder::Sign :encode, "\004\bo:\022ASIR::Request\t:\016@selector:\0\
17send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@arguments[\\
a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{" => 
    {:signature=>"410772a1662ec5760db1c791b008e74e80ab2e4f", :function=>:SHA1, :\
payload=>"\004\bo:\022ASIR::Request\t:\016@selector:\017send_email:\016@receiver\
m\nEmail:\024@receiver_classc\vModule:\017@arguments[\a:\020pdf_invoice{\a:\ato\\
"\023user@email.com:\rcustomeri\001{"}
  69211 ASIR::Coder::Yaml :encode, {:signature=>"410772a1662ec5760db1c791b008e74\

MORE

One-way, named pipe service with signature – Output – Page 3

e80ab2e4f", :function=>:SHA1, :payload=>"\004\bo:\022ASIR::Request\t:\016@select\
or:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@argume\
nts[\a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{"} => ...
  69211 ASIR::Coder::Yaml :encode, {:signature=>"410772a1662ec5760db1c791b008e74\
e80ab2e4f", :function=>:SHA1, :payload=>"\004\bo:\022ASIR::Request\t:\016@select\
or:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@argume\
nts[\a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{"} => 
    "--- \n:signature: 410772a1662ec5760db1c791b008e74e80ab2e4f\n:function: :SHA\
1\n:payload: \"\\x04\\bo:\\x12ASIR::Request\\t:\\x0E@selector:\\x0Fsend_email:\\\
x0E@receiverm\\n\\\n  Email:\\x14@receiver_classc\\vModule:\\x0F@arguments[\\a:\\
\x10pdf_invoice{\\a:\\ato\\\"\\x13user@email.com:\\rcustomeri\\x01{\"\n"
  69211 ASIR::Coder::Chain :encode, #<ASIR::Request:0x10047fe18 @selector=:send_\
email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>\
"user@email.com", :customer=>123}]> => 
    "--- \n:signature: 410772a1662ec5760db1c791b008e74e80ab2e4f\n:function: :SHA\

MORE

One-way, named pipe service with signature – Output – Page 4

1\n:payload: \"\\x04\\bo:\\x12ASIR::Request\\t:\\x0E@selector:\\x0Fsend_email:\\\
x0E@receiverm\\n\\\n  Email:\\x14@receiver_classc\\vModule:\\x0F@arguments[\\a:\\
\x10pdf_invoice{\\a:\\ato\\\"\\x13user@email.com:\\rcustomeri\\x01{\"\n"
  69211 ASIR::Transport::File   _write size = 278
  69211 ASIR::Transport::File   _write "--- \n:signature: 410772a1662ec5760db1c7\
91b008e74e80ab2e4f\n:function: :SHA1\n:payload: \"\\x04\\bo:\\x12ASIR::Request\\\
t:\\x0E@selector:\\x0Fsend_email:\\x0E@receiverm\\n\\\n  Email:\\x14@receiver_cl\
assc\\vModule:\\x0F@arguments[\\a:\\x10pdf_invoice{\\a:\\ato\\\"\\x13user@email.\
com:\\rcustomeri\\x01{\"\n"
  69211 ASIR::Transport::File :receive_response => ...
  69211 ASIR::Coder::Chain :decode, nil => ...
  69211 ASIR::Coder::Chain :decode, nil => 
    nil
  69211 ASIR::Transport::File :receive_response => 
    nil

MORE

One-way, named pipe service with signature – Output – Page 5

  69213 ASIR::Transport::File :receive_request, :stream, #<
ile:service.pipe> => ...
					

One-way, named pipe service with signature – Output – Page 6

\x0E@selector:\\x0Fsend_email:\\x0E@receiverm\\n\\\n  Email:\\x14@receiver_class\
c\\vModule:\\x0F@arguments[\\a:\\x10pdf_invoice{\\a:\\ato\\\"\\x13user@email.com\
:\\rcustomeri\\x01{\"\n" => ...
  69213 ASIR::Coder::Yaml :decode, "--- \n:signature: 410772a1662ec5760db1c791b0\
08e74e80ab2e4f\n:function: :SHA1\n:payload: \"\\x04\\bo:\\x12ASIR::Request\\t:\\\
x0E@selector:\\x0Fsend_email:\\x0E@receiverm\\n\\\n  Email:\\x14@receiver_classc\
\\vModule:\\x0F@arguments[\\a:\\x10pdf_invoice{\\a:\\ato\\\"\\x13user@email.com:\
\\rcustomeri\\x01{\"\n" => ...
  69213 ASIR::Coder::Yaml :decode, "--- \n:signature: 410772a1662ec5760db1c791b0\
08e74e80ab2e4f\n:function: :SHA1\n:payload: \"\\x04\\bo:\\x12ASIR::Request\\t:\\\
x0E@selector:\\x0Fsend_email:\\x0E@receiverm\\n\\\n  Email:\\x14@receiver_classc\
\\vModule:\\x0F@arguments[\\a:\\x10pdf_invoice{\\a:\\ato\\\"\\x13user@email.com:\
\\rcustomeri\\x01{\"\n" => 
    {:signature=>"410772a1662ec5760db1c791b008e74e80ab2e4f", :function=>:SHA1, :\
payload=>"\004\bo:\022ASIR::Request\t:\016@selector:\017send_email:\016@receiver\

MORE

One-way, named pipe service with signature – Output – Page 7

m\nEmail:\024@receiver_classc\vModule:\017@arguments[\a:\020pdf_invoice{\a:\ato\\
"\023user@email.com:\rcustomeri\001{"}
  69213 ASIR::Coder::Sign :decode, {:signature=>"410772a1662ec5760db1c791b008e74\
e80ab2e4f", :function=>:SHA1, :payload=>"\004\bo:\022ASIR::Request\t:\016@select\
or:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@argume\
nts[\a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{"} => ...
  69213 ASIR::Coder::Sign :decode, {:signature=>"410772a1662ec5760db1c791b008e74\
e80ab2e4f", :function=>:SHA1, :payload=>"\004\bo:\022ASIR::Request\t:\016@select\
or:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@argume\
nts[\a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{"} => 
    "\004\bo:\022ASIR::Request\t:\016@selector:\017send_email:\016@receiverm\nEm\
ail:\024@receiver_classc\vModule:\017@arguments[\a:\020pdf_invoice{\a:\ato\"\023\
user@email.com:\rcustomeri\001{"
  69213 ASIR::Coder::Marshal :decode, "\004\bo:\022ASIR::Request\t:\016@selector\
:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@argument\

MORE

One-way, named pipe service with signature – Output – Page 8

s[\a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{" => ...
  69213 ASIR::Coder::Marshal :decode, "\004\bo:\022ASIR::Request\t:\016@selector\
:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@argument\
s[\a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{" => 
    #<ASIR::Request:0x10047e6a8 @selector=:send_email, @receiver=Email, @receive\
r_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>123\
}]>
  69213 ASIR::Coder::Chain :decode, "--- \n:signature: 410772a1662ec5760db1c791b\
008e74e80ab2e4f\n:function: :SHA1\n:payload: \"\\x04\\bo:\\x12ASIR::Request\\t:\\
\x0E@selector:\\x0Fsend_email:\\x0E@receiverm\\n\\\n  Email:\\x14@receiver_class\
c\\vModule:\\x0F@arguments[\\a:\\x10pdf_invoice{\\a:\\ato\\\"\\x13user@email.com\
:\\rcustomeri\\x01{\"\n" => 
    #<ASIR::Request:0x10047e6a8 @selector=:send_email, @receiver=Email, @receive\
r_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>123\
}]>

MORE

One-way, named pipe service with signature – Output – Page 9

  69213 ASIR::Transport::File :receive_request, :stream, #<File:service.pipe> =>\
 
    #<ASIR::Request:0x10047e6a8 @selector=:send_email, @receiver=Email, @receive\
r_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>123\
}]>
  69213 ASIR::Transport::File :invoke_request!, #<ASIR::Request:0x10047e6a8 @sel\
ector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_inv\
oice, {:to=>"user@email.com", :customer=>123}]> => ...
*** 69213: Email.send_mail :pdf_invoice {:to=>"user@email.com", :customer=>123}
  69213 ASIR::Transport::File :invoke_request!, #<ASIR::Request:0x10047e6a8 @sel\
ector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_inv\
oice, {:to=>"user@email.com", :customer=>123}]> => 
    #<ASIR::Response:0x10047db40 @request=#<ASIR::Request:0x10047e6a8 @selector=\
:send_email, @receiver=Email, @receiver_class=Module, @result=:ok, @arguments=[:\
pdf_invoice, {:to=>"user@email.com", :customer=>123}]>, @result=:ok, @exception=\

MORE

One-way, named pipe service with signature – Output – Page 10

nil>
*** 69211: pr: nil

					

One-way, named pipe service with invalid signature

require 'example_helper'
begin
  File.unlink(service_pipe = "service.pipe") rescue nil

  Email.client.transport = t = 
    ASIR::Transport::File.new(:file => service_pipe)
  t.encoder = 
    ASIR::Coder::Chain.new(:encoders =>
      [ ASIR::Coder::Marshal.new,
        s = ASIR::Coder::Sign.new(:secret => 'abc123'),
        ASIR::Coder::Yaml.new,
      ])
  
  t.prepare_pipe_server!
  child_pid = Process.fork do 
    t.run_pipe_server!
  end
  
  s.secret = 'I do not know the secret! :('
  
  pr Email.client.send_email(:pdf_invoice, :to => "user@email.com", :customer => @customer)
ensure
  t.close; sleep 1
  Process.kill 9, child_pid
  File.unlink(service_pipe) rescue nil
end

One-way, named pipe service with invalid signature – Output

  69215 ASIR::Transport::File :prepare_pipe_server!
  69215 ASIR::Client::Proxy method_missing :send_email [:pdf_invoice, {:to=>"use\
r@email.com", :customer=>123}]
  69215 ASIR::Transport::File :send_request, :request, #<ASIR::Request:0x10047fc\
88 @selector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:\
pdf_invoice, {:to=>"user@email.com", :customer=>123}]> => ...
  69215 ASIR::Coder::Chain :encode, #<ASIR::Request:0x10047fc88 @selector=:send_\
email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>\
"user@email.com", :customer=>123}]> => ...
  69215 ASIR::Coder::Marshal :encode, #<ASIR::Request:0x10047fc88 @selector=:sen\
d_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to\
=>"user@email.com", :customer=>123}]> => ...
  69217 ASIR::Transport::File :run_pipe_server!  69215 ASIR::Coder::Marshal :enc\
ode, #<ASIR::Request:0x10047fc88 @selector=:send_email, @receiver=Email, @receiv\
er_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>12\

MORE

One-way, named pipe service with invalid signature – Output – Page 2

3}]> => 
    "\004\bo:\022ASIR::Request\t:\016@selector:\017send_email:\016@receiverm\nEm\
ail:\024@receiver_classc\vModule:\017@arguments[\a:\020pdf_invoice{\a:\ato\"\023\
user@email.com:\rcustomeri\001{"
					

One-way, named pipe service with invalid signature – Output – Page 3

5f5d055a4", :function=>:SHA1, :payload=>"\004\bo:\022ASIR::Request\t:\016@select\
or:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@argume\
nts[\a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{"} => ...
  69215 ASIR::Coder::Yaml :encode, {:signature=>"54acdd09394051fa1e32a06bc00633c\
5f5d055a4", :function=>:SHA1, :payload=>"\004\bo:\022ASIR::Request\t:\016@select\
or:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@argume\
nts[\a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{"} => 
    "--- \n:signature: 54acdd09394051fa1e32a06bc00633c5f5d055a4\n:function: :SHA\
1\n:payload: \"\\x04\\bo:\\x12ASIR::Request\\t:\\x0E@selector:\\x0Fsend_email:\\\
x0E@receiverm\\n\\\n  Email:\\x14@receiver_classc\\vModule:\\x0F@arguments[\\a:\\
\x10pdf_invoice{\\a:\\ato\\\"\\x13user@email.com:\\rcustomeri\\x01{\"\n"
  69215 ASIR::Coder::Chain :encode, #<ASIR::Request:0x10047fc88 @selector=:send_\
email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>\
"user@email.com", :customer=>123}]> => 
    "--- \n:signature: 54acdd09394051fa1e32a06bc00633c5f5d055a4\n:function: :SHA\

MORE

One-way, named pipe service with invalid signature – Output – Page 4

1\n:payload: \"\\x04\\bo:\\x12ASIR::Request\\t:\\x0E@selector:\\x0Fsend_email:\\\
x0E@receiverm\\n\\\n  Email:\\x14@receiver_classc\\vModule:\\x0F@arguments[\\a:\\
\x10pdf_invoice{\\a:\\ato\\\"\\x13user@email.com:\\rcustomeri\\x01{\"\n"
  69215 ASIR::Transport::File   _write size = 278
  69215 ASIR::Transport::File   _write "--- \n:signature: 54acdd09394051fa1e32a0\
6bc00633c5f5d055a4\n:function: :SHA1\n:payload: \"\\x04\\bo:\\x12ASIR::Request\\\
t:\\x0E@selector:\\x0Fsend_email:\\x0E@receiverm\\n\\\n  Email:\\x14@receiver_cl\
assc\\vModule:\\x0F@arguments[\\a:\\x10pdf_invoice{\\a:\\ato\\\"\\x13user@email.\
com:\\rcustomeri\\x01{\"\n"
  69215 ASIR::Transport::File :receive_response => ...
  69215 ASIR::Coder::Chain :decode, nil => ...
  69215 ASIR::Coder::Chain :decode, nil => 
    nil
  69215 ASIR::Transport::File :receive_response => 
    nil

MORE

One-way, named pipe service with invalid signature – Output – Page 5

  69215 ASIR::Transport::File :send_request, :response, nil
  69215 ASIR::Transport::File :send_request, :request, #<ASIR::Request:0x10047fc\
88 @selector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:\
pdf_invoice, {:to=>"user@email.com", :customer=>123}]> => 
    nil
  69217 ASIR::Transport::File :receive_request, :stream, #<File:service.pipe> =>\
 ...
  69217 ASIR::Transport::File   _read  size = 278
  69217 ASIR::Transport::File   _read  "--- \n:signature: 54acdd09394051fa1e32a0\
6bc00633c5f5d055a4\n:function: :SHA1\n:payload: \"\\x04\\bo:\\x12ASIR::Request\\\
t:\\x0E@selector:\\x0Fsend_email:\\x0E@receiverm\\n\\\n  Email:\\x14@receiver_cl\
assc\\vModule:\\x0F@arguments[\\a:\\x10pdf_invoice{\\a:\\ato\\\"\\x13user@email.\
com:\\rcustomeri\\x01{\"\n"
  69217 ASIR::Coder::Chain :decode, "--- \n:signature: 54acdd09394051fa1e32a06bc\
00633c5f5d055a4\n:function: :SHA1\n:payload: \"\\x04\\bo:\\x12ASIR::Request\\t:\\

MORE

One-way, named pipe service with invalid signature – Output – Page 6

\x0E@selector:\\x0Fsend_email:\\x0E@receiverm\\n\\\n  Email:\\x14@receiver_class\
c\\vModule:\\x0F@arguments[\\a:\\x10pdf_invoice{\\a:\\ato\\\"\\x13user@email.com\
:\\rcustomeri\\x01{\"\n" => ...
  69217 ASIR::Coder::Yaml :decode, "--- \n:signature: 54acdd09394051fa1e32a06bc0\
0633c5f5d055a4\n:function: :SHA1\n:payload: \"\\x04\\bo:\\x12ASIR::Request\\t:\\\
x0E@selector:\\x0Fsend_email:\\x0E@receiverm\\n\\\n  Email:\\x14@receiver_classc\
\\vModule:\\x0F@arguments[\\a:\\x10pdf_invoice{\\a:\\ato\\\"\\x13user@email.com:\
\\rcustomeri\\x01{\"\n" => ...
  69217 ASIR::Coder::Yaml :decode, "--- \n:signature: 54acdd09394051fa1e32a06bc0\
0633c5f5d055a4\n:function: :SHA1\n:payload: \"\\x04\\bo:\\x12ASIR::Request\\t:\\\
x0E@selector:\\x0Fsend_email:\\x0E@receiverm\\n\\\n  Email:\\x14@receiver_classc\
\\vModule:\\x0F@arguments[\\a:\\x10pdf_invoice{\\a:\\ato\\\"\\x13user@email.com:\
\\rcustomeri\\x01{\"\n" => 
    {:signature=>"54acdd09394051fa1e32a06bc00633c5f5d055a4", :function=>:SHA1, :\
payload=>"\004\bo:\022ASIR::Request\t:\016@selector:\017send_email:\016@receiver\

MORE

One-way, named pipe service with invalid signature – Output – Page 7

m\nEmail:\024@receiver_classc\vModule:\017@arguments[\a:\020pdf_invoice{\a:\ato\\
"\023user@email.com:\rcustomeri\001{"}
  69217 ASIR::Coder::Sign :decode, {:signature=>"54acdd09394051fa1e32a06bc00633c\
5f5d055a4", :function=>:SHA1, :payload=>"\004\bo:\022ASIR::Request\t:\016@select\
or:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@argume\
nts[\a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{"} => ...
  69217 ASIR::Transport::File :request_error, #<ASIR::Coder::Sign::SignatureErro\
r: signature invalid>
    ./lib/asir.rb:500:in `_decode'
    ./lib/asir.rb:352:in `decode'
    ./lib/asir.rb:226:in `_log_result'
    ./lib/asir.rb:351:in `decode'
    ./lib/asir.rb:467:in `_decode'
    ./lib/asir.rb:466:in `reverse_each'
    ./lib/asir.rb:466:in `_decode'

MORE

One-way, named pipe service with invalid signature – Output – Page 8

    ./lib/asir.rb:352:in `decode'
    ./lib/asir.rb:226:in `_log_result'
    ./lib/asir.rb:351:in `decode'
    ./lib/asir.rb:565:in `receive_request'
    ./lib/asir.rb:226:in `_log_result'
    ./lib/asir.rb:563:in `receive_request'
    ./lib/asir.rb:739:in `serve_stream_request!'
    ./lib/asir.rb:728:in `serve_stream!'
    ./lib/asir.rb:808:in `serve_file!'
    ./lib/asir.rb:807:in `open'
    ./lib/asir.rb:807:in `serve_file!'
    ./lib/asir.rb:826:in `run_pipe_server!'
    .riterate/ex08.rb-1-capture.rb:29
    .riterate/ex08.rb-1-capture.rb:28:in `fork'
    .riterate/ex08.rb-1-capture.rb:28

MORE

One-way, named pipe service with invalid signature – Output – Page 9

    .riterate/ex08.rb-1-capture.rb:4:in `__capture_stream'
    .riterate/ex08.rb-1-capture.rb:12
    .riterate/ex08.rb-1-capture.rb:4:in `__capture_stream'
    .riterate/ex08.rb-1-capture.rb:11
    .riterate/ex08.rb-1-capture.rb:10:in `open'
    .riterate/ex08.rb-1-capture.rb:10
  69217 ASIR::Transport::File :serve_stream_error, #<ASIR::Coder::Sign::Signatur\
eError: signature invalid>
    ./lib/asir.rb:500:in `_decode'
    ./lib/asir.rb:352:in `decode'
    ./lib/asir.rb:226:in `_log_result'
    ./lib/asir.rb:351:in `decode'
    ./lib/asir.rb:467:in `_decode'
    ./lib/asir.rb:466:in `reverse_each'
    ./lib/asir.rb:466:in `_decode'

MORE

One-way, named pipe service with invalid signature – Output – Page 10

    ./lib/asir.rb:352:in `decode'
    ./lib/asir.rb:226:in `_log_result'
    ./lib/asir.rb:351:in `decode'
    ./lib/asir.rb:565:in `receive_request'
    ./lib/asir.rb:226:in `_log_result'
    ./lib/asir.rb:563:in `receive_request'
    ./lib/asir.rb:739:in `serve_stream_request!'
    ./lib/asir.rb:728:in `serve_stream!'
    ./lib/asir.rb:808:in `serve_file!'
    ./lib/asir.rb:807:in `open'
    ./lib/asir.rb:807:in `serve_file!'
    ./lib/asir.rb:826:in `run_pipe_server!'
    .riterate/ex08.rb-1-capture.rb:29
    .riterate/ex08.rb-1-capture.rb:28:in `fork'
    .riterate/ex08.rb-1-capture.rb:28

MORE

One-way, named pipe service with invalid signature – Output – Page 11

    .riterate/ex08.rb-1-capture.rb:4:in `__capture_stream'
    .riterate/ex08.rb-1-capture.rb:12
    .riterate/ex08.rb-1-capture.rb:4:in `__capture_stream'
    .riterate/ex08.rb-1-capture.rb:11
    .riterate/ex08.rb-1-capture.rb:10:in `open'
    .riterate/ex08.rb-1-capture.rb:10
*** 69215: pr: nil

					

Socket service

require 'example_helper'
begin
  Email.client.transport = t = 
    ASIR::Transport::TcpSocket.new(:port => 30901)
  t.encoder = 
    ASIR::Coder::Marshal.new
  
  t.prepare_socket_server!
  child_pid = Process.fork do 
    t.run_socket_server!
  end
  
  pr Email.client.send_email(:pdf_invoice, 
                             :to => "user@email.com", :customer => @customer)
ensure
  t.close; sleep 1
  Process.kill 9, child_pid
end

TCP Socket Transport

module ASIR
  class Transport
    class TcpSocket < Stream
      include PayloadIO
      attr_accessor :port, :address
      
      # Returns a connected TCP socket.
      def stream ; ...; end 
      # Sends the encoded Request payload.
      def _send_request request_payload; ...; end 
      # Receives the encoded Request payload.
      def _receive_request stream; ...; end 
      # Sends the encoded Response payload.
      def _send_response response_payload, stream; ...; end 
      # Receives the encoded Response payload.
      def _receive_response opaque; ...; end 
      # TCP Socket Server
      def prepare_socket_server!; ...; end 
  end
end

TCP Socket Server

module ASIR
  class Transport
    class TcpSocket < Stream
      def prepare_socket_server!
        _log { "prepare_socket_server! #{port}" }
        @server = TCPServer.open(port)
        @server.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, false)
      end

      def run_socket_server!
        _log :run_socket_server!
        @running = true
        while @running
          stream = @server.accept
          _log { "run_socket_server!: connected" }
          begin
            # Same socket for both in and out stream.
            serve_stream! stream, stream
          ensure
            stream.close
          end
          _log { "run_socket_server!: disconnected" }
        end
      end

    end
    end
  end
end

Returns a connected TCP socket.

module ASIR
  class Transport
    class TcpSocket < Stream
      def stream 
        @stream ||=
          begin
            addr = address || '127.0.0.1'
            _log { "connect #{addr}:#{port}" }
            sock = TCPSocket.open(addr, port)
            sock
          rescue Exception => err
            raise Error, "Cannot connect to #{addr}:#{port}: #{err.inspect}", err.backtrace
          end
      end
    end
  end
end

Transport#send_response

Send Response to stream.

module ASIR
  class Transport
    def send_response response, stream
      _log_result [ :receive_request, :response, response, :stream, stream ] do
        response_payload = decoder.encode(response)
        _send_response(response_payload, stream)
      end
    end
  end
end

Transport#receive_response

Receieve Response from stream.

module ASIR
  class Transport
    def receive_response opaque
      _log_result [ :receive_response ] do
        response_payload = _receive_response opaque
        decoder.decode(response_payload)
      end
    end
  end
end

Sends the encoded Request payload.

module ASIR
  class Transport
    class TcpSocket < Stream
      def _send_request request_payload
        _write request_payload, stream
      end
    end
  end
end

Receives the encoded Request payload.

module ASIR
  class Transport
    class TcpSocket < Stream
      def _receive_request stream
        _read stream
      end
    end
  end
end

Sends the encoded Response payload.

module ASIR
  class Transport
    class TcpSocket < Stream
      def _send_response response_payload, stream
        _write response_payload, stream
      end
    end
  end
end

Receives the encoded Response payload.

module ASIR
  class Transport
    class TcpSocket < Stream
      def _receive_response opaque
        _read stream
      end
    end
  end
end

Socket service – Output

  69219 ASIR::Transport::TcpSocket prepare_socket_server! 30901
  69219 ASIR::Client::Proxy method_missing :send_email [:pdf_invoice, {:to=>"use\
r@email.com", :customer=>123}]
  69219 ASIR::Transport::TcpSocket :send_request, :request, #<ASIR::Request:0x10\
0480d90 @selector=:send_email, @receiver=Email, @receiver_class=Module, @argumen\
ts=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}]> => ...
  69219 ASIR::Coder::Marshal :encode, #<ASIR::Request:0x100480d90 @selector=:sen\
d_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to\
=>"user@email.com", :customer=>123}]> => ...
  69220 ASIR::Transport::TcpSocket :run_socket_server!
  69219 ASIR::Coder::Marshal :encode, #<ASIR::Request:0x100480d90 @selector=:sen\
d_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to\
=>"user@email.com", :customer=>123}]> => 
    "\004\bo:\022ASIR::Request\t:\016@selector:\017send_email:\016@receiverm\nEm\
ail:\024@receiver_classc\vModule:\017@arguments[\a:\020pdf_invoice{\a:\ato\"\023\

MORE

Socket service – Output – Page 2

user@email.com:\rcustomeri\001{"
  69219 ASIR::Transport::TcpSocket connect 127.0.0.1:30901
  69219 ASIR::Transport::TcpSocket   _write size = 147
  69220 ASIR::Transport::TcpSocket run_socket_server!: connected
  69219 ASIR::Transport::TcpSocket   _write "\004\bo:\022ASIR::Request\t:\016@se\
lector:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@ar\
guments[\a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{"
  69219 ASIR::Transport::TcpSocket :receive_response => ...
  69220 ASIR::Transport::TcpSocket :receive_request, :stream, #<TCPSocket:0x1004\
80de0> => ...
  69220 ASIR::Transport::TcpSocket   _read  size = 147
  69220 ASIR::Transport::TcpSocket   _read  "\004\bo:\022ASIR::Request\t:\016@se\
lector:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@ar\
guments[\a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{"
  69220 ASIR::Coder::Marshal :decode, "\004\bo:\022ASIR::Request\t:\016@selector\

MORE

Socket service – Output – Page 3

:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@argument\
s[\a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{" => ...
  69220 ASIR::Coder::Marshal :decode, "\004\bo:\022ASIR::Request\t:\016@selector\
:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@argument\
s[\a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{" => 
    #<ASIR::Request:0x1004804d0 @selector=:send_email, @receiver=Email, @receive\
r_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>123\
}]>
  69220 ASIR::Transport::TcpSocket :receive_request, :stream, #<TCPSocket:0x1004\
80de0> => 
    #<ASIR::Request:0x1004804d0 @selector=:send_email, @receiver=Email, @receive\
r_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>123\
}]>
  69220 ASIR::Transport::TcpSocket :invoke_request!, #<ASIR::Request:0x1004804d0\
 @selector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:pd\

MORE

Socket service – Output – Page 4

f_invoice, {:to=>"user@email.com", :customer=>123}]> => ...
*** 69220: Email.send_mail :pdf_invoice {:to=>"user@email.com", :customer=>123}
  69220 ASIR::Transport::TcpSocket :invoke_request!, #<ASIR::Request:0x1004804d0\
 @selector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:pd\
f_invoice, {:to=>"user@email.com", :customer=>123}]> => 
    #<ASIR::Response:0x10047faa8 @request=#<ASIR::Request:0x1004804d0 @selector=\
:send_email, @receiver=Email, @receiver_class=Module, @result=:ok, @arguments=[:\
pdf_invoice, {:to=>"user@email.com", :customer=>123}]>, @result=:ok, @exception=\
nil>
  69220 ASIR::Transport::TcpSocket :receive_request, :response, #<ASIR::Response\
:0x10047faa8 @request=#<ASIR::Request:0x1004804d0 @selector=:send_email, @receiv\
er=Email, @receiver_class=Module, @result=:ok, @arguments=[:pdf_invoice, {:to=>"\
user@email.com", :customer=>123}]>, @result=:ok, @exception=nil>, :stream, #<TCP\
Socket:0x100480de0> => ...
  69220 ASIR::Coder::Marshal :encode, #<ASIR::Response:0x10047faa8 @request=#<AS\

MORE

Socket service – Output – Page 5

IR::Request:0x1004804d0 @selector=:send_email, @receiver=Email, @receiver_class=\
Module, @result=:ok, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer\
=>123}]>, @result=:ok, @exception=nil> => ...
  69220 ASIR::Coder::Marshal :encode, #<ASIR::Response:0x10047faa8 @request=#<AS\
IR::Request:0x1004804d0 @selector=:send_email, @receiver=Email, @receiver_class=\
Module, @result=:ok, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer\
=>123}]>, @result=:ok, @exception=nil> => 
    "\004\bo:\023ASIR::Response\b:\r@requesto:\022ASIR::Request\n:\016@selector:\
\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\f@result:\aok\
:\017@arguments[\a:\020pdf_invoice{\a:\ato\"\023user@email.com:\rcustomeri\001{;\
\f;\r:\017@exception0"
  69220 ASIR::Transport::TcpSocket   _write size = 205
  69220 ASIR::Transport::TcpSocket   _write "\004\bo:\023ASIR::Response\b:\r@req\
uesto:\022ASIR::Request\n:\016@selector:\017send_email:\016@receiverm\nEmail:\02\
4@receiver_classc\vModule:\f@result:\aok:\017@arguments[\a:\020pdf_invoice{\a:\a\

MORE

Socket service – Output – Page 6

to\"\023user@email.com:\rcustomeri\001{;\f;\r:\017@exception0"
  69219 ASIR::Transport::TcpSocket   _read  size = 205
  69220 ASIR::Transport::TcpSocket :receive_request, :response, #<ASIR::Response\
:0x10047faa8 @request=#<ASIR::Request:0x1004804d0 @selector=:send_email, @receiv\
er=Email, @receiver_class=Module, @result=:ok, @arguments=[:pdf_invoice, {:to=>"\
user@email.com", :customer=>123}]>, @result=:ok, @exception=nil>, :stream, #<TCP\
Socket:0x100480de0> => 
    #<TCPSocket:0x100480de0>
  69219 ASIR::Transport::TcpSocket   _read  "\004\bo:\023ASIR::Response\b:\r@req\
uesto:\022ASIR::Request\n:\016@selector:\017send_email:\016@receiverm\nEmail:\02\
4@receiver_classc\vModule:\f@result:\aok:\017@arguments[\a:\020pdf_invoice{\a:\a\
to\"\023user@email.com:\rcustomeri\001{;\f;\r:\017@exception0"
  69219 ASIR::Coder::Marshal :decode, "\004\bo:\023ASIR::Response\b:\r@requesto:\
\022ASIR::Request\n:\016@selector:\017send_email:\016@receiverm\nEmail:\024@rece\
iver_classc\vModule:\f@result:\aok:\017@arguments[\a:\020pdf_invoice{\a:\ato\"\0\

MORE

Socket service – Output – Page 7

23user@email.com:\rcustomeri\001{;\f;\r:\017@exception0" => ...
  69219 ASIR::Coder::Marshal :decode, "\004\bo:\023ASIR::Response\b:\r@requesto:\
\022ASIR::Request\n:\016@selector:\017send_email:\016@receiverm\nEmail:\024@rece\
iver_classc\vModule:\f@result:\aok:\017@arguments[\a:\020pdf_invoice{\a:\ato\"\0\
23user@email.com:\rcustomeri\001{;\f;\r:\017@exception0" => 
    #<ASIR::Response:0x10047f8a0 @request=#<ASIR::Request:0x10047f800 @selector=\
:send_email, @receiver=Email, @receiver_class=Module, @result=:ok, @arguments=[:\
pdf_invoice, {:to=>"user@email.com", :customer=>123}]>, @result=:ok, @exception=\
nil>
  69219 ASIR::Transport::TcpSocket :receive_response => 
    #<ASIR::Response:0x10047f8a0 @request=#<ASIR::Request:0x10047f800 @selector=\
:send_email, @receiver=Email, @receiver_class=Module, @result=:ok, @arguments=[:\
pdf_invoice, {:to=>"user@email.com", :customer=>123}]>, @result=:ok, @exception=\
nil>
  69219 ASIR::Transport::TcpSocket :send_request, :response, #<ASIR::Response:0x\

MORE

Socket service – Output – Page 8

10047f8a0 @request=#<ASIR::Request:0x10047f800 @selector=:send_email, @receiver=\
Email, @receiver_class=Module, @result=:ok, @arguments=[:pdf_invoice, {:to=>"use\
r@email.com", :customer=>123}]>, @result=:ok, @exception=nil>
  69219 ASIR::Transport::TcpSocket :send_request, :request, #<ASIR::Request:0x10\
0480d90 @selector=:send_email, @receiver=Email, @receiver_class=Module, @argumen\
ts=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}]> => 
    :ok
  69220 ASIR::Transport::TcpSocket run_socket_server!: disconnected
*** 69219: pr: :ok

					

Socket service with forwarded exception.

require 'example_helper'
begin
  Email.client.transport = t =
    ASIR::Transport::TcpSocket.new(:port => 30902)
  t.encoder = 
    ASIR::Coder::Marshal.new

  t.prepare_socket_server!
  child_pid = Process.fork do 
    t.run_socket_server!
  end
  
  pr Email.client.do_raise("Raise Me!")
rescue Exception => err
  pr [ :exception, err ]
ensure
  t.close; sleep 1
  Process.kill 9, child_pid
end

Example Exception

Email.do_raise("DOH!")


response.exception = ee = EncapsulatedException.new(...)
ee.exception_class = "::RuntimeError"
ee.exception_message = "DOH!"
ee.exception_backtrace = [ ... ]
ruby

Encapsulated Exception

Encapsulates exceptions raised in the Service.

module ASIR
  class EncapsulatedException
    attr_accessor :exception_class, :exception_message, :exception_backtrace

    def initialize exc
      @exception_class     = exc.class.name
      @exception_message   = exc.message
      @exception_backtrace = exc.backtrace
    end

    def invoke!
      raise eval("::#{@exception_class}"), @exception_message, @exception_backtrace
    end
  end
end

Socket service with forwarded exception. – Output

  69222 ASIR::Transport::TcpSocket prepare_socket_server! 30902
  69222 ASIR::Client::Proxy method_missing :do_raise ["Raise Me!"]
  69222 ASIR::Transport::TcpSocket :send_request, :request, #<ASIR::Request:0x10\
0480cc8 @receiver_class=Module, @arguments=["Raise Me!"], @selector=:do_raise, @\
receiver=Email> => ...
  69222 ASIR::Coder::Marshal :encode, #<ASIR::Request:0x100480cc8 @receiver_clas\
s=Module, @arguments=["Raise Me!"], @selector=:do_raise, @receiver=Email> => ...
  69222 ASIR::Coder::Marshal :encode, #<ASIR::Request:0x100480cc8 @receiver_clas\
s=Module, @arguments=["Raise Me!"], @selector=:do_raise, @receiver=Email> => 
    "\004\bo:\022ASIR::Request\t:\024@receiver_classc\vModule:\017@arguments[\00\
6\"\016Raise Me!:\016@selector:\rdo_raise:\016@receiverm\nEmail"
  69222 ASIR::Transport::TcpSocket connect 127.0.0.1:30902
  69223 ASIR::Transport::TcpSocket :run_socket_server!
  69222 ASIR::Transport::TcpSocket   _write size = 108
  69222 ASIR::Transport::TcpSocket   _write "\004\bo:\022ASIR::Request\t:\024@re\

MORE

Socket service with forwarded exception. – Output – Page 2

ceiver_classc\vModule:\017@arguments[\006\"\016Raise Me!:\016@selector:\rdo_rais\
e:\016@receiverm\nEmail"
  69223 ASIR::Transport::TcpSocket run_socket_server!: connected
  69222 ASIR::Transport::TcpSocket :receive_response => ...
  69223 ASIR::Transport::TcpSocket :receive_request, :stream, #<TCPSocket:0x1004\
80cf0> => ...
  69223 ASIR::Transport::TcpSocket   _read  size = 108
  69223 ASIR::Transport::TcpSocket   _read  "\004\bo:\022ASIR::Request\t:\024@re\
ceiver_classc\vModule:\017@arguments[\006\"\016Raise Me!:\016@selector:\rdo_rais\
e:\016@receiverm\nEmail"
  69223 ASIR::Coder::Marshal :decode, "\004\bo:\022ASIR::Request\t:\024@receiver\
_classc\vModule:\017@arguments[\006\"\016Raise Me!:\016@selector:\rdo_raise:\016\
@receiverm\nEmail" => ...
  69223 ASIR::Coder::Marshal :decode, "\004\bo:\022ASIR::Request\t:\024@receiver\
_classc\vModule:\017@arguments[\006\"\016Raise Me!:\016@selector:\rdo_raise:\016\

MORE

Socket service with forwarded exception. – Output – Page 3

@receiverm\nEmail" => 
    #<ASIR::Request:0x1004803e0 @receiver_class=Module, @arguments=["Raise Me!"]\
, @selector=:do_raise, @receiver=Email>
  69223 ASIR::Transport::TcpSocket :receive_request, :stream, #<TCPSocket:0x1004\
80cf0> => 
    #<ASIR::Request:0x1004803e0 @receiver_class=Module, @arguments=["Raise Me!"]\
, @selector=:do_raise, @receiver=Email>
  69223 ASIR::Transport::TcpSocket :invoke_request!, #<ASIR::Request:0x1004803e0\
 @receiver_class=Module, @arguments=["Raise Me!"], @selector=:do_raise, @receive\
r=Email> => ...
  69223 ASIR::Transport::TcpSocket :invoke_request!, #<ASIR::Request:0x1004803e0\
 @receiver_class=Module, @arguments=["Raise Me!"], @selector=:do_raise, @receive\
r=Email> => 
    #<ASIR::Response:0x10047f968 @result=nil, @exception=#<ASIR::EncapsulatedExc\
eption:0x10047fcb0 @exception_message="Raise Me!", @exception_class="RuntimeErro\

MORE

Socket service with forwarded exception. – Output – Page 4

r", @exception_backtrace=["./example/sample_service.rb:162:in `do_raise'", "./li\
b/asir.rb:258:in `__send__'", "./lib/asir.rb:258:in `invoke!'", "./lib/asir.rb:6\
22:in `invoke_request!'", "./lib/asir.rb:226:in `_log_result'", "./lib/asir.rb:6\
21:in `invoke_request!'", "./lib/asir.rb:741:in `serve_stream_request!'", "./lib\
/asir.rb:728:in `serve_stream!'", "./lib/asir.rb:895:in `run_socket_server!'", "\
.riterate/ex10.rb-1-capture.rb:23", ".riterate/ex10.rb-1-capture.rb:22:in `fork'\
", ".riterate/ex10.rb-1-capture.rb:22", ".riterate/ex10.rb-1-capture.rb:4:in `__\
capture_stream'", ".riterate/ex10.rb-1-capture.rb:12", ".riterate/ex10.rb-1-capt\
ure.rb:4:in `__capture_stream'", ".riterate/ex10.rb-1-capture.rb:11", ".riterate\
/ex10.rb-1-capture.rb:10:in `open'", ".riterate/ex10.rb-1-capture.rb:10"]>, @req\
uest=#<ASIR::Request:0x1004803e0 @receiver_class=Module, @arguments=["Raise Me!"\
], @selector=:do_raise, @receiver=Email>>
  69223 ASIR::Transport::TcpSocket :receive_request, :response, #<ASIR::Response\
:0x10047f968 @result=nil, @exception=#<ASIR::EncapsulatedException:0x10047fcb0 @\
exception_message="Raise Me!", @exception_class="RuntimeError", @exception_backt\

MORE

Socket service with forwarded exception. – Output – Page 5

race=["./example/sample_service.rb:162:in `do_raise'", "./lib/asir.rb:258:in `__\
send__'", "./lib/asir.rb:258:in `invoke!'", "./lib/asir.rb:622:in `invoke_reques\
t!'", "./lib/asir.rb:226:in `_log_result'", "./lib/asir.rb:621:in `invoke_reques\
t!'", "./lib/asir.rb:741:in `serve_stream_request!'", "./lib/asir.rb:728:in `ser\
ve_stream!'", "./lib/asir.rb:895:in `run_socket_server!'", ".riterate/ex10.rb-1-\
capture.rb:23", ".riterate/ex10.rb-1-capture.rb:22:in `fork'", ".riterate/ex10.r\
b-1-capture.rb:22", ".riterate/ex10.rb-1-capture.rb:4:in `__capture_stream'", ".\
riterate/ex10.rb-1-capture.rb:12", ".riterate/ex10.rb-1-capture.rb:4:in `__captu\
re_stream'", ".riterate/ex10.rb-1-capture.rb:11", ".riterate/ex10.rb-1-capture.r\
b:10:in `open'", ".riterate/ex10.rb-1-capture.rb:10"]>, @request=#<ASIR::Request\
:0x1004803e0 @receiver_class=Module, @arguments=["Raise Me!"], @selector=:do_rai\
se, @receiver=Email>>, :stream, #<TCPSocket:0x100480cf0> => ...
  69223 ASIR::Coder::Marshal :encode, #<ASIR::Response:0x10047f968 @result=nil, \
@exception=#<ASIR::EncapsulatedException:0x10047fcb0 @exception_message="Raise M\
e!", @exception_class="RuntimeError", @exception_backtrace=["./example/sample_se\

MORE

Socket service with forwarded exception. – Output – Page 6

rvice.rb:162:in `do_raise'", "./lib/asir.rb:258:in `__send__'", "./lib/asir.rb:2\
58:in `invoke!'", "./lib/asir.rb:622:in `invoke_request!'", "./lib/asir.rb:226:i\
n `_log_result'", "./lib/asir.rb:621:in `invoke_request!'", "./lib/asir.rb:741:i\
n `serve_stream_request!'", "./lib/asir.rb:728:in `serve_stream!'", "./lib/asir.\
rb:895:in `run_socket_server!'", ".riterate/ex10.rb-1-capture.rb:23", ".riterate\
/ex10.rb-1-capture.rb:22:in `fork'", ".riterate/ex10.rb-1-capture.rb:22", ".rite\
rate/ex10.rb-1-capture.rb:4:in `__capture_stream'", ".riterate/ex10.rb-1-capture\
.rb:12", ".riterate/ex10.rb-1-capture.rb:4:in `__capture_stream'", ".riterate/ex\
10.rb-1-capture.rb:11", ".riterate/ex10.rb-1-capture.rb:10:in `open'", ".riterat\
e/ex10.rb-1-capture.rb:10"]>, @request=#<ASIR::Request:0x1004803e0 @receiver_cla\
ss=Module, @arguments=["Raise Me!"], @selector=:do_raise, @receiver=Email>> => .\
..
  69223 ASIR::Coder::Marshal :encode, #<ASIR::Response:0x10047f968 @result=nil, \
@exception=#<ASIR::EncapsulatedException:0x10047fcb0 @exception_message="Raise M\
e!", @exception_class="RuntimeError", @exception_backtrace=["./example/sample_se\

MORE

Socket service with forwarded exception. – Output – Page 7

rvice.rb:162:in `do_raise'", "./lib/asir.rb:258:in `__send__'", "./lib/asir.rb:2\
58:in `invoke!'", "./lib/asir.rb:622:in `invoke_request!'", "./lib/asir.rb:226:i\
n `_log_result'", "./lib/asir.rb:621:in `invoke_request!'", "./lib/asir.rb:741:i\
n `serve_stream_request!'", "./lib/asir.rb:728:in `serve_stream!'", "./lib/asir.\
rb:895:in `run_socket_server!'", ".riterate/ex10.rb-1-capture.rb:23", ".riterate\
/ex10.rb-1-capture.rb:22:in `fork'", ".riterate/ex10.rb-1-capture.rb:22", ".rite\
rate/ex10.rb-1-capture.rb:4:in `__capture_stream'", ".riterate/ex10.rb-1-capture\
.rb:12", ".riterate/ex10.rb-1-capture.rb:4:in `__capture_stream'", ".riterate/ex\
10.rb-1-capture.rb:11", ".riterate/ex10.rb-1-capture.rb:10:in `open'", ".riterat\
e/ex10.rb-1-capture.rb:10"]>, @request=#<ASIR::Request:0x1004803e0 @receiver_cla\
ss=Module, @arguments=["Raise Me!"], @selector=:do_raise, @receiver=Email>> => 
    "\004\bo:\023ASIR::Response\b:\f@result0:\017@exceptiono: ASIR::Encapsulated\
Exception\b:\027@exception_message\"\016Raise Me!:\025@exception_class\"\021Runt\
imeError:\031@exception_backtrace[\027\"2./example/sample_service.rb:162:in `do_\
raise'\"$./lib/asir.rb:258:in `__send__'\"#./lib/asir.rb:258:in `invoke!'\"+./li\

MORE

Socket service with forwarded exception. – Output – Page 8

b/asir.rb:622:in `invoke_request!'\"'./lib/asir.rb:226:in `_log_result'\"+./lib/\
asir.rb:621:in `invoke_request!'\"1./lib/asir.rb:741:in `serve_stream_request!'\\
")./lib/asir.rb:728:in `serve_stream!'\"../lib/asir.rb:895:in `run_socket_server\
!'\"&.riterate/ex10.rb-1-capture.rb:23\"0.riterate/ex10.rb-1-capture.rb:22:in `f\
ork'\"&.riterate/ex10.rb-1-capture.rb:22\";.riterate/ex10.rb-1-capture.rb:4:in `\
__capture_stream'\"&.riterate/ex10.rb-1-capture.rb:12\";.riterate/ex10.rb-1-capt\
ure.rb:4:in `__capture_stream'\"&.riterate/ex10.rb-1-capture.rb:11\"0.riterate/e\
x10.rb-1-capture.rb:10:in `open'\"&.riterate/ex10.rb-1-capture.rb:10:\r@requesto\
:\022ASIR::Request\t:\024@receiver_classc\vModule:\017@arguments[\006@\a:\016@se\
lector:\rdo_raise:\016@receiverm\nEmail"
  69223 ASIR::Transport::TcpSocket   _write size = 999
  69223 ASIR::Transport::TcpSocket   _write "\004\bo:\023ASIR::Response\b:\f@res\
ult0:\017@exceptiono: ASIR::EncapsulatedException\b:\027@exception_message\"\016\
Raise Me!:\025@exception_class\"\021RuntimeError:\031@exception_backtrace[\027\"\
2./example/sample_service.rb:162:in `do_raise'\"$./lib/asir.rb:258:in `__send__'\

MORE

Socket service with forwarded exception. – Output – Page 9

\"#./lib/asir.rb:258:in `invoke!'\"+./lib/asir.rb:622:in `invoke_request!'\"'./l\
ib/asir.rb:226:in `_log_result'\"+./lib/asir.rb:621:in `invoke_request!'\"1./lib\
/asir.rb:741:in `serve_stream_request!'\")./lib/asir.rb:728:in `serve_stream!'\"\
../lib/asir.rb:895:in `run_socket_server!'\"&.riterate/ex10.rb-1-capture.rb:23\"\
0.riterate/ex10.rb-1-capture.rb:22:in `fork'\"&.riterate/ex10.rb-1-capture.rb:22\
\";.riterate/ex10.rb-1-capture.rb:4:in `__capture_stream'\"&.riterate/ex10.rb-1-\
capture.rb:12\";.riterate/ex10.rb-1-capture.rb:4:in `__capture_stream'\"&.ritera\
te/ex10.rb-1-capture.rb:11\"0.riterate/ex10.rb-1-capture.rb:10:in `open'\"&.rite\
rate/ex10.rb-1-capture.rb:10:\r@requesto:\022ASIR::Request\t:\024@receiver_class\
c\vModule:\017@arguments[\006@\a:\016@selector:\rdo_raise:\016@receiverm\nEmail"\
  69222 ASIR::Transport::TcpSocket   _read  size = 999
					

Socket service with forwarded exception. – Output – Page 10

send__'", "./lib/asir.rb:258:in `invoke!'", "./lib/asir.rb:622:in `invoke_reques\
t!'", "./lib/asir.rb:226:in `_log_result'", "./lib/asir.rb:621:in `invoke_reques\
t!'", "./lib/asir.rb:741:in `serve_stream_request!'", "./lib/asir.rb:728:in `ser\
ve_stream!'", "./lib/asir.rb:895:in `run_socket_server!'", ".riterate/ex10.rb-1-\
capture.rb:23", ".riterate/ex10.rb-1-capture.rb:22:in `fork'", ".riterate/ex10.r\
b-1-capture.rb:22", ".riterate/ex10.rb-1-capture.rb:4:in `__capture_stream'", ".\
riterate/ex10.rb-1-capture.rb:12", ".riterate/ex10.rb-1-capture.rb:4:in `__captu\
re_stream'", ".riterate/ex10.rb-1-capture.rb:11", ".riterate/ex10.rb-1-capture.r\
b:10:in `open'", ".riterate/ex10.rb-1-capture.rb:10"]>, @request=#<ASIR::Request\
:0x1004803e0 @receiver_class=Module, @arguments=["Raise Me!"], @selector=:do_rai\
se, @receiver=Email>>, :stream, #<TCPSocket:0x100480cf0> => 
    #<TCPSocket:0x100480cf0>
  69222 ASIR::Transport::TcpSocket   _read  "\004\bo:\023ASIR::Response\b:\f@res\
ult0:\017@exceptiono: ASIR::EncapsulatedException\b:\027@exception_message\"\016\
Raise Me!:\025@exception_class\"\021RuntimeError:\031@exception_backtrace[\027\"\

MORE

Socket service with forwarded exception. – Output – Page 11

2./example/sample_service.rb:162:in `do_raise'\"$./lib/asir.rb:258:in `__send__'\
\"#./lib/asir.rb:258:in `invoke!'\"+./lib/asir.rb:622:in `invoke_request!'\"'./l\
ib/asir.rb:226:in `_log_result'\"+./lib/asir.rb:621:in `invoke_request!'\"1./lib\
/asir.rb:741:in `serve_stream_request!'\")./lib/asir.rb:728:in `serve_stream!'\"\
../lib/asir.rb:895:in `run_socket_server!'\"&.riterate/ex10.rb-1-capture.rb:23\"\
0.riterate/ex10.rb-1-capture.rb:22:in `fork'\"&.riterate/ex10.rb-1-capture.rb:22\
\";.riterate/ex10.rb-1-capture.rb:4:in `__capture_stream'\"&.riterate/ex10.rb-1-\
capture.rb:12\";.riterate/ex10.rb-1-capture.rb:4:in `__capture_stream'\"&.ritera\
te/ex10.rb-1-capture.rb:11\"0.riterate/ex10.rb-1-capture.rb:10:in `open'\"&.rite\
rate/ex10.rb-1-capture.rb:10:\r@requesto:\022ASIR::Request\t:\024@receiver_class\
c\vModule:\017@arguments[\006@\a:\016@selector:\rdo_raise:\016@receiverm\nEmail"
  69222 ASIR::Coder::Marshal :decode, "\004\bo:\023ASIR::Response\b:\f@result0:\\
017@exceptiono: ASIR::EncapsulatedException\b:\027@exception_message\"\016Raise \
Me!:\025@exception_class\"\021RuntimeError:\031@exception_backtrace[\027\"2./exa\
mple/sample_service.rb:162:in `do_raise'\"$./lib/asir.rb:258:in `__send__'\"#./l\

MORE

Socket service with forwarded exception. – Output – Page 12

ib/asir.rb:258:in `invoke!'\"+./lib/asir.rb:622:in `invoke_request!'\"'./lib/asi\
r.rb:226:in `_log_result'\"+./lib/asir.rb:621:in `invoke_request!'\"1./lib/asir.\
rb:741:in `serve_stream_request!'\")./lib/asir.rb:728:in `serve_stream!'\"../lib\
/asir.rb:895:in `run_socket_server!'\"&.riterate/ex10.rb-1-capture.rb:23\"0.rite\
rate/ex10.rb-1-capture.rb:22:in `fork'\"&.riterate/ex10.rb-1-capture.rb:22\";.ri\
terate/ex10.rb-1-capture.rb:4:in `__capture_stream'\"&.riterate/ex10.rb-1-captur\
e.rb:12\";.riterate/ex10.rb-1-capture.rb:4:in `__capture_stream'\"&.riterate/ex1\
0.rb-1-capture.rb:11\"0.riterate/ex10.rb-1-capture.rb:10:in `open'\"&.riterate/e\
x10.rb-1-capture.rb:10:\r@requesto:\022ASIR::Request\t:\024@receiver_classc\vMod\
ule:\017@arguments[\006@\a:\016@selector:\rdo_raise:\016@receiverm\nEmail" => ..\
.
  69222 ASIR::Coder::Marshal :decode, "\004\bo:\023ASIR::Response\b:\f@result0:\\
017@exceptiono: ASIR::EncapsulatedException\b:\027@exception_message\"\016Raise \
Me!:\025@exception_class\"\021RuntimeError:\031@exception_backtrace[\027\"2./exa\
mple/sample_service.rb:162:in `do_raise'\"$./lib/asir.rb:258:in `__send__'\"#./l\

MORE

Socket service with forwarded exception. – Output – Page 13

ib/asir.rb:258:in `invoke!'\"+./lib/asir.rb:622:in `invoke_request!'\"'./lib/asi\
r.rb:226:in `_log_result'\"+./lib/asir.rb:621:in `invoke_request!'\"1./lib/asir.\
rb:741:in `serve_stream_request!'\")./lib/asir.rb:728:in `serve_stream!'\"../lib\
/asir.rb:895:in `run_socket_server!'\"&.riterate/ex10.rb-1-capture.rb:23\"0.rite\
rate/ex10.rb-1-capture.rb:22:in `fork'\"&.riterate/ex10.rb-1-capture.rb:22\";.ri\
terate/ex10.rb-1-capture.rb:4:in `__capture_stream'\"&.riterate/ex10.rb-1-captur\
e.rb:12\";.riterate/ex10.rb-1-capture.rb:4:in `__capture_stream'\"&.riterate/ex1\
0.rb-1-capture.rb:11\"0.riterate/ex10.rb-1-capture.rb:10:in `open'\"&.riterate/e\
x10.rb-1-capture.rb:10:\r@requesto:\022ASIR::Request\t:\024@receiver_classc\vMod\
ule:\017@arguments[\006@\a:\016@selector:\rdo_raise:\016@receiverm\nEmail" => 
    #<ASIR::Response:0x10047fa30 @result=nil, @exception=#<ASIR::EncapsulatedExc\
eption:0x10047f968 @exception_message="Raise Me!", @exception_class="RuntimeErro\
r", @exception_backtrace=["./example/sample_service.rb:162:in `do_raise'", "./li\
b/asir.rb:258:in `__send__'", "./lib/asir.rb:258:in `invoke!'", "./lib/asir.rb:6\
22:in `invoke_request!'", "./lib/asir.rb:226:in `_log_result'", "./lib/asir.rb:6\

MORE

Socket service with forwarded exception. – Output – Page 14

21:in `invoke_request!'", "./lib/asir.rb:741:in `serve_stream_request!'", "./lib\
/asir.rb:728:in `serve_stream!'", "./lib/asir.rb:895:in `run_socket_server!'", "\
.riterate/ex10.rb-1-capture.rb:23", ".riterate/ex10.rb-1-capture.rb:22:in `fork'\
", ".riterate/ex10.rb-1-capture.rb:22", ".riterate/ex10.rb-1-capture.rb:4:in `__\
capture_stream'", ".riterate/ex10.rb-1-capture.rb:12", ".riterate/ex10.rb-1-capt\
ure.rb:4:in `__capture_stream'", ".riterate/ex10.rb-1-capture.rb:11", ".riterate\
/ex10.rb-1-capture.rb:10:in `open'", ".riterate/ex10.rb-1-capture.rb:10"]>, @req\
uest=#<ASIR::Request:0x10047f508 @receiver_class=Module, @arguments=["Raise Me!"\
], @selector=:do_raise, @receiver=Email>>
  69222 ASIR::Transport::TcpSocket :receive_response => 
    #<ASIR::Response:0x10047fa30 @result=nil, @exception=#<ASIR::EncapsulatedExc\
eption:0x10047f968 @exception_message="Raise Me!", @exception_class="RuntimeErro\
r", @exception_backtrace=["./example/sample_service.rb:162:in `do_raise'", "./li\
b/asir.rb:258:in `__send__'", "./lib/asir.rb:258:in `invoke!'", "./lib/asir.rb:6\
22:in `invoke_request!'", "./lib/asir.rb:226:in `_log_result'", "./lib/asir.rb:6\

MORE

Socket service with forwarded exception. – Output – Page 15

21:in `invoke_request!'", "./lib/asir.rb:741:in `serve_stream_request!'", "./lib\
/asir.rb:728:in `serve_stream!'", "./lib/asir.rb:895:in `run_socket_server!'", "\
.riterate/ex10.rb-1-capture.rb:23", ".riterate/ex10.rb-1-capture.rb:22:in `fork'\
", ".riterate/ex10.rb-1-capture.rb:22", ".riterate/ex10.rb-1-capture.rb:4:in `__\
capture_stream'", ".riterate/ex10.rb-1-capture.rb:12", ".riterate/ex10.rb-1-capt\
ure.rb:4:in `__capture_stream'", ".riterate/ex10.rb-1-capture.rb:11", ".riterate\
/ex10.rb-1-capture.rb:10:in `open'", ".riterate/ex10.rb-1-capture.rb:10"]>, @req\
uest=#<ASIR::Request:0x10047f508 @receiver_class=Module, @arguments=["Raise Me!"\
], @selector=:do_raise, @receiver=Email>>
  69222 ASIR::Transport::TcpSocket :send_request, :response, #<ASIR::Response:0x\
10047fa30 @result=nil, @exception=#<ASIR::EncapsulatedException:0x10047f968 @exc\
eption_message="Raise Me!", @exception_class="RuntimeError", @exception_backtrac\
e=["./example/sample_service.rb:162:in `do_raise'", "./lib/asir.rb:258:in `__sen\
d__'", "./lib/asir.rb:258:in `invoke!'", "./lib/asir.rb:622:in `invoke_request!'\
", "./lib/asir.rb:226:in `_log_result'", "./lib/asir.rb:621:in `invoke_request!'\

MORE

Socket service with forwarded exception. – Output – Page 16

", "./lib/asir.rb:741:in `serve_stream_request!'", "./lib/asir.rb:728:in `serve_\
stream!'", "./lib/asir.rb:895:in `run_socket_server!'", ".riterate/ex10.rb-1-cap\
ture.rb:23", ".riterate/ex10.rb-1-capture.rb:22:in `fork'", ".riterate/ex10.rb-1\
-capture.rb:22", ".riterate/ex10.rb-1-capture.rb:4:in `__capture_stream'", ".rit\
erate/ex10.rb-1-capture.rb:12", ".riterate/ex10.rb-1-capture.rb:4:in `__capture_\
stream'", ".riterate/ex10.rb-1-capture.rb:11", ".riterate/ex10.rb-1-capture.rb:1\
0:in `open'", ".riterate/ex10.rb-1-capture.rb:10"]>, @request=#<ASIR::Request:0x\
10047f508 @receiver_class=Module, @arguments=["Raise Me!"], @selector=:do_raise,\
 @receiver=Email>>
  69223 ASIR::Transport::TcpSocket run_socket_server!: disconnected
*** 69222: pr: [:exception, #<RuntimeError: Raise Me!>]

					

Socket service with local fallback.

require 'example_helper'
begin
  File.unlink(service_log = "service.log") rescue nil

  Email.client.transport = t =
    ASIR::Transport::Fallback.new(:transports => [
      tcp = ASIR::Transport::TcpSocket.new(:port => 30903,
                                           :encoder => ASIR::Coder::Marshal.new),
      ASIR::Transport::Broadcast.new(:transports => [ 
        file = ASIR::Transport::File.new(:file => service_log,
                                         :encoder => ASIR::Coder::Yaml.new),
        ASIR::Transport::Subprocess.new,
      ]),
    ])

  pr Email.client.send_email(:pdf_invoice, 
                             :to => "user@email.com", :customer => @customer)

  tcp.prepare_socket_server!
  child_pid = Process.fork do 
    tcp.run_socket_server!
  end

  pr Email.client.send_email(:pdf_invoice, 
                             :to => "user2@email.com", :customer => @customer)
  
ensure
  file.close;
  tcp.close; sleep 1
  Process.kill 9, child_pid
  puts "\x1a\n#{service_log.inspect} contents:"
  puts File.read(service_log)
end
# Sample client support
#

require 'pp'

$_log_verbose = true
@customer = 123
def pr result
  puts "*** #{$$}: pr: #{PP.pp(result, '')}"
end

require 'asir'
require 'sample_service'
end

Fallback Transport

module ASIR
  class Transport
    class Fallback < self
      attr_accessor :transports

      def send_request request
        result = sent = exceptions = nil
        transports.each do | transport |
          begin
            _log { [ :send_request, :transport, transport ] }
            result = transport.send_request request
            sent = true
            break
          rescue ::Exception => exc
            (exceptions ||= [ ]) << [ transport, exc ]
            _log { [ :send_request, :transport_failed, transport, exc ] }
          end
        end
        unless sent
          _log { [ :send_request, :fallback_failed, exceptions ] }
          raise FallbackError, "fallback failed"
        end
        result
      end
      class FallbackError < Error; end
    end
  end
end

Socket service with local fallback. – Output

  69226 ASIR::Client::Proxy method_missing :send_email [:pdf_invoice, {:to=>"use\
r@email.com", :customer=>123}]
  69226 ASIR::Transport::Fallback :send_request, :transport, #<ASIR::Transport::\
TcpSocket:0x10047f2b0 @port=30903, @encoder=#<ASIR::Coder::Marshal:0x10047f260>>
  69226 ASIR::Transport::TcpSocket :send_request, :request, #<ASIR::Request:0x10\
047ecc0 @selector=:send_email, @receiver=Email, @receiver_class=Module, @argumen\
ts=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}]> => ...
  69226 ASIR::Coder::Marshal :encode, #<ASIR::Request:0x10047ecc0 @selector=:sen\
d_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to\
=>"user@email.com", :customer=>123}]> => ...
  69226 ASIR::Coder::Marshal :encode, #<ASIR::Request:0x10047ecc0 @selector=:sen\
d_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to\
=>"user@email.com", :customer=>123}]> => 
    "\004\bo:\022ASIR::Request\t:\016@selector:\017send_email:\016@receiverm\nEm\
ail:\024@receiver_classc\vModule:\017@arguments[\a:\020pdf_invoice{\a:\ato\"\023\

MORE

Socket service with local fallback. – Output – Page 2

user@email.com:\rcustomeri\001{"
  69226 ASIR::Transport::TcpSocket connect 127.0.0.1:30903
  69226 ASIR::Transport::Fallback :send_request, :transport_failed, #<ASIR::Tran\
sport::TcpSocket:0x10047f2b0 @port=30903, @encoder=#<ASIR::Coder::Marshal:0x1004\
7f260>>, #<ASIR::Error: Cannot connect to 127.0.0.1:30903: #<Errno::ECONNREFUSED\
: Connection refused - connect(2)>>
    ./lib/asir.rb:847:in `initialize'
    ./lib/asir.rb:847:in `open'
    ./lib/asir.rb:847:in `stream'
    ./lib/asir.rb:857:in `_send_request'
    ./lib/asir.rb:544:in `send_request'
    ./lib/asir.rb:226:in `_log_result'
    ./lib/asir.rb:542:in `send_request'
    ./lib/asir.rb:931:in `send_request'
    ./lib/asir.rb:928:in `each'

MORE

Socket service with local fallback. – Output – Page 3

    ./lib/asir.rb:928:in `send_request'
    ./lib/asir.rb:1016:in `method_missing'
    .riterate/ex11.rb-1-capture.rb:29
    .riterate/ex11.rb-1-capture.rb:4:in `__capture_stream'
    .riterate/ex11.rb-1-capture.rb:12
    .riterate/ex11.rb-1-capture.rb:4:in `__capture_stream'
    .riterate/ex11.rb-1-capture.rb:11
    .riterate/ex11.rb-1-capture.rb:10:in `open'
    .riterate/ex11.rb-1-capture.rb:10
  69226 ASIR::Transport::Fallback :send_request, :transport, #<ASIR::Transport::\
Broadcast:0x10047eef0 @transports=[#<ASIR::Transport::File:0x10047f030 @encoder=\
#<ASIR::Coder::Yaml:0x10047f058>, @file="service.log">, #<ASIR::Transport::Subpr\
ocess:0x10047efb8>]>
  69226 ASIR::Transport::Broadcast :send_request, :request, #<ASIR::Request:0x10\
047ecc0 @selector=:send_email, @receiver=Email, @receiver_class=Module, @argumen\

MORE

Socket service with local fallback. – Output – Page 4

ts=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}]> => ...
  69226 ASIR::Coder::Identity :encode, #<ASIR::Request:0x10047ecc0 @selector=:se\
nd_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:t\
o=>"user@email.com", :customer=>123}]> => ...
  69226 ASIR::Coder::Identity :encode, #<ASIR::Request:0x10047ecc0 @selector=:se\
nd_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:t\
o=>"user@email.com", :customer=>123}]> => 
    #<ASIR::Request:0x10047ecc0 @selector=:send_email, @receiver=Email, @receive\
r_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>123\
}]>
  69226 ASIR::Transport::Broadcast :send_request, :transport, #<ASIR::Transport:\
:File:0x10047f030 @encoder=#<ASIR::Coder::Yaml:0x10047f058>, @file="service.log"\
>
  69226 ASIR::Transport::File :send_request, :request, #<ASIR::Request:0x10047ec\
c0 @selector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:\

MORE

Socket service with local fallback. – Output – Page 5

pdf_invoice, {:to=>"user@email.com", :customer=>123}]> => ...
  69226 ASIR::Coder::Yaml :encode, #<ASIR::Request:0x10047ecc0 @selector=:send_e\
mail, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"\
user@email.com", :customer=>123}]> => ...
  69226 ASIR::Coder::Yaml :encode, #<ASIR::Request:0x10047ecc0 @selector=:send_e\
mail, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to=>"\
user@email.com", :customer=>123}]> => 
    "--- !ruby/object:ASIR::Request \narguments: \n- :pdf_invoice\n- :to: user@e\
mail.com\n  :customer: 123\nreceiver: Email\nreceiver_class: Module\nselector: :\
send_email\n"
  69226 ASIR::Transport::File   _write size = 159
  69226 ASIR::Transport::File   _write "--- !ruby/object:ASIR::Request \nargumen\
ts: \n- :pdf_invoice\n- :to: user@email.com\n  :customer: 123\nreceiver: Email\n\
receiver_class: Module\nselector: :send_email\n"
  69226 ASIR::Transport::File :receive_response => ...

MORE

Socket service with local fallback. – Output – Page 6

  69226 ASIR::Coder::Yaml :decode, nil => ...
  69226 ASIR::Coder::Yaml :decode, nil => 
    nil
  69226 ASIR::Transport::File :receive_response => 
    nil
  69226 ASIR::Transport::File :send_request, :response, nil
  69226 ASIR::Transport::File :send_request, :request, #<ASIR::Request:0x10047ec\
c0 @selector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:\
pdf_invoice, {:to=>"user@email.com", :customer=>123}]> => 
    nil
  69226 ASIR::Transport::Broadcast :send_request, :transport, #<ASIR::Transport:\
:Subprocess:0x10047efb8>
  69226 ASIR::Transport::Subprocess :send_request, :request, #<ASIR::Request:0x1\
0047ecc0 @selector=:send_email, @receiver=Email, @receiver_class=Module, @argume\
nts=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}]> => ...

MORE

Socket service with local fallback. – Output – Page 7

  69226 ASIR::Coder::Identity :encode, #<ASIR::Request:0x10047ecc0 @selector=:se\
nd_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:t\
o=>"user@email.com", :customer=>123}]> => ...
  69226 ASIR::Coder::Identity :encode, #<ASIR::Request:0x10047ecc0 @selector=:se\
nd_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:t\
o=>"user@email.com", :customer=>123}]> => 
    #<ASIR::Request:0x10047ecc0 @selector=:send_email, @receiver=Email, @receive\
r_class=Module, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>123\
}]>
  69226 ASIR::Transport::Subprocess :receive_response => ...
  69226 ASIR::Coder::Identity :decode, nil => ...
  69226 ASIR::Coder::Identity :decode, nil => 
    nil
  69226 ASIR::Transport::Subprocess :receive_response => 
    nil

MORE

Socket service with local fallback. – Output – Page 8

  69226 ASIR::Transport::Subprocess :send_request, :response, nil
  69226 ASIR::Transport::Subprocess :send_request, :request, #<ASIR::Request:0x1\
0047ecc0 @selector=:send_email, @receiver=Email, @receiver_class=Module, @argume\
nts=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}]> => 
    nil
  69226 ASIR::Transport::Broadcast :receive_response => ...
  69226 ASIR::Coder::Identity :decode, nil => ...
  69226 ASIR::Coder::Identity :decode, nil => 
    nil
  69226 ASIR::Transport::Broadcast :receive_response => 
    nil
  69226 ASIR::Transport::Broadcast :send_request, :response, nil
  69226 ASIR::Transport::Broadcast :send_request, :request, #<ASIR::Request:0x10\
047ecc0 @selector=:send_email, @receiver=Email, @receiver_class=Module, @argumen\
ts=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}]> => 

MORE

Socket service with local fallback. – Output – Page 9

    nil
  69228 ASIR::Transport::Subprocess :invoke_request!, #<ASIR::Request:0x10047ecc\
0 @selector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:p\
df_invoice, {:to=>"user@email.com", :customer=>123}]> => ...
*** 69228: Email.send_mail :pdf_invoice {:to=>"user@email.com", :customer=>123}
  69226 ASIR::Transport::TcpSocket prepare_socket_server! 30903
  69228 ASIR::Transport::Subprocess :invoke_request!, #<ASIR::Request:0x10047ecc\
0 @selector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:p\
df_invoice, {:to=>"user@email.com", :customer=>123}]> => 
    #<ASIR::Response:0x100478320 @exception=nil, @request=#<ASIR::Request:0x1004\
7ecc0 @selector=:send_email, @receiver=Email, @receiver_class=Module, @result=:o\
k, @arguments=[:pdf_invoice, {:to=>"user@email.com", :customer=>123}]>, @result=\
:ok>
*** 69226: pr: nil
  69226 ASIR::Client::Proxy method_missing :send_email [:pdf_invoice, {:to=>"use\

MORE

Socket service with local fallback. – Output – Page 10

r2@email.com", :customer=>123}]
  69226 ASIR::Transport::Fallback :send_request, :transport, #<ASIR::Transport::\
TcpSocket:0x10047f2b0 @port=30903, @encoder=#<ASIR::Coder::Marshal:0x10047f260>,\
 @server=#<TCPServer:0x1004776a0>>
  69226 ASIR::Transport::TcpSocket :send_request, :request, #<ASIR::Request:0x10\
0477330 @selector=:send_email, @receiver=Email, @receiver_class=Module, @argumen\
ts=[:pdf_invoice, {:to=>"user2@email.com", :customer=>123}]> => ...
  69226 ASIR::Coder::Marshal :encode, #<ASIR::Request:0x100477330 @selector=:sen\
d_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to\
=>"user2@email.com", :customer=>123}]> => ...
  69226 ASIR::Coder::Marshal :encode, #<ASIR::Request:0x100477330 @selector=:sen\
d_email, @receiver=Email, @receiver_class=Module, @arguments=[:pdf_invoice, {:to\
=>"user2@email.com", :customer=>123}]> => 
    "\004\bo:\022ASIR::Request\t:\016@selector:\017send_email:\016@receiverm\nEm\
ail:\024@receiver_classc\vModule:\017@arguments[\a:\020pdf_invoice{\a:\ato\"\024\

MORE

Socket service with local fallback. – Output – Page 11

user2@email.com:\rcustomeri\001{"
  69226 ASIR::Transport::TcpSocket connect 127.0.0.1:30903
  69229 ASIR::Transport::TcpSocket :run_socket_server!
  69229 ASIR::Transport::TcpSocket run_socket_server!: connected
  69226 ASIR::Transport::TcpSocket   _write size = 148
  69226 ASIR::Transport::TcpSocket   _write "\004\bo:\022ASIR::Request\t:\016@se\
lector:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@ar\
guments[\a:\020pdf_invoice{\a:\ato\"\024user2@email.com:\rcustomeri\001{"
  69226 ASIR::Transport::TcpSocket :receive_response => ...
  69229 ASIR::Transport::TcpSocket :receive_request, :stream, #<TCPSocket:0x1004\
77380> => ...
  69229 ASIR::Transport::TcpSocket   _read  size = 148
  69229 ASIR::Transport::TcpSocket   _read  "\004\bo:\022ASIR::Request\t:\016@se\
lector:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@ar\
guments[\a:\020pdf_invoice{\a:\ato\"\024user2@email.com:\rcustomeri\001{"

MORE

Socket service with local fallback. – Output – Page 12

  69229 ASIR::Coder::Marshal :decode, "\004\bo:\022ASIR::Request\t:\016@selector\
:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@argument\
s[\a:\020pdf_invoice{\a:\ato\"\024user2@email.com:\rcustomeri\001{" => ...
  69229 ASIR::Coder::Marshal :decode, "\004\bo:\022ASIR::Request\t:\016@selector\
:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModule:\017@argument\
s[\a:\020pdf_invoice{\a:\ato\"\024user2@email.com:\rcustomeri\001{" => 
    #<ASIR::Request:0x100476a70 @selector=:send_email, @receiver=Email, @receive\
r_class=Module, @arguments=[:pdf_invoice, {:to=>"user2@email.com", :customer=>12\
3}]>
  69229 ASIR::Transport::TcpSocket :receive_request, :stream, #<TCPSocket:0x1004\
77380> => 
    #<ASIR::Request:0x100476a70 @selector=:send_email, @receiver=Email, @receive\
r_class=Module, @arguments=[:pdf_invoice, {:to=>"user2@email.com", :customer=>12\
3}]>
  69229 ASIR::Transport::TcpSocket :invoke_request!, #<ASIR::Request:0x100476a70\

MORE

Socket service with local fallback. – Output – Page 13

 @selector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:pd\
f_invoice, {:to=>"user2@email.com", :customer=>123}]> => ...
*** 69229: Email.send_mail :pdf_invoice {:to=>"user2@email.com", :customer=>123}
  69229 ASIR::Transport::TcpSocket :invoke_request!, #<ASIR::Request:0x100476a70\
 @selector=:send_email, @receiver=Email, @receiver_class=Module, @arguments=[:pd\
f_invoice, {:to=>"user2@email.com", :customer=>123}]> => 
    #<ASIR::Response:0x100476098 @exception=nil, @request=#<ASIR::Request:0x1004\
76a70 @selector=:send_email, @receiver=Email, @receiver_class=Module, @result=:o\
k, @arguments=[:pdf_invoice, {:to=>"user2@email.com", :customer=>123}]>, @result\
=:ok>
  69229 ASIR::Transport::TcpSocket :receive_request, :response, #<ASIR::Response\
:0x100476098 @exception=nil, @request=#<ASIR::Request:0x100476a70 @selector=:sen\
d_email, @receiver=Email, @receiver_class=Module, @result=:ok, @arguments=[:pdf_\
invoice, {:to=>"user2@email.com", :customer=>123}]>, @result=:ok>, :stream, #<TC\
PSocket:0x100477380> => ...

MORE

Socket service with local fallback. – Output – Page 14

  69229 ASIR::Coder::Marshal :encode, #<ASIR::Response:0x100476098 @exception=ni\
l, @request=#<ASIR::Request:0x100476a70 @selector=:send_email, @receiver=Email, \
@receiver_class=Module, @result=:ok, @arguments=[:pdf_invoice, {:to=>"user2@emai\
l.com", :customer=>123}]>, @result=:ok> => ...
  69229 ASIR::Coder::Marshal :encode, #<ASIR::Response:0x100476098 @exception=ni\
l, @request=#<ASIR::Request:0x100476a70 @selector=:send_email, @receiver=Email, \
@receiver_class=Module, @result=:ok, @arguments=[:pdf_invoice, {:to=>"user2@emai\
l.com", :customer=>123}]>, @result=:ok> => 
    "\004\bo:\023ASIR::Response\b:\017@exception0:\r@requesto:\022ASIR::Request\\
n:\016@selector:\017send_email:\016@receiverm\nEmail:\024@receiver_classc\vModul\
e:\f@result:\aok:\017@arguments[\a:\020pdf_invoice{\a:\ato\"\024user2@email.com:\
\rcustomeri\001{;\r;\016"
  69229 ASIR::Transport::TcpSocket   _write size = 206
  69229 ASIR::Transport::TcpSocket   _write "\004\bo:\023ASIR::Response\b:\017@e\
xception0:\r@requesto:\022ASIR::Request\n:\016@selector:\017send_email:\016@rece\

MORE

Socket service with local fallback. – Output – Page 15

iverm\nEmail:\024@receiver_classc\vModule:\f@result:\aok:\017@arguments[\a:\020p\
df_invoice{\a:\ato\"\024user2@email.com:\rcustomeri\001{;\r;\016"
  69229 ASIR::Transport::TcpSocket :receive_request, :response, #<ASIR::Response\
:0x100476098 @exception=nil, @request=#<ASIR::Request:0x100476a70 @selector=:sen\
d_email, @receiver=Email, @receiver_class=Module, @result=:ok, @arguments=[:pdf_\
invoice, {:to=>"user2@email.com", :customer=>123}]>, @result=:ok>, :stream, #<TC\
PSocket:0x100477380> => 
    #<TCPSocket:0x100477380>
  69226 ASIR::Transport::TcpSocket   _read  size = 206
  69226 ASIR::Transport::TcpSocket   _read  "\004\bo:\023ASIR::Response\b:\017@e\
xception0:\r@requesto:\022ASIR::Request\n:\016@selector:\017send_email:\016@rece\
iverm\nEmail:\024@receiver_classc\vModule:\f@result:\aok:\017@arguments[\a:\020p\
df_invoice{\a:\ato\"\024user2@email.com:\rcustomeri\001{;\r;\016"
  69226 ASIR::Coder::Marshal :decode, "\004\bo:\023ASIR::Response\b:\017@excepti\
on0:\r@requesto:\022ASIR::Request\n:\016@selector:\017send_email:\016@receiverm\\

MORE

Socket service with local fallback. – Output – Page 16

nEmail:\024@receiver_classc\vModule:\f@result:\aok:\017@arguments[\a:\020pdf_inv\
oice{\a:\ato\"\024user2@email.com:\rcustomeri\001{;\r;\016" => ...
  69226 ASIR::Coder::Marshal :decode, "\004\bo:\023ASIR::Response\b:\017@excepti\
on0:\r@requesto:\022ASIR::Request\n:\016@selector:\017send_email:\016@receiverm\\
nEmail:\024@receiver_classc\vModule:\f@result:\aok:\017@arguments[\a:\020pdf_inv\
oice{\a:\ato\"\024user2@email.com:\rcustomeri\001{;\r;\016" => 
    #<ASIR::Response:0x100475be8 @exception=nil, @request=#<ASIR::Request:0x1004\
75b20 @selector=:send_email, @receiver=Email, @receiver_class=Module, @result=:o\
k, @arguments=[:pdf_invoice, {:to=>"user2@email.com", :customer=>123}]>, @result\
=:ok>
  69226 ASIR::Transport::TcpSocket :receive_response => 
    #<ASIR::Response:0x100475be8 @exception=nil, @request=#<ASIR::Request:0x1004\
75b20 @selector=:send_email, @receiver=Email, @receiver_class=Module, @result=:o\
k, @arguments=[:pdf_invoice, {:to=>"user2@email.com", :customer=>123}]>, @result\
=:ok>

MORE

Socket service with local fallback. – Output – Page 17

  69226 ASIR::Transport::TcpSocket :send_request, :response, #<ASIR::Response:0x\
100475be8 @exception=nil, @request=#<ASIR::Request:0x100475b20 @selector=:send_e\
mail, @receiver=Email, @receiver_class=Module, @result=:ok, @arguments=[:pdf_inv\
oice, {:to=>"user2@email.com", :customer=>123}]>, @result=:ok>
  69226 ASIR::Transport::TcpSocket :send_request, :request, #<ASIR::Request:0x10\
0477330 @selector=:send_email, @receiver=Email, @receiver_class=Module, @argumen\
ts=[:pdf_invoice, {:to=>"user2@email.com", :customer=>123}]> => 
    :ok
  69229 ASIR::Transport::TcpSocket run_socket_server!: disconnected
*** 69226: pr: :ok

MORE

Socket service with local fallback. – Output – Page 18

"service.log" contents:
159
--- !ruby/object:ASIR::Request 
arguments: 
- :pdf_invoice
- :to: user@email.com
  :customer: 123
receiver: Email
receiver_class: Module
selector: :send_email

Synopsis

  • Services are easy to abstract away.
  • Separation of transport, encoding.

Object Initialization

Support initialization by Hash.

E.g.:

  Foo.new(:bar => 1, :baz => 2)


  obj = Foo.new; obj.bar = 1; obj.baz = 2; obj

module ASIR
  module Initialization
    def initialize opts = nil
      opts ||= EMPTY_HASH
      initialize_before_opts if respond_to? :initialize_before_opts
      opts.each do | k, v |
        send(:"#{k}=", v)
      end
      initialize_after_opts if respond_to? :initialize_after_opts
    end
  end
end

Diagnostic Logging

Logging mixin.

module ASIR
  module Log
    def _log msg = nil
      msg ||= yield
      msg = String === msg ? msg : _log_format(msg)
      msg = "  #{$$} #{Module === self ? self : self.class} #{msg}"
      case @logger
      when Proc
        @logger.call msg
      when IO
        @logger.puts msg
      else
        $stderr.puts msg if $_log_verbose
      end
    end

    def _log_result msg
      msg = String === msg ? msg : _log_format(msg)
      _log { "#{msg} => ..." }
      result = yield
      _log { "#{msg} => \n    #{result.inspect}" }
      result
    end

    def _log_format obj
      case obj
      when Exception
        "#{obj.inspect}\n    #{obj.backtrace * "\n    "}"
      when Array
        obj.map { | x | _log_format x } * ", "
      else
        obj.inspect
      end
    end
  end
end

Null Coder

Always encode/decode as nil.

module ASIR
  class Coder
    class Null < self
      def _encode obj
        nil
      end

      def _decode obj
        nil
      end
    end
  end
end

Other Coders

Encode as XML.

module ASIR
  class Coder
    class XML < self
      # ...
    end

    # Encode as JSON.
    class JSON < self
      # ...
    end
  end
end

Broadcast Transport

Broadcast to multiple Transports.

module ASIR
  class Transport
    class Broadcast < self
      attr_accessor :transports

      def _send_request request
        result = nil
        transports.each do | transport |
          _log { [ :send_request, :transport, transport ] }
          result = transport.send_request(request)
        end
        result
      end

      def _receive_response opaque
        opaque
      end

      def needs_request_identifier?
        transports.any? { | t | t.needs_request_identifier? }
      end
    end
  end
end

HTTP Transport

Send to an HTTP server.

module ASIR
  class Transport
    class HTTP < self
      attr_accessor :uri

      def _send_request request
        # ...
      end
    end
  end
end