asir 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|