asir 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +11 -0
- data/Gemfile +16 -0
- data/README.textile +50 -0
- data/Rakefile +83 -0
- data/VERSION +1 -0
- data/asir.gemspec +36 -0
- data/asir.riterate.yml +114 -0
- data/bin/asir +6 -0
- data/doc/Rakefile +8 -0
- data/doc/asir-sequence.pic +84 -0
- data/doc/asir-sequence.svg +1559 -0
- data/doc/sequence.pic +430 -0
- data/example/asir_control.sh +24 -0
- data/example/asir_control_client_http.rb +14 -0
- data/example/asir_control_client_zmq.rb +15 -0
- data/example/config/asir_config.rb +63 -0
- data/example/delayed_service.rb +15 -0
- data/example/ex01.rb +12 -0
- data/example/ex02.rb +12 -0
- data/example/ex03.rb +19 -0
- data/example/ex04.rb +33 -0
- data/example/ex05.rb +16 -0
- data/example/ex06.rb +26 -0
- data/example/ex07.rb +28 -0
- data/example/ex08.rb +30 -0
- data/example/ex09.rb +25 -0
- data/example/ex10.rb +24 -0
- data/example/ex11.rb +48 -0
- data/example/ex12.rb +34 -0
- data/example/ex13.rb +35 -0
- data/example/ex14.rb +30 -0
- data/example/ex15.rb +13 -0
- data/example/ex16.rb +33 -0
- data/example/ex17.rb +41 -0
- data/example/ex18.rb +62 -0
- data/example/ex19.rb +32 -0
- data/example/ex20.rb +28 -0
- data/example/ex21.rb +28 -0
- data/example/ex22.rb +15 -0
- data/example/ex23.rb +20 -0
- data/example/ex24.rb +35 -0
- data/example/example_helper.rb +51 -0
- data/example/sample_service.rb +162 -0
- data/example/unsafe_service.rb +12 -0
- data/hack_night/README.txt +18 -0
- data/hack_night/exercise/prob-1.rb +18 -0
- data/hack_night/exercise/prob-2.rb +21 -0
- data/hack_night/exercise/prob-3.rb +16 -0
- data/hack_night/exercise/prob-4.rb +36 -0
- data/hack_night/exercise/prob-5.rb +36 -0
- data/hack_night/exercise/prob-6.rb +95 -0
- data/hack_night/exercise/prob-7.rb +34 -0
- data/hack_night/solution/math_service.rb +11 -0
- data/hack_night/solution/prob-1.rb +12 -0
- data/hack_night/solution/prob-2.rb +15 -0
- data/hack_night/solution/prob-3.rb +17 -0
- data/hack_night/solution/prob-4.rb +37 -0
- data/hack_night/solution/prob-5.rb +21 -0
- data/hack_night/solution/prob-6.rb +33 -0
- data/hack_night/solution/prob-7.rb +36 -0
- data/lab/phony_proc.rb +31 -0
- data/lib/asir.rb +253 -0
- data/lib/asir/additional_data.rb +25 -0
- data/lib/asir/channel.rb +130 -0
- data/lib/asir/client.rb +111 -0
- data/lib/asir/code_block.rb +57 -0
- data/lib/asir/code_more.rb +50 -0
- data/lib/asir/coder.rb +26 -0
- data/lib/asir/coder/base64.rb +19 -0
- data/lib/asir/coder/chain.rb +30 -0
- data/lib/asir/coder/identity.rb +23 -0
- data/lib/asir/coder/json.rb +30 -0
- data/lib/asir/coder/marshal.rb +17 -0
- data/lib/asir/coder/null.rb +23 -0
- data/lib/asir/coder/proc.rb +22 -0
- data/lib/asir/coder/sign.rb +48 -0
- data/lib/asir/coder/xml.rb +213 -0
- data/lib/asir/coder/yaml.rb +33 -0
- data/lib/asir/coder/zlib.rb +21 -0
- data/lib/asir/configuration.rb +32 -0
- data/lib/asir/error.rb +34 -0
- data/lib/asir/identity.rb +36 -0
- data/lib/asir/initialization.rb +23 -0
- data/lib/asir/log.rb +82 -0
- data/lib/asir/main.rb +396 -0
- data/lib/asir/message.rb +31 -0
- data/lib/asir/message/delay.rb +35 -0
- data/lib/asir/object_resolving.rb +15 -0
- data/lib/asir/result.rb +39 -0
- data/lib/asir/retry_behavior.rb +54 -0
- data/lib/asir/transport.rb +241 -0
- data/lib/asir/transport/beanstalk.rb +217 -0
- data/lib/asir/transport/broadcast.rb +34 -0
- data/lib/asir/transport/buffer.rb +115 -0
- data/lib/asir/transport/composite.rb +19 -0
- data/lib/asir/transport/connection_oriented.rb +180 -0
- data/lib/asir/transport/delay.rb +38 -0
- data/lib/asir/transport/delegation.rb +53 -0
- data/lib/asir/transport/fallback.rb +36 -0
- data/lib/asir/transport/file.rb +88 -0
- data/lib/asir/transport/http.rb +54 -0
- data/lib/asir/transport/local.rb +21 -0
- data/lib/asir/transport/null.rb +14 -0
- data/lib/asir/transport/payload_io.rb +52 -0
- data/lib/asir/transport/rack.rb +73 -0
- data/lib/asir/transport/retry.rb +41 -0
- data/lib/asir/transport/stream.rb +35 -0
- data/lib/asir/transport/subprocess.rb +30 -0
- data/lib/asir/transport/tcp_socket.rb +34 -0
- data/lib/asir/transport/webrick.rb +50 -0
- data/lib/asir/transport/zmq.rb +110 -0
- data/lib/asir/uuid.rb +32 -0
- data/lib/asir/version.rb +3 -0
- data/spec/const_get_speed_spec.rb +33 -0
- data/spec/debug_helper.rb +20 -0
- data/spec/example_spec.rb +88 -0
- data/spec/json_spec.rb +128 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/xml_spec.rb +144 -0
- data/stylesheets/slides.css +105 -0
- metadata +173 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'asir/transport/composite'
|
|
2
|
+
|
|
3
|
+
module ASIR
|
|
4
|
+
class Transport
|
|
5
|
+
# !SLIDE
|
|
6
|
+
# Broadcast Transport
|
|
7
|
+
#
|
|
8
|
+
# Broadcast to multiple Transports.
|
|
9
|
+
class Broadcast < self
|
|
10
|
+
include Composite
|
|
11
|
+
|
|
12
|
+
def _send_message message, message_payload
|
|
13
|
+
result = first_exception = nil
|
|
14
|
+
transports.each do | transport |
|
|
15
|
+
begin
|
|
16
|
+
result = transport.send_message(message)
|
|
17
|
+
rescue ::Exception => exc
|
|
18
|
+
first_exception ||= exc
|
|
19
|
+
_handle_send_message_exception! transport, message, exc
|
|
20
|
+
raise unless @continue_on_exception
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
if first_exception && @reraise_first_exception
|
|
24
|
+
$! = first_exception
|
|
25
|
+
raise
|
|
26
|
+
end
|
|
27
|
+
result
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
# !SLIDE END
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
require 'asir/transport'
|
|
2
|
+
require 'thread'
|
|
3
|
+
|
|
4
|
+
module ASIR
|
|
5
|
+
class Transport
|
|
6
|
+
# !SLIDE
|
|
7
|
+
# Buffer Transport
|
|
8
|
+
#
|
|
9
|
+
# Buffers Messages until #flush!
|
|
10
|
+
# Assumes One-way Messages.
|
|
11
|
+
class Buffer < self
|
|
12
|
+
include Delegation
|
|
13
|
+
|
|
14
|
+
# Transport to send_message.
|
|
15
|
+
attr_accessor :transport
|
|
16
|
+
|
|
17
|
+
def initialize *args
|
|
18
|
+
super
|
|
19
|
+
@messages = Queue.new
|
|
20
|
+
@messages_mutex = Mutex.new
|
|
21
|
+
@paused = 0
|
|
22
|
+
@paused_mutex = Mutex.new
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# If paused, queue messages,
|
|
26
|
+
# Otherwise delegate immediately to #transport.
|
|
27
|
+
def _send_message message, message_payload
|
|
28
|
+
return nil if @ignore
|
|
29
|
+
if paused?
|
|
30
|
+
@messages_mutex.synchronize do
|
|
31
|
+
@messages << message
|
|
32
|
+
end
|
|
33
|
+
nil
|
|
34
|
+
else
|
|
35
|
+
@transport.send_message(message)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Returns true if currently paused.
|
|
40
|
+
# Messages are queued until #resume!.
|
|
41
|
+
def paused?
|
|
42
|
+
@paused > 0
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Pauses all messages until resume!.
|
|
46
|
+
# May be called multiple times.
|
|
47
|
+
def pause!
|
|
48
|
+
@paused_mutex.synchronize do
|
|
49
|
+
@paused += 1
|
|
50
|
+
end
|
|
51
|
+
self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Will automatically call #flush! when not #paused?.
|
|
55
|
+
def resume!
|
|
56
|
+
should_flush = @paused_mutex.synchronize do
|
|
57
|
+
@paused -= 1 if @paused > 0
|
|
58
|
+
@paused == 0
|
|
59
|
+
end
|
|
60
|
+
flush! if should_flush
|
|
61
|
+
self
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def size
|
|
65
|
+
@messages_mutex.synchronize do
|
|
66
|
+
@messages.size
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Will flush pending Messages even if ! #paused?.
|
|
71
|
+
def flush!
|
|
72
|
+
clear!.each do | message |
|
|
73
|
+
@transport.send_message(message)
|
|
74
|
+
end
|
|
75
|
+
self
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Clear all pending Messages without sending them.
|
|
79
|
+
# Returns Array of Messages that would have been sent.
|
|
80
|
+
def clear!
|
|
81
|
+
messages = [ ]
|
|
82
|
+
@messages_mutex.synchronize do
|
|
83
|
+
@messages.size.times do
|
|
84
|
+
messages << @messages.shift(true)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
messages
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Take Message from head of Queue.
|
|
91
|
+
def shift non_block=false
|
|
92
|
+
@messages.shift(non_block)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Processes queue.
|
|
96
|
+
# Usually used in worker Thread.
|
|
97
|
+
def process! non_block=false
|
|
98
|
+
@running = true
|
|
99
|
+
while @running && message = shift(non_block)
|
|
100
|
+
@transport.send_message(message)
|
|
101
|
+
end
|
|
102
|
+
message
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Stop processing queue.
|
|
106
|
+
def stop!
|
|
107
|
+
@messages_mutex.synchronize do
|
|
108
|
+
@ignore = true; @running = false
|
|
109
|
+
end
|
|
110
|
+
self
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
# !SLIDE END
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'asir/transport/delegation'
|
|
2
|
+
module ASIR
|
|
3
|
+
class Transport
|
|
4
|
+
# !SLIDE
|
|
5
|
+
# A Transport composed of other Transports.
|
|
6
|
+
#
|
|
7
|
+
# Classes that mix this in should define #_send_message(message, message_payload).
|
|
8
|
+
module Composite
|
|
9
|
+
include Delegation
|
|
10
|
+
|
|
11
|
+
# Enumerable of Transport objects.
|
|
12
|
+
attr_accessor :transports
|
|
13
|
+
# If true, continue with other Transports when Transport#send_message raises an Exception.
|
|
14
|
+
attr_accessor :continue_on_exception
|
|
15
|
+
end
|
|
16
|
+
# !SLIDE END
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
require 'asir/transport/stream'
|
|
2
|
+
require 'asir/transport/payload_io'
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
module ASIR
|
|
6
|
+
class Transport
|
|
7
|
+
# !SLIDE
|
|
8
|
+
# Connection-Oriented Transport
|
|
9
|
+
class ConnectionOriented < Stream
|
|
10
|
+
include PayloadIO
|
|
11
|
+
|
|
12
|
+
attr_accessor :uri, :scheme, :port, :address
|
|
13
|
+
alias :protocol :scheme
|
|
14
|
+
alias :protocol= :scheme=
|
|
15
|
+
|
|
16
|
+
def uri
|
|
17
|
+
@uri ||= "#{scheme}://#{address}:#{port}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def scheme
|
|
21
|
+
@scheme ||=
|
|
22
|
+
case
|
|
23
|
+
when @uri
|
|
24
|
+
URI.parse(@uri).scheme
|
|
25
|
+
else
|
|
26
|
+
'tcp'.freeze
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def address
|
|
31
|
+
@address ||=
|
|
32
|
+
case
|
|
33
|
+
when @uri
|
|
34
|
+
URI.parse(@uri).host
|
|
35
|
+
else
|
|
36
|
+
'127.0.0.1'.freeze
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def port
|
|
41
|
+
@port ||=
|
|
42
|
+
case
|
|
43
|
+
when @uri
|
|
44
|
+
URI.parse(@uri).port
|
|
45
|
+
else
|
|
46
|
+
raise Error, "#{self.class}: port not set."
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# !SLIDE
|
|
51
|
+
# Returns a connected Channel.
|
|
52
|
+
def stream
|
|
53
|
+
@stream ||=
|
|
54
|
+
connect!
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Yields Channel after _connect!.
|
|
58
|
+
def connect!(opts = nil, &blk)
|
|
59
|
+
base_opts = {
|
|
60
|
+
:on_connect => lambda { | channel |
|
|
61
|
+
connection = _connect!
|
|
62
|
+
blk.call(connection) if blk
|
|
63
|
+
connection
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
base_opts.update(opts) if opts
|
|
67
|
+
Channel.new(base_opts)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Returns raw client stream.
|
|
71
|
+
def _connect!
|
|
72
|
+
_log { "_connect! #{uri}" } if @verbose >= 1
|
|
73
|
+
sock = _client_connect!
|
|
74
|
+
_log { "_connect! socket=#{sock}" } if @verbose >= 1
|
|
75
|
+
_after_connect! sock
|
|
76
|
+
sock
|
|
77
|
+
rescue ::Exception => err
|
|
78
|
+
raise Error, "Cannot connect to #{self.class} #{uri}: #{err.inspect}", err.backtrace
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Subclasses can override.
|
|
82
|
+
def _after_connect! stream
|
|
83
|
+
self
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Subclasses can override.
|
|
87
|
+
def _before_close! stream
|
|
88
|
+
self
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# !SLIDE
|
|
92
|
+
# Sends the encoded Message payload String.
|
|
93
|
+
def _send_message message, message_payload
|
|
94
|
+
stream.with_stream! do | io |
|
|
95
|
+
_write message_payload, io
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# !SLIDE
|
|
100
|
+
# Receives the encoded Message payload String.
|
|
101
|
+
def _receive_message stream, additional_data
|
|
102
|
+
[ _read(stream), nil ]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# !SLIDE
|
|
106
|
+
# Sends the encoded Result payload String.
|
|
107
|
+
def _send_result message, result, result_payload, stream, message_state
|
|
108
|
+
unless @one_way || message.one_way
|
|
109
|
+
_write result_payload, stream
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# !SLIDE
|
|
114
|
+
# Receives the encoded Result payload String.
|
|
115
|
+
def _receive_result message, opaque_result
|
|
116
|
+
unless @one_way || message.one_way
|
|
117
|
+
stream.with_stream! do | io |
|
|
118
|
+
_read io
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# !SLIDE
|
|
124
|
+
# Server
|
|
125
|
+
|
|
126
|
+
def prepare_server!
|
|
127
|
+
_log { "prepare_server! #{uri}" } if @verbose >= 1
|
|
128
|
+
_server!
|
|
129
|
+
rescue ::Exception => err
|
|
130
|
+
_log [ "prepare_server! #{uri}", :exception, err ]
|
|
131
|
+
raise Error, "Cannot prepare server on #{self.class} #{uri}: #{err.inspect}", err.backtrace
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def run_server!
|
|
135
|
+
_log { "run_server! #{uri}" } if @verbose >= 1
|
|
136
|
+
with_server_signals! do
|
|
137
|
+
@running = true
|
|
138
|
+
while @running
|
|
139
|
+
stream = _server_accept_connection! @server
|
|
140
|
+
_log { "run_server!: connected" } if @verbose >= 1
|
|
141
|
+
begin
|
|
142
|
+
# Same socket for both in and out stream.
|
|
143
|
+
serve_stream! stream, stream
|
|
144
|
+
ensure
|
|
145
|
+
_server_close_connection!(stream)
|
|
146
|
+
end
|
|
147
|
+
_log { "run_server!: disconnected" } if @verbose >= 1
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
self
|
|
151
|
+
ensure
|
|
152
|
+
_server_close!
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def _server!
|
|
156
|
+
raise Error::SubclassResponsibility, "_server!"
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def _server_close!
|
|
160
|
+
if @server
|
|
161
|
+
@server.close rescue nil
|
|
162
|
+
end
|
|
163
|
+
@server = nil
|
|
164
|
+
self
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Accept a client connection.
|
|
168
|
+
def _server_accept_connection! server
|
|
169
|
+
raise Error::SubclassResponsibility, "_server_accept_connection!"
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Close a client connection.
|
|
173
|
+
def _server_close_connection! stream
|
|
174
|
+
raise Error::SubclassResponsibility, "_server_close_connection!"
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
# !SLIDE END
|
|
178
|
+
end # class
|
|
179
|
+
end # module
|
|
180
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module ASIR
|
|
2
|
+
class Message
|
|
3
|
+
# !SLIDE
|
|
4
|
+
# Message Delay Support
|
|
5
|
+
#
|
|
6
|
+
module Delay
|
|
7
|
+
# Returns the number of seconds from now, that the message should be delayed.
|
|
8
|
+
# If message.delay is Numeric, sets message.delay to the Time to delay til.
|
|
9
|
+
# If message.delay is Time, returns (now - message.delay).to_f
|
|
10
|
+
# Returns Float if message.delay was set, or nil.
|
|
11
|
+
# Returns 0 if delay has already expired.
|
|
12
|
+
def relative_message_delay! message, now = nil
|
|
13
|
+
case delay = message.delay
|
|
14
|
+
when nil
|
|
15
|
+
when Numeric
|
|
16
|
+
now ||= Time.now
|
|
17
|
+
delay = delay.to_f
|
|
18
|
+
message.delay = (now + delay).utc
|
|
19
|
+
when Time
|
|
20
|
+
now ||= Time.now
|
|
21
|
+
delay = (delay - now).to_f
|
|
22
|
+
delay = 0 if delay < 0
|
|
23
|
+
else
|
|
24
|
+
raise TypeError, "Expected message.delay to be Numeric or Time, given #{delay.class}"
|
|
25
|
+
end
|
|
26
|
+
delay
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def wait_for_delay! message
|
|
30
|
+
while (delay = relative_message_delay!(message)) && delay > 0
|
|
31
|
+
sleep delay
|
|
32
|
+
end
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module ASIR
|
|
2
|
+
class Transport
|
|
3
|
+
# !SLIDE
|
|
4
|
+
# A Transport that delgated to one or more other Transports.
|
|
5
|
+
#
|
|
6
|
+
# Classes that include this must define #_send_message(message, message_payload).
|
|
7
|
+
module Delegation
|
|
8
|
+
# If true, reraise the first Exception that occurred during Transport#send_message.
|
|
9
|
+
attr_accessor :reraise_first_exception
|
|
10
|
+
|
|
11
|
+
# Proc to call(transport, message, exc) when a delegated #send_message fails.
|
|
12
|
+
attr_accessor :on_send_message_exception
|
|
13
|
+
|
|
14
|
+
# Proc to call(transport, message) when #send_message fails with no recourse.
|
|
15
|
+
attr_accessor :on_failed_message
|
|
16
|
+
|
|
17
|
+
# Return the subTransports#send_message result unmodified from #_send_message.
|
|
18
|
+
def _receive_result message, opaque_result
|
|
19
|
+
opaque_result
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Return the subTransports#send_message result unmodified from #_send_message.
|
|
23
|
+
def receive_result message, opaque_result
|
|
24
|
+
opaque_result
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def needs_message_identifier?
|
|
28
|
+
@needs_message_identifier ||
|
|
29
|
+
transports.any? { | t | t.needs_message_identifier? }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def needs_message_timestamp?
|
|
33
|
+
@needs_message_timestamp ||
|
|
34
|
+
transports.any? { | t | t.needs_message_timestamp? }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Subclasses with multiple transport should override this method.
|
|
38
|
+
def transports
|
|
39
|
+
@transports ||= [ transport ]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Called from within _send_message rescue.
|
|
43
|
+
def _handle_send_message_exception! transport, message, exc
|
|
44
|
+
_log { [ :send_message, :transport_failed, exc ] }
|
|
45
|
+
(message[:transport_exceptions] ||= [ ]) << "#{exc.inspect}"
|
|
46
|
+
@on_send_message_exception.call(self, message, exc) if @on_send_message_exception
|
|
47
|
+
self
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
# !SLIDE END
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require 'asir/transport/composite'
|
|
2
|
+
|
|
3
|
+
module ASIR
|
|
4
|
+
class Transport
|
|
5
|
+
# !SLIDE
|
|
6
|
+
# Fallback Transport
|
|
7
|
+
class Fallback < self
|
|
8
|
+
include Composite
|
|
9
|
+
|
|
10
|
+
def _send_message message, message_payload
|
|
11
|
+
result = sent = first_exception = nil
|
|
12
|
+
transports.each do | transport |
|
|
13
|
+
begin
|
|
14
|
+
result = transport.send_message(message)
|
|
15
|
+
sent = true
|
|
16
|
+
break
|
|
17
|
+
rescue ::Exception => exc
|
|
18
|
+
first_exception ||= exc
|
|
19
|
+
_handle_send_message_exception! transport, message, exc
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
unless sent
|
|
23
|
+
if first_exception && @reraise_first_exception
|
|
24
|
+
$! = first_exception
|
|
25
|
+
raise
|
|
26
|
+
end
|
|
27
|
+
raise FallbackError, "fallback failed"
|
|
28
|
+
end
|
|
29
|
+
result
|
|
30
|
+
end
|
|
31
|
+
class FallbackError < Error; end
|
|
32
|
+
end
|
|
33
|
+
# !SLIDE END
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|