rtext 0.8.0 → 0.9.3
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.
- checksums.yaml +4 -4
- data/CHANGELOG +113 -84
- data/Project.yaml +14 -0
- data/RText_Protocol +47 -4
- data/lib/rtext/context_builder.rb +49 -8
- data/lib/rtext/default_completer.rb +212 -163
- data/lib/rtext/default_service_provider.rb +3 -3
- data/lib/rtext/frontend/connector.rb +122 -55
- data/lib/rtext/frontend/context.rb +12 -12
- data/lib/rtext/instantiator.rb +11 -3
- data/lib/rtext/language.rb +5 -5
- data/lib/rtext/serializer.rb +1 -1
- data/lib/rtext/service.rb +264 -253
- data/lib/rtext/tokenizer.rb +1 -1
- metadata +18 -43
- data/Rakefile +0 -46
- data/test/completer_test.rb +0 -606
- data/test/context_builder_test.rb +0 -948
- data/test/frontend/context_test.rb +0 -205
- data/test/instantiator_test.rb +0 -1691
- data/test/integration/backend.out +0 -13
- data/test/integration/crash_on_request_editor.rb +0 -12
- data/test/integration/ecore_editor.rb +0 -50
- data/test/integration/frontend.log +0 -36049
- data/test/integration/model/invalid_encoding.invenc +0 -2
- data/test/integration/model/test.crash_on_request +0 -18
- data/test/integration/model/test.crashing_backend +0 -18
- data/test/integration/model/test.dont_open_socket +0 -0
- data/test/integration/model/test.invalid_cmd_line +0 -0
- data/test/integration/model/test.not_in_rtext +0 -0
- data/test/integration/model/test_large_with_errors.ect3 +0 -43523
- data/test/integration/model/test_metamodel.ect +0 -24
- data/test/integration/model/test_metamodel2.ect +0 -5
- data/test/integration/model/test_metamodel_error.ect2 +0 -3
- data/test/integration/model/test_metamodel_ok.ect2 +0 -18
- data/test/integration/test.rb +0 -918
- data/test/link_detector_test.rb +0 -287
- data/test/message_helper_test.rb +0 -118
- data/test/rtext_test.rb +0 -11
- data/test/serializer_test.rb +0 -1004
- data/test/tokenizer_test.rb +0 -173
@@ -55,27 +55,27 @@ def filter_lines(lines)
|
|
55
55
|
}
|
56
56
|
end
|
57
57
|
|
58
|
-
# when joining two lines, all whitespace
|
59
|
-
#
|
60
|
-
#
|
61
|
-
# (i.e. with whitespace after the last non-whitespace character)
|
58
|
+
# when joining two lines, all whitespace is preserved in order to simplify the algorithm
|
59
|
+
# whitespace after a backslash is also preserved, only the backslash itself is removed
|
60
|
+
# note that whitespace left of the cursor is important for proper context calculation
|
62
61
|
def join_lines(lines, pos)
|
63
62
|
outlines = []
|
64
63
|
while lines.size > 0
|
65
64
|
outlines << lines.shift
|
66
65
|
while lines.size > 0 &&
|
67
|
-
(outlines.last =~
|
68
|
-
|
69
|
-
|
70
|
-
|
66
|
+
(outlines.last =~ /[,\\]\s*$/ ||
|
67
|
+
# don't join after a child label
|
68
|
+
(outlines.last !~ /^\s*\w+:/ &&
|
69
|
+
(outlines.last =~ /\[\s*$/ ||
|
70
|
+
(lines.first =~ /^\s*\]/ && outlines.last =~ /\[/))))
|
71
71
|
l = lines.shift
|
72
|
+
outlines.last.gsub!("\\","")
|
72
73
|
if lines.size == 0
|
73
|
-
#
|
74
|
+
# the prefix might have whitespace on the
|
74
75
|
# right hand side which is relevant for the position
|
75
|
-
|
76
|
-
pos = outlines.last.size + non_ws_prefix.size
|
76
|
+
pos = outlines.last.size + pos
|
77
77
|
end
|
78
|
-
outlines.last.concat(l
|
78
|
+
outlines.last.concat(l)
|
79
79
|
end
|
80
80
|
end
|
81
81
|
[outlines, pos]
|
data/lib/rtext/instantiator.rb
CHANGED
@@ -247,7 +247,15 @@ class Instantiator
|
|
247
247
|
element.setOrAddGeneric(feature.name, proxy)
|
248
248
|
else
|
249
249
|
begin
|
250
|
-
|
250
|
+
v_value = v.value
|
251
|
+
feature_instance_class = feature.eType.instanceClass
|
252
|
+
if feature_instance_class == String && (v_value.is_a?(Float) || v_value.is_a?(Fixnum))
|
253
|
+
element.setOrAddGeneric(feature.name, v_value.to_s)
|
254
|
+
elsif feature_instance_class == Float && v_value.is_a?(Fixnum)
|
255
|
+
element.setOrAddGeneric(feature.name, v_value.to_f)
|
256
|
+
else
|
257
|
+
element.setOrAddGeneric(feature.name, v_value)
|
258
|
+
end
|
251
259
|
rescue StandardError
|
252
260
|
# backward compatibility for RGen versions not supporting BigDecimal
|
253
261
|
if v.value.is_a?(BigDecimal)
|
@@ -302,9 +310,9 @@ class Instantiator
|
|
302
310
|
elsif feature.eType.is_a?(RGen::ECore::EEnum)
|
303
311
|
[:identifier, :string]
|
304
312
|
else
|
305
|
-
expected = { String => [:string, :identifier],
|
313
|
+
expected = { String => [:string, :identifier, :integer, :float],
|
306
314
|
Integer => [:integer],
|
307
|
-
Float => [:float],
|
315
|
+
Float => [:float, :integer],
|
308
316
|
RGen::MetamodelBuilder::DataTypes::Boolean => [:boolean],
|
309
317
|
Object => [:string, :identifier, :integer, :float, :boolean]
|
310
318
|
}[feature.eType.instanceClass]
|
data/lib/rtext/language.rb
CHANGED
@@ -260,7 +260,7 @@ class Language
|
|
260
260
|
end
|
261
261
|
|
262
262
|
def concrete_types(clazz)
|
263
|
-
([clazz] + clazz.eAllSubTypes).select{|c|
|
263
|
+
([clazz] + clazz.eAllSubTypes).select{|c| c.concrete}
|
264
264
|
end
|
265
265
|
|
266
266
|
def containments_by_target_type(clazz, type)
|
@@ -299,7 +299,7 @@ class Language
|
|
299
299
|
@command_by_class = {}
|
300
300
|
@has_command = {}
|
301
301
|
root_epackage.eAllClasses.each do |c|
|
302
|
-
next
|
302
|
+
next unless c.concrete
|
303
303
|
cmd = command_name_provider.call(c)
|
304
304
|
@command_by_class[c.instanceClass] = cmd
|
305
305
|
@has_command[cmd] = true
|
@@ -307,7 +307,7 @@ class Language
|
|
307
307
|
@class_by_command[clazz] ||= {}
|
308
308
|
containments(c).collect{|r|
|
309
309
|
[r.eType] + r.eType.eAllSubTypes}.flatten.uniq.each do |t|
|
310
|
-
next
|
310
|
+
next unless t.concrete
|
311
311
|
cmw = command_name_provider.call(t)
|
312
312
|
raise "ambiguous command name #{cmw}" if @class_by_command[clazz][cmw]
|
313
313
|
@class_by_command[clazz][cmw] = t.instanceClass
|
@@ -315,7 +315,7 @@ class Language
|
|
315
315
|
end
|
316
316
|
@class_by_command[nil] = {}
|
317
317
|
@root_classes.each do |c|
|
318
|
-
next
|
318
|
+
next unless c.concrete
|
319
319
|
cmw = command_name_provider.call(c)
|
320
320
|
raise "ambiguous command name #{cmw}" if @class_by_command[nil][cmw]
|
321
321
|
@class_by_command[nil][cmw] = c.instanceClass
|
@@ -323,7 +323,7 @@ class Language
|
|
323
323
|
end
|
324
324
|
|
325
325
|
def default_root_classes(root_package)
|
326
|
-
root_epackage.eAllClasses.select{|c|
|
326
|
+
root_epackage.eAllClasses.select{|c| c.concrete &&
|
327
327
|
!c.eAllReferences.any?{|r| r.eOpposite && r.eOpposite.containment}}
|
328
328
|
end
|
329
329
|
|
data/lib/rtext/serializer.rb
CHANGED
@@ -179,7 +179,7 @@ class Serializer
|
|
179
179
|
result << v.to_s
|
180
180
|
end
|
181
181
|
elsif feature.eType.instanceClass == Object
|
182
|
-
if v.to_s =~
|
182
|
+
if v.to_s =~ /^-?\d+(\.\d+)?$|^\w+$|^true$|^false$/
|
183
183
|
result << v.to_s
|
184
184
|
else
|
185
185
|
result << "\"#{v.to_s.gsub("\\","\\\\\\\\").gsub("\"","\\\"").gsub("\n","\\n").
|
data/lib/rtext/service.rb
CHANGED
@@ -1,253 +1,264 @@
|
|
1
|
-
require 'socket'
|
2
|
-
require 'rtext/context_builder'
|
3
|
-
require 'rtext/message_helper'
|
4
|
-
require 'rtext/link_detector'
|
5
|
-
|
6
|
-
# optimization: garbage collect while service is idle
|
7
|
-
|
8
|
-
module RText
|
9
|
-
|
10
|
-
class Service
|
11
|
-
include RText::MessageHelper
|
12
|
-
|
13
|
-
PortRangeStart = 9001
|
14
|
-
PortRangeEnd = 9100
|
15
|
-
|
16
|
-
FlushInterval = 1
|
17
|
-
|
18
|
-
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
@
|
33
|
-
@
|
34
|
-
@
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
sock
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
rescue
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
rescue
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
request_data[sock]
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
@logger.
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
@logger.
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
@logger.warn("
|
132
|
-
false
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
response["
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
1
|
+
require 'socket'
|
2
|
+
require 'rtext/context_builder'
|
3
|
+
require 'rtext/message_helper'
|
4
|
+
require 'rtext/link_detector'
|
5
|
+
|
6
|
+
# optimization: garbage collect while service is idle
|
7
|
+
|
8
|
+
module RText
|
9
|
+
|
10
|
+
class Service
|
11
|
+
include RText::MessageHelper
|
12
|
+
|
13
|
+
PortRangeStart = 9001
|
14
|
+
PortRangeEnd = 9100
|
15
|
+
|
16
|
+
FlushInterval = 1
|
17
|
+
ProtocolVersion = 1
|
18
|
+
|
19
|
+
# Creates an RText backend service. Options:
|
20
|
+
#
|
21
|
+
# :timeout
|
22
|
+
# idle time in seconds after which the service will terminate itself
|
23
|
+
#
|
24
|
+
# :logger
|
25
|
+
# a logger object on which the service will write its logging output
|
26
|
+
#
|
27
|
+
# :on_startup:
|
28
|
+
# a Proc which is called right after the service has started up
|
29
|
+
# can be used to output version information
|
30
|
+
#
|
31
|
+
def initialize(service_provider, options={})
|
32
|
+
@service_provider = service_provider
|
33
|
+
@timeout = options[:timeout] || 60
|
34
|
+
@logger = options[:logger]
|
35
|
+
@on_startup = options[:on_startup]
|
36
|
+
end
|
37
|
+
|
38
|
+
def run
|
39
|
+
server = create_server
|
40
|
+
puts "RText service, listening on port #{server.addr[1]}"
|
41
|
+
@on_startup.call if @on_startup
|
42
|
+
$stdout.flush
|
43
|
+
|
44
|
+
last_access_time = Time.now
|
45
|
+
last_flush_time = Time.now
|
46
|
+
@stop_requested = false
|
47
|
+
sockets = []
|
48
|
+
request_data = {}
|
49
|
+
while !@stop_requested
|
50
|
+
begin
|
51
|
+
sock = server.accept_nonblock
|
52
|
+
sock.sync = true
|
53
|
+
sockets << sock
|
54
|
+
@logger.info "accepted connection" if @logger
|
55
|
+
rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR, Errno::EWOULDBLOCK
|
56
|
+
rescue Exception => e
|
57
|
+
@logger.warn "unexpected exception during socket accept: #{e.class}"
|
58
|
+
end
|
59
|
+
sockets.dup.each do |sock|
|
60
|
+
data = nil
|
61
|
+
begin
|
62
|
+
data = sock.read_nonblock(100000)
|
63
|
+
rescue Errno::EWOULDBLOCK
|
64
|
+
rescue IOError, EOFError, Errno::ECONNRESET, Errno::ECONNABORTED
|
65
|
+
sock.close
|
66
|
+
request_data[sock] = nil
|
67
|
+
sockets.delete(sock)
|
68
|
+
rescue Exception => e
|
69
|
+
# catch Exception to make sure we don't crash due to unexpected exceptions
|
70
|
+
@logger.warn "unexpected exception during socket read: #{e.class}"
|
71
|
+
sock.close
|
72
|
+
request_data[sock] = nil
|
73
|
+
sockets.delete(sock)
|
74
|
+
end
|
75
|
+
if data
|
76
|
+
last_access_time = Time.now
|
77
|
+
request_data[sock] ||= ""
|
78
|
+
request_data[sock].concat(data)
|
79
|
+
while obj = extract_message(request_data[sock])
|
80
|
+
message_received(sock, obj)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
IO.select([server] + sockets, [], [], 1)
|
85
|
+
if Time.now > last_access_time + @timeout
|
86
|
+
@logger.info("RText service, stopping now (timeout)") if @logger
|
87
|
+
break
|
88
|
+
end
|
89
|
+
if Time.now > last_flush_time + FlushInterval
|
90
|
+
$stdout.flush
|
91
|
+
last_flush_time = Time.now
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def message_received(sock, obj)
|
97
|
+
if check_request(obj)
|
98
|
+
request_start = Time.now
|
99
|
+
@logger.debug("request: "+obj.inspect) if @logger
|
100
|
+
response = { "type" => "response", "invocation_id" => obj["invocation_id"] }
|
101
|
+
case obj["command"]
|
102
|
+
when "version"
|
103
|
+
version(sock, obj, response)
|
104
|
+
when "load_model"
|
105
|
+
load_model(sock, obj, response)
|
106
|
+
when "content_complete"
|
107
|
+
content_complete(sock, obj, response)
|
108
|
+
when "link_targets"
|
109
|
+
link_targets(sock, obj, response)
|
110
|
+
when "find_elements"
|
111
|
+
find_elements(sock, obj, response)
|
112
|
+
when "stop"
|
113
|
+
@logger.info("RText service, stopping now (stop requested)") if @logger
|
114
|
+
@stop_requested = true
|
115
|
+
else
|
116
|
+
@logger.warn("unknown command #{obj["command"]}") if @logger
|
117
|
+
response["type"] = "unknown_command_error"
|
118
|
+
response["command"] = obj["command"]
|
119
|
+
end
|
120
|
+
@logger.debug("response: "+truncate_response_for_debug_output(response).inspect) \
|
121
|
+
if response && @logger
|
122
|
+
send_response(sock, response)
|
123
|
+
@logger.info("request complete (#{Time.now-request_start}s)")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def check_request(obj)
|
130
|
+
if obj["type"] != "request"
|
131
|
+
@logger.warn("received message is not a request") if @logger
|
132
|
+
false
|
133
|
+
elsif !obj["invocation_id"].is_a?(Integer)
|
134
|
+
@logger.warn("invalid invocation id #{obj["invocation_id"]}") if @logger
|
135
|
+
false
|
136
|
+
else
|
137
|
+
true
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def version(sock, request, response)
|
142
|
+
response["version"] = ProtocolVersion
|
143
|
+
end
|
144
|
+
|
145
|
+
def load_model(sock, request, response)
|
146
|
+
problems = @service_provider.get_problems(
|
147
|
+
:on_progress => lambda do |frag, work_done, work_overall|
|
148
|
+
work_overall = 1 if work_overall < 1
|
149
|
+
work_done = work_overall if work_done > work_overall
|
150
|
+
work_done = 0 if work_done < 0
|
151
|
+
send_response(sock, {
|
152
|
+
"type" => "progress",
|
153
|
+
"invocation_id" => request["invocation_id"],
|
154
|
+
"percentage" => work_done*100/work_overall
|
155
|
+
})
|
156
|
+
end)
|
157
|
+
total = 0
|
158
|
+
response["problems"] = problems.collect do |fp|
|
159
|
+
{ "file" => fp.file,
|
160
|
+
"problems" => fp.problems.collect do |p|
|
161
|
+
total += 1
|
162
|
+
{ "severity" => "error", "line" => p.line, "message" => p.message }
|
163
|
+
end }
|
164
|
+
end
|
165
|
+
response["total_problems"] = total
|
166
|
+
end
|
167
|
+
|
168
|
+
InsertString = "insert"
|
169
|
+
DisplayString = "display"
|
170
|
+
DescriptionString = "desc"
|
171
|
+
|
172
|
+
def content_complete(sock, request, response)
|
173
|
+
# column numbers start at 1
|
174
|
+
linepos = request["column"]-1
|
175
|
+
lines = request["context"]
|
176
|
+
version = request["version"].to_i
|
177
|
+
lang = @service_provider.language
|
178
|
+
response["options"] = []
|
179
|
+
return unless lang
|
180
|
+
context = ContextBuilder.build_context(lang, lines, linepos)
|
181
|
+
@logger.debug("context element: #{lang.identifier_provider.call(context.element, nil, nil, nil)}") \
|
182
|
+
if context && context.element && @logger
|
183
|
+
if @service_provider.method(:get_completion_options).arity == 1
|
184
|
+
options = @service_provider.get_completion_options(context)
|
185
|
+
else
|
186
|
+
options = @service_provider.get_completion_options(context, version)
|
187
|
+
end
|
188
|
+
response["options"] = options.collect do |o|
|
189
|
+
{ InsertString => o.insert, DisplayString => o.display, DescriptionString => o.description }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def link_targets(sock, request, response)
|
194
|
+
# column numbers start at 1
|
195
|
+
linepos = request["column"]
|
196
|
+
lines = request["context"]
|
197
|
+
lang = @service_provider.language
|
198
|
+
response["targets"] = []
|
199
|
+
return unless lang
|
200
|
+
link_descriptor = RText::LinkDetector.new(lang).detect(lines, linepos)
|
201
|
+
if link_descriptor
|
202
|
+
response["begin_column"] = link_descriptor.scol
|
203
|
+
response["end_column"] = link_descriptor.ecol
|
204
|
+
targets = []
|
205
|
+
@service_provider.get_link_targets(link_descriptor).each do |t|
|
206
|
+
targets << { "file" => t.file, "line" => t.line, "display" => t.display_name }
|
207
|
+
end
|
208
|
+
response["targets"] = targets
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def find_elements(sock, request, response)
|
213
|
+
pattern = request["search_pattern"]
|
214
|
+
total = 0
|
215
|
+
response["elements"] = @service_provider.get_open_element_choices(pattern).collect do |c|
|
216
|
+
total += 1
|
217
|
+
{ "display" => c.display_name, "file" => c.file, "line" => c.line }
|
218
|
+
end
|
219
|
+
response["total_elements"] = total
|
220
|
+
end
|
221
|
+
|
222
|
+
def send_response(sock, response)
|
223
|
+
if response
|
224
|
+
begin
|
225
|
+
sock.write(serialize_message(response))
|
226
|
+
sock.flush
|
227
|
+
# if there is an exception, the next read should shutdown the connection properly
|
228
|
+
rescue IOError, EOFError, Errno::ECONNRESET, Errno::ECONNABORTED
|
229
|
+
rescue Exception => e
|
230
|
+
# catch Exception to make sure we don't crash due to unexpected exceptions
|
231
|
+
@logger.warn "unexpected exception during socket write: #{e.class}"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def truncate_response_for_debug_output(response_hash)
|
237
|
+
result = {}
|
238
|
+
response_hash.each_pair do |k,v|
|
239
|
+
if v.is_a?(Array) && v.size > 100
|
240
|
+
result[k] = v[0..99] + ["<truncated>"]
|
241
|
+
else
|
242
|
+
result[k] = v
|
243
|
+
end
|
244
|
+
end
|
245
|
+
result
|
246
|
+
end
|
247
|
+
|
248
|
+
def create_server
|
249
|
+
port = PortRangeStart
|
250
|
+
serv = nil
|
251
|
+
begin
|
252
|
+
serv = TCPServer.new("127.0.0.1", port)
|
253
|
+
rescue Errno::EADDRINUSE, Errno::EAFNOSUPPORT, Errno::EACCES
|
254
|
+
port += 1
|
255
|
+
retry if port <= PortRangeEnd
|
256
|
+
raise
|
257
|
+
end
|
258
|
+
serv
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
|