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,36 @@
|
|
1
|
+
# Use the Marshal and Base64 coders in prob-4.rb
|
2
|
+
|
3
|
+
$: << File.expand_path("../../../lib", __FILE__)
|
4
|
+
require 'asir/transport/http'
|
5
|
+
require 'asir/coder/marshal'
|
6
|
+
require 'asir/coder/base64'
|
7
|
+
require 'asir/coder/chain'
|
8
|
+
|
9
|
+
require 'math_service'
|
10
|
+
MathService.send(:include, ASIR::Client)
|
11
|
+
|
12
|
+
port = 3001
|
13
|
+
begin
|
14
|
+
t = ASIR::Transport::HTTP.new(:uri => "http://localhost:#{port}/")
|
15
|
+
t._log_enabled = true
|
16
|
+
c = t.encoder = ASIR::Coder::Chain.new(:encoders =>
|
17
|
+
[
|
18
|
+
ASIR::Coder::Marshal.new,
|
19
|
+
ASIR::Coder::Base64.new,
|
20
|
+
])
|
21
|
+
c._log_enabled = true
|
22
|
+
|
23
|
+
server_pid = Process.fork do
|
24
|
+
t.setup_webrick_server!
|
25
|
+
t.start_webrick_server!
|
26
|
+
end
|
27
|
+
sleep 1 # wait for server to start
|
28
|
+
|
29
|
+
MathService.client.transport = t
|
30
|
+
MathService.client.sum([1, 2, 3])
|
31
|
+
rescue Exception => err
|
32
|
+
$stderr.puts "ERROR: #{err.inspect}\n#{err.backtrace * "\n"}"
|
33
|
+
ensure
|
34
|
+
Process.kill(9, server_pid)
|
35
|
+
end
|
36
|
+
|
data/lab/phony_proc.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
class PhonyProc
|
5
|
+
def initialize hash
|
6
|
+
@data = hash
|
7
|
+
end
|
8
|
+
def to_proc
|
9
|
+
self
|
10
|
+
end
|
11
|
+
def call lang, *args, &blk
|
12
|
+
@data[lang].call(*args, &blk)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def with_block something, &block
|
17
|
+
pp block
|
18
|
+
block.call(:ruby, something)
|
19
|
+
end
|
20
|
+
|
21
|
+
with_block(123, &PhonyProc.new(:ruby => proc { | x | x * 2 },
|
22
|
+
:js => 'function (x) { return x * 2; }'))
|
23
|
+
|
24
|
+
=begin
|
25
|
+
|
26
|
+
> ruby lab/phony_proc.rb
|
27
|
+
lab/phony_proc.rb:21: PhonyProc#to_proc should return Proc (TypeError)
|
28
|
+
|
29
|
+
wah-wah.. :(
|
30
|
+
|
31
|
+
=end
|
data/lib/asir.rb
ADDED
@@ -0,0 +1,253 @@
|
|
1
|
+
# !SLIDE
|
2
|
+
# Abstracting Services in Ruby
|
3
|
+
#
|
4
|
+
# * Kurt Stephens
|
5
|
+
# * Enova Financial
|
6
|
+
# * 2012/04/02
|
7
|
+
# * Slides -- "":http://kurtstephens.com/pub/ruby/abstracting_services_in_ruby/asir.slides/
|
8
|
+
# * Code -- "":http://kurtstephens.com/pub/ruby/abstracting_services_in_ruby/
|
9
|
+
# * Git -- "":http://github.com/kstephens/abstracting_services_in_ruby
|
10
|
+
#
|
11
|
+
# !SLIDE END
|
12
|
+
|
13
|
+
# !SLIDE
|
14
|
+
# Issues
|
15
|
+
#
|
16
|
+
# * Problem Domain, Solution Domain
|
17
|
+
# * Service Middleware Semantics
|
18
|
+
# * Testing, Debugging, Diagnostics
|
19
|
+
# * Productivity
|
20
|
+
#
|
21
|
+
# !SLIDE END
|
22
|
+
|
23
|
+
# !SLIDE
|
24
|
+
# Problem Domain, Solution Domain
|
25
|
+
#
|
26
|
+
# * Client knows too much about infrastructure.
|
27
|
+
# * Evaluating and switching infrastructures.
|
28
|
+
#
|
29
|
+
# !SLIDE END
|
30
|
+
|
31
|
+
# !SLIDE
|
32
|
+
# Service Middleware Semantics
|
33
|
+
#
|
34
|
+
# * Directionality: One-way, Two-way
|
35
|
+
# * Synchronicity: Synchronous, Asynchronous, Delayed, Buffered
|
36
|
+
# * Distribution: Local Thread, Local Process, Distributed
|
37
|
+
# * Transport: File, IPC, Pipe, Network
|
38
|
+
# * Robustness: Retry, Replay, Fallback
|
39
|
+
# * Encoding: XML, JSON, YAML, Base64, Compression
|
40
|
+
#
|
41
|
+
# !SLIDE END
|
42
|
+
|
43
|
+
# !SLIDE
|
44
|
+
# Testing, Debugging, Diagnostics
|
45
|
+
#
|
46
|
+
# * Configuration for testing and QA is more complex.
|
47
|
+
# * Measuring test coverage of remote services.
|
48
|
+
# * Debugging the root cause of remote service errors.
|
49
|
+
# * Diagnostic hooks.
|
50
|
+
#
|
51
|
+
# !SLIDE END
|
52
|
+
|
53
|
+
# !SLIDE
|
54
|
+
# Objectives
|
55
|
+
#
|
56
|
+
# * Simplify service/client definitions and interfaces.
|
57
|
+
# * Anticipate new encoding, delivery and security requirements.
|
58
|
+
# * Separate encoding and transport concerns.
|
59
|
+
# * Composition over Configuration.
|
60
|
+
# * Elide deployment decisions.
|
61
|
+
# * Integrate diagnostics and logging.
|
62
|
+
# * Simplify testing.
|
63
|
+
#
|
64
|
+
# !SLIDE END
|
65
|
+
|
66
|
+
# !SLIDE
|
67
|
+
# Foundations of Objects
|
68
|
+
#
|
69
|
+
# * Message
|
70
|
+
# * State
|
71
|
+
# * Behavior
|
72
|
+
|
73
|
+
# !SLIDE
|
74
|
+
# Messaging
|
75
|
+
#
|
76
|
+
# * "Call a Method", "Call a Function" are all the same, in *all* languages.
|
77
|
+
# ** Decomposed into lookup() and apply().
|
78
|
+
# * "Send Message", not "Call a Method".
|
79
|
+
# * Messaging abstracts:
|
80
|
+
# ** Object use from its implementation.
|
81
|
+
# ** Transfer of control (method, function invocation, RPC, etc).
|
82
|
+
|
83
|
+
# !SLIDE
|
84
|
+
# REST
|
85
|
+
#
|
86
|
+
# "Roy Fielding - Architectural Styles and the Design of Network-based Software Architectures":http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
|
87
|
+
#
|
88
|
+
# * Imperative Action .vs. Behavorial Resource
|
89
|
+
# * REST Connector .vs. REST Component
|
90
|
+
# * "Generality of connectors leads to middleware..."
|
91
|
+
# * "Modifiability is about the ease with which a change can be made to an application architecture... broken down into evolvability, extensibility, customizability, configurability, and reusability..."
|
92
|
+
#
|
93
|
+
# !SLIDE END
|
94
|
+
|
95
|
+
# !SLIDE
|
96
|
+
# Design
|
97
|
+
#
|
98
|
+
# * Nouns -> Objects -> Classes
|
99
|
+
# * Verbs -> Responsibilities -> Methods
|
100
|
+
#
|
101
|
+
# h3. Book: "Designing Object-Oriented Software"
|
102
|
+
# * Wirfs-Brock, Wilkerson, Wiener
|
103
|
+
#
|
104
|
+
# !SLIDE END
|
105
|
+
|
106
|
+
# !SLIDE
|
107
|
+
# Design: Nouns
|
108
|
+
#
|
109
|
+
# * Service -> Object
|
110
|
+
# * Client -> Just a Ruby message sender.
|
111
|
+
# * Proxy
|
112
|
+
# * Message -> Just a Ruby message.
|
113
|
+
# * Result, Exception (two-way) -> Return value or else.
|
114
|
+
# * Transport -> (file, pipe, http, queue, ActiveResource)
|
115
|
+
# * Encoder, Decoder -> Coder (Marshal, XML, JSON, ActiveResource)
|
116
|
+
#
|
117
|
+
# !SLIDE END
|
118
|
+
|
119
|
+
# !SLIDE
|
120
|
+
# Design: Verbs
|
121
|
+
#
|
122
|
+
# * Intercept Message -> Proxy
|
123
|
+
# * Invoke Message -> Message
|
124
|
+
# * Return Result, Invoke Exception -> Result
|
125
|
+
# * Send Message, Recieve Message -> Transport
|
126
|
+
# * Encode Object, Decode Object -> Coder
|
127
|
+
#
|
128
|
+
# !SLIDE END
|
129
|
+
|
130
|
+
# !SLIDE
|
131
|
+
# Simple
|
132
|
+
#
|
133
|
+
# !IMAGE BEGIN PIC width:800 height:300
|
134
|
+
#
|
135
|
+
# box "Client" "(CustomersController" "#send_invoice)" wid 4.0 ht 2.5; arrow;
|
136
|
+
# ellipse "Send" "Message" "(Ruby message)" wid 4.0 ht 2.5; arrow;
|
137
|
+
# box "Service" "(Email.send_email)" wid 4.0 ht 2.5;
|
138
|
+
#
|
139
|
+
# !IMAGE END
|
140
|
+
#
|
141
|
+
# !SLIDE END
|
142
|
+
|
143
|
+
# !SLIDE
|
144
|
+
# Client-Side Message
|
145
|
+
#
|
146
|
+
# !IMAGE BEGIN PIC width:800 height:300
|
147
|
+
# box "Client" wid 2.5 ht 2.5; arrow;
|
148
|
+
# ellipse "Proxy" wid 2.5 ht 2.5; arrow;
|
149
|
+
# ellipse "Create" "Message" wid 2.5 ht 2.5; arrow;
|
150
|
+
# ellipse "Encode" "Message" wid 2.5 ht 2.5; arrow;
|
151
|
+
# ellipse "Send" "Message" wid 2.5 ht 2.5;
|
152
|
+
# line; down; arrow;
|
153
|
+
# !IMAGE END
|
154
|
+
#
|
155
|
+
# !SLIDE END
|
156
|
+
|
157
|
+
# !SLIDE
|
158
|
+
# Server-Side
|
159
|
+
#
|
160
|
+
# !IMAGE BEGIN PIC width:800 height:300
|
161
|
+
# down; line; right; arrow;
|
162
|
+
# ellipse "Receive" "Message" wid 2.5 ht 2.5; arrow;
|
163
|
+
# ellipse "Decode" "Message" wid 2.5 ht 2.5; arrow;
|
164
|
+
# ellipse "Message" wid 2.5 ht 2.5;
|
165
|
+
# line; down; arrow;
|
166
|
+
# IR: ellipse "Invoke" "Message" wid 2.5 ht 2.5;
|
167
|
+
# right; move; move;
|
168
|
+
# Service: box "Service" wid 2.5 ht 2.5 with .w at IR.e + (movewid, 0);
|
169
|
+
# arrow <-> from IR.e to Service.w;
|
170
|
+
# move to IR.s; down; line;
|
171
|
+
# left; arrow;
|
172
|
+
# ellipse "Create" "Result" wid 2.5 ht 2.5; arrow;
|
173
|
+
# ellipse "Encode" "Result" wid 2.5 ht 2.5; arrow;
|
174
|
+
# ellipse "Send" "Result" wid 2.5 ht 2.5;
|
175
|
+
# line; down; arrow
|
176
|
+
# !IMAGE END
|
177
|
+
#
|
178
|
+
# !SLIDE END
|
179
|
+
|
180
|
+
# !SLIDE
|
181
|
+
# Client-Side Result
|
182
|
+
#
|
183
|
+
# !IMAGE BEGIN PIC width:800 height:300
|
184
|
+
# down; line; left; arrow;
|
185
|
+
# ellipse "Receive" "Result" wid 2.5 ht 2.5; arrow;
|
186
|
+
# ellipse "Decode" "Result" wid 2.5 ht 2.5; arrow;
|
187
|
+
# ellipse "Result" wid 2.5 ht 2.5; arrow;
|
188
|
+
# ellipse "Proxy" wid 2.5 ht 2.5; arrow;
|
189
|
+
# box "Client" wid 2.5 ht 2.5;
|
190
|
+
# !IMAGE END
|
191
|
+
#
|
192
|
+
# !SLIDE END
|
193
|
+
|
194
|
+
# !SLIDE
|
195
|
+
# Implementation
|
196
|
+
#
|
197
|
+
# * Primary Base classes: Transport, Coder.
|
198
|
+
# * Primary API: Proxy via Client mixin.
|
199
|
+
# * Handful of mixins.
|
200
|
+
#
|
201
|
+
# !SLIDE END
|
202
|
+
|
203
|
+
# !SLIDE
|
204
|
+
# Modules and Classes
|
205
|
+
module ASIR
|
206
|
+
# Reusable constants to avoid unnecessary garbage.
|
207
|
+
EMPTY_ARRAY = [ ].freeze; EMPTY_HASH = { }.freeze; EMPTY_STRING = ''.freeze
|
208
|
+
MODULE_SEP = '::'.freeze; IDENTITY_LAMBDA = lambda { | x | x }
|
209
|
+
end
|
210
|
+
# !SLIDE END
|
211
|
+
Asir = ASIR
|
212
|
+
|
213
|
+
require "asir/version"
|
214
|
+
require 'asir/error'
|
215
|
+
require 'asir/log'
|
216
|
+
require 'asir/initialization'
|
217
|
+
require 'asir/additional_data'
|
218
|
+
require 'asir/object_resolving'
|
219
|
+
require 'asir/identity'
|
220
|
+
require 'asir/code_block'
|
221
|
+
require 'asir/code_more'
|
222
|
+
require 'asir/message'
|
223
|
+
require 'asir/result'
|
224
|
+
require 'asir/client'
|
225
|
+
require 'asir/coder'
|
226
|
+
require 'asir/coder/null'
|
227
|
+
require 'asir/coder/identity'
|
228
|
+
require 'asir/transport'
|
229
|
+
require 'asir/channel'
|
230
|
+
require 'asir/transport/local'
|
231
|
+
|
232
|
+
# !SLIDE
|
233
|
+
# Synopsis
|
234
|
+
#
|
235
|
+
# * Services are easy to abstract away.
|
236
|
+
# * Separation of transport, encoding.
|
237
|
+
#
|
238
|
+
# !SLIDE END
|
239
|
+
|
240
|
+
# !SLIDE
|
241
|
+
# Slide Tools
|
242
|
+
#
|
243
|
+
# * Riterate -- "":http://github.com/kstephens/riterate
|
244
|
+
# * Scarlet -- "":http://github.com/kstephens/scarlet
|
245
|
+
#
|
246
|
+
# !SLIDE END
|
247
|
+
|
248
|
+
# !SLIDE
|
249
|
+
# Appendix
|
250
|
+
#
|
251
|
+
#
|
252
|
+
# !SLIDE END
|
253
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ASIR
|
2
|
+
# !SLIDE
|
3
|
+
# Addtional Data
|
4
|
+
#
|
5
|
+
# Support additional data attached to any object.
|
6
|
+
module AdditionalData
|
7
|
+
def additional_data
|
8
|
+
@additional_data || EMPTY_HASH
|
9
|
+
end
|
10
|
+
def additional_data!
|
11
|
+
@additional_data ||= { }
|
12
|
+
end
|
13
|
+
def additional_data= x
|
14
|
+
@additional_data = x
|
15
|
+
end
|
16
|
+
def [] key
|
17
|
+
@additional_data && @additional_data[key]
|
18
|
+
end
|
19
|
+
def []= key, value
|
20
|
+
(@additional_data ||= { })[key] = value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
# !SLIDE END
|
24
|
+
end
|
25
|
+
|
data/lib/asir/channel.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'asir'
|
2
|
+
require 'asir/retry_behavior'
|
3
|
+
|
4
|
+
module ASIR
|
5
|
+
# Generic I/O Channel abstraction.
|
6
|
+
# Handles stream per Thread and forked child processes.
|
7
|
+
class Channel
|
8
|
+
include Initialization, RetryBehavior
|
9
|
+
|
10
|
+
attr_accessor :on_connect, :on_close, :on_retry, :on_error
|
11
|
+
|
12
|
+
ON_ERROR = lambda do | channel, exc, action, stream |
|
13
|
+
channel.close rescue nil if stream
|
14
|
+
raise exc
|
15
|
+
end
|
16
|
+
ON_CLOSE = lambda do | channel, stream |
|
17
|
+
stream.close
|
18
|
+
end
|
19
|
+
ON_RETRY = lambda do | channel, exc, action |
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize opts = nil
|
23
|
+
@on_close = ON_CLOSE
|
24
|
+
@on_error = ON_ERROR
|
25
|
+
# @on_retry = ON_RETRY
|
26
|
+
self.try_max = 10
|
27
|
+
self.try_sleep = 0.1
|
28
|
+
self.try_sleep_increment = 0.1
|
29
|
+
self.try_sleep_max = 10
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns IO stream for current Thread.
|
34
|
+
# Automatically calls #connect! if stream is created.
|
35
|
+
def stream
|
36
|
+
_streams[self] ||=
|
37
|
+
connect!
|
38
|
+
end
|
39
|
+
|
40
|
+
# Invokes @on_connect.call(self).
|
41
|
+
# On Exception, invokes @on_error.call(self, exc, :connect, nil).
|
42
|
+
def connect!
|
43
|
+
n_try = nil
|
44
|
+
with_retry do | action, data |
|
45
|
+
case action
|
46
|
+
when :try
|
47
|
+
n_try = data
|
48
|
+
@on_connect.call(self)
|
49
|
+
when :retry #, exc
|
50
|
+
exc = data
|
51
|
+
$stderr.puts "RETRY: #{n_try}: ERROR : #{data.inspect}"
|
52
|
+
@on_retry.call(self, exc, :connect) if @on_retry
|
53
|
+
when :failed
|
54
|
+
exc = data
|
55
|
+
$stderr.puts "FAILED: #{n_try}: ERROR : #{data.inspect}"
|
56
|
+
handle_error!(exc, :connect, nil)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Invokes @on_close.call(self, stream).
|
62
|
+
# On Exception, invokes @on_error.call(self, exc, :close, stream).
|
63
|
+
def close
|
64
|
+
if stream = _stream
|
65
|
+
self.stream = nil
|
66
|
+
@on_close.call(self, stream) if @on_close
|
67
|
+
end
|
68
|
+
rescue ::Exception => exc
|
69
|
+
handle_error!(exc, :close, stream)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Yield #stream to block.
|
73
|
+
# On Exception, invokes @on_error.call(self, exc, :with_stream, stream).
|
74
|
+
def with_stream!
|
75
|
+
x = stream
|
76
|
+
begin
|
77
|
+
yield x
|
78
|
+
rescue ::Exception => exc
|
79
|
+
handle_error!(exc, :with_stream, x)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Delegate to actual stream.
|
84
|
+
def method_missing sel, *args, &blk
|
85
|
+
with_stream! do | obj |
|
86
|
+
obj.__send__(sel, *args, &blk)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Dispatches exception and arguments if @on_error is defined.
|
91
|
+
# Otherwise, reraise exception.
|
92
|
+
def handle_error! exc, action, stream
|
93
|
+
if @on_error
|
94
|
+
@on_error.call(self, exc, action, stream)
|
95
|
+
else
|
96
|
+
raise exc
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns a Thread-specific mapping, unique to this process id.
|
101
|
+
# Maps from Channel objects to actual stream.
|
102
|
+
def _streams
|
103
|
+
streams = Thread.current[:'ASIR::Channel._streams'] ||= { }
|
104
|
+
if ! @owning_process ||
|
105
|
+
@owning_process != $$ || # child process?
|
106
|
+
@owning_process > $$ # PIDs wrapped around?
|
107
|
+
@owning_process = $$
|
108
|
+
streams.clear
|
109
|
+
end
|
110
|
+
streams
|
111
|
+
end
|
112
|
+
|
113
|
+
# Returns the stream for this Channel, or nil.
|
114
|
+
def _stream
|
115
|
+
_streams[self]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Sets the stream for this Channel, or nil.
|
119
|
+
def stream= x
|
120
|
+
if x == nil
|
121
|
+
_streams.delete(self)
|
122
|
+
else
|
123
|
+
_streams[self] = x
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end # class Channel
|
128
|
+
end # module
|
129
|
+
|
130
|
+
|