infopark-user_io 0.1.1 → 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.
- checksums.yaml +4 -4
- data/lib/infopark/user_io.rb +48 -12
- data/lib/infopark/user_io/version.rb +1 -1
- data/spec/user_io_spec.rb +220 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a723c208d08faafcf16ab1a69f3d1a0258626a2
|
4
|
+
data.tar.gz: 18a653156ce1b8bbbcff53bdc7808924951d813a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8d775c13cdca54d5dcfaf57fea54c6c7384df57297a48025548ef4209ebfc7c4e2b1bf0e6020a4a2e121eb471137973b4612de0d33bcea57585eb9fb954ab8f
|
7
|
+
data.tar.gz: e9977f5ff38901242d98fe5c84ef2ee18c321c8bf1d20d6d6d21d8ad5741dda401c045d1a0f82df15d508ad0528ea42a48b1e88c80cfda89bb843219e0528fb9
|
data/lib/infopark/user_io.rb
CHANGED
@@ -65,6 +65,25 @@ class UserIO
|
|
65
65
|
tell_line(lines.last, newline: newline, **line_options)
|
66
66
|
end
|
67
67
|
|
68
|
+
def tell_pty_stream(stream, **color_options)
|
69
|
+
color_prefix, color_postfix = compute_color(**color_options)
|
70
|
+
write_raw(output_prefix) unless line_pending?
|
71
|
+
write_raw(color_prefix)
|
72
|
+
nl_pending = false
|
73
|
+
uncolored_prefix = "#{color_postfix}#{output_prefix}#{color_prefix}"
|
74
|
+
until stream.eof?
|
75
|
+
chunk = stream.read_nonblock(100)
|
76
|
+
next if chunk.empty?
|
77
|
+
write_raw("\n#{uncolored_prefix}") if nl_pending
|
78
|
+
chunk.chop! if nl_pending = chunk.end_with?("\n")
|
79
|
+
chunk.gsub!(/([\r\n])/, "\\1#{uncolored_prefix}")
|
80
|
+
write_raw(chunk)
|
81
|
+
end
|
82
|
+
write_raw("\n") if nl_pending
|
83
|
+
write_raw(color_postfix)
|
84
|
+
line_pending!(false)
|
85
|
+
end
|
86
|
+
|
68
87
|
def warn(*text)
|
69
88
|
tell(*text, color: :yellow, bright: true)
|
70
89
|
end
|
@@ -119,14 +138,14 @@ class UserIO
|
|
119
138
|
|
120
139
|
def background_other_threads
|
121
140
|
unless @foreground_thread
|
122
|
-
@
|
141
|
+
@background_data = []
|
123
142
|
@foreground_thread = Thread.current
|
124
143
|
end
|
125
144
|
end
|
126
145
|
|
127
146
|
def foreground
|
128
147
|
if @foreground_thread
|
129
|
-
@
|
148
|
+
@background_data.each(&STDOUT.method(:write))
|
130
149
|
@foreground_thread = nil
|
131
150
|
# take over line_pending from background
|
132
151
|
@line_pending[false] = @line_pending[true]
|
@@ -223,22 +242,39 @@ class UserIO
|
|
223
242
|
end
|
224
243
|
|
225
244
|
def tell_line(line, newline: true, prefix: true, **color_options)
|
226
|
-
|
227
|
-
|
228
|
-
line_postfix = text_color(color: :none, bright: false)
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
prefix = false if @line_pending[background?]
|
245
|
+
line_prefix, line_postfix = compute_color(**color_options)
|
246
|
+
prefix = false if line_pending?
|
233
247
|
|
234
248
|
out_line = "#{output_prefix if prefix}#{line_prefix}#{line}#{line_postfix}#{"\n" if newline}"
|
249
|
+
write_raw(out_line)
|
250
|
+
|
251
|
+
line_pending!(!newline)
|
252
|
+
end
|
253
|
+
|
254
|
+
def write_raw(bytes)
|
235
255
|
if background?
|
236
|
-
@
|
256
|
+
@background_data << bytes
|
237
257
|
else
|
238
|
-
STDOUT.write(
|
258
|
+
STDOUT.write(bytes)
|
239
259
|
end
|
260
|
+
end
|
240
261
|
|
241
|
-
|
262
|
+
def line_pending?
|
263
|
+
@line_pending[background?]
|
264
|
+
end
|
265
|
+
|
266
|
+
def line_pending!(value)
|
267
|
+
@line_pending[background?] = value
|
268
|
+
end
|
269
|
+
|
270
|
+
def compute_color(**options)
|
271
|
+
if tty?
|
272
|
+
if prefix = text_color(**options)
|
273
|
+
# TODO matching annihilators for options
|
274
|
+
postfix = text_color(color: :none, bright: false)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
[prefix, postfix]
|
242
278
|
end
|
243
279
|
|
244
280
|
def control_sequence(*parameters, function)
|
data/spec/user_io_spec.rb
CHANGED
@@ -130,4 +130,224 @@ RSpec.describe ::Infopark::UserIO do
|
|
130
130
|
end
|
131
131
|
end
|
132
132
|
end
|
133
|
+
|
134
|
+
describe "#tell_pty_stream" do
|
135
|
+
let(:color_options) { {} }
|
136
|
+
let(:stream) { instance_double(IO) }
|
137
|
+
let(:data) { "test data" }
|
138
|
+
|
139
|
+
subject(:tell) { user_io.tell_pty_stream(stream, **color_options) }
|
140
|
+
|
141
|
+
before do
|
142
|
+
chunks = Array(data)
|
143
|
+
allow(stream).to receive(:eof?).and_return(*[false] * chunks.size, true)
|
144
|
+
allow(stream).to receive(:read_nonblock).and_return(*chunks)
|
145
|
+
RSpec::Mocks.space.proxy_for($stdout).reset
|
146
|
+
allow($stdout).to receive(:write).with(nil)
|
147
|
+
end
|
148
|
+
|
149
|
+
it "tells all data from stream in non blocking chunks" do
|
150
|
+
expect(stream).to receive(:eof?).and_return(false, false, false, true)
|
151
|
+
expect(stream).to receive(:read_nonblock).with(100).
|
152
|
+
and_return("first\nchunk", "second chunk", "\nlast chunk")
|
153
|
+
expect($stdout).to receive(:write).with("first\nchunk")
|
154
|
+
expect($stdout).to receive(:write).with("second chunk")
|
155
|
+
expect($stdout).to receive(:write).with("\nlast chunk")
|
156
|
+
tell
|
157
|
+
end
|
158
|
+
|
159
|
+
context "with color" do
|
160
|
+
let(:color_options) { {color: :yellow} }
|
161
|
+
|
162
|
+
it "colorizes the output" do
|
163
|
+
expect($stdout).to receive(:write).with("\e[33m").ordered
|
164
|
+
expect($stdout).to receive(:write).with("test data").ordered
|
165
|
+
expect($stdout).to receive(:write).with("\e[22;39m").ordered
|
166
|
+
tell
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context "with output_prefix" do
|
171
|
+
let(:options) { {output_prefix: "the prefix"} }
|
172
|
+
|
173
|
+
it "prefixes the output" do
|
174
|
+
expect($stdout).to receive(:write).with("[the prefix] ").ordered
|
175
|
+
expect($stdout).to receive(:write).with("test data").ordered
|
176
|
+
tell
|
177
|
+
end
|
178
|
+
|
179
|
+
context "with color" do
|
180
|
+
let(:color_options) { {color: :yellow} }
|
181
|
+
|
182
|
+
it "does not colorize the prefix" do
|
183
|
+
expect($stdout).to receive(:write).with("[the prefix] ").ordered
|
184
|
+
expect($stdout).to receive(:write).with("\e[33m").ordered
|
185
|
+
expect($stdout).to receive(:write).with("test data").ordered
|
186
|
+
expect($stdout).to receive(:write).with("\e[22;39m").ordered
|
187
|
+
tell
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context "when stream contains carriage return" do
|
192
|
+
let(:data) { "some\rdata\rwith\rCRs" }
|
193
|
+
|
194
|
+
it "writes the prefix right after the CR" do
|
195
|
+
expect($stdout).to receive(:write).with("[the prefix] ").ordered
|
196
|
+
expect($stdout).to receive(:write).
|
197
|
+
with("some\r[the prefix] data\r[the prefix] with\r[the prefix] CRs").ordered
|
198
|
+
tell
|
199
|
+
end
|
200
|
+
|
201
|
+
context "with color" do
|
202
|
+
let(:color_options) { {color: :yellow} }
|
203
|
+
|
204
|
+
it "uncolorizes the prefix" do
|
205
|
+
expect($stdout).to receive(:write).with("[the prefix] ").ordered
|
206
|
+
expect($stdout).to receive(:write).with("\e[33m").ordered
|
207
|
+
expect($stdout).to receive(:write).with(
|
208
|
+
"some\r"\
|
209
|
+
"\e[22;39m" "[the prefix] " "\e[33m" "data\r"\
|
210
|
+
"\e[22;39m" "[the prefix] " "\e[33m" "with\r"\
|
211
|
+
"\e[22;39m" "[the prefix] " "\e[33m" "CRs"
|
212
|
+
).ordered
|
213
|
+
expect($stdout).to receive(:write).with("\e[22;39m").ordered
|
214
|
+
tell
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context "when stream contains newline" do
|
220
|
+
let(:data) { "some\ndata\nwith\nNLs" }
|
221
|
+
|
222
|
+
it "writes the prefix right after the NL" do
|
223
|
+
expect($stdout).to receive(:write).with("[the prefix] ").ordered
|
224
|
+
expect($stdout).to receive(:write).
|
225
|
+
with("some\n[the prefix] data\n[the prefix] with\n[the prefix] NLs")
|
226
|
+
tell
|
227
|
+
end
|
228
|
+
|
229
|
+
context "with color" do
|
230
|
+
let(:color_options) { {color: :yellow} }
|
231
|
+
|
232
|
+
it "uncolorizes the prefix" do
|
233
|
+
expect($stdout).to receive(:write).with("[the prefix] ").ordered
|
234
|
+
expect($stdout).to receive(:write).with("\e[33m").ordered
|
235
|
+
expect($stdout).to receive(:write).with(
|
236
|
+
"some\n"\
|
237
|
+
"\e[22;39m" "[the prefix] " "\e[33m" "data\n"\
|
238
|
+
"\e[22;39m" "[the prefix] " "\e[33m" "with\n"\
|
239
|
+
"\e[22;39m" "[the prefix] " "\e[33m" "NLs"
|
240
|
+
).ordered
|
241
|
+
expect($stdout).to receive(:write).with("\e[22;39m").ordered
|
242
|
+
tell
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
context "when stream ends with newline" do
|
247
|
+
# includes an empty chunk to verify, that they don't consume the pending NL
|
248
|
+
let(:data) { ["some\n", "data\n", "with\n", "", "NLs\n", ""] }
|
249
|
+
|
250
|
+
it "does not write prefix after the last newline" do
|
251
|
+
expect($stdout).to receive(:write).with("[the prefix] ").ordered
|
252
|
+
expect($stdout).to receive(:write).with("some").ordered
|
253
|
+
expect($stdout).to receive(:write).with("\n" "[the prefix] ").ordered
|
254
|
+
expect($stdout).to receive(:write).with("data").ordered
|
255
|
+
expect($stdout).to receive(:write).with("\n" "[the prefix] ").ordered
|
256
|
+
expect($stdout).to receive(:write).with("with").ordered
|
257
|
+
expect($stdout).to receive(:write).with("\n" "[the prefix] ").ordered
|
258
|
+
expect($stdout).to receive(:write).with("NLs").ordered
|
259
|
+
expect($stdout).to receive(:write).with("\n").ordered
|
260
|
+
tell
|
261
|
+
end
|
262
|
+
|
263
|
+
context "with color" do
|
264
|
+
let(:color_options) { {color: :yellow} }
|
265
|
+
|
266
|
+
it "uncolorizes the prefix" do
|
267
|
+
expect($stdout).to receive(:write).with("[the prefix] ").ordered
|
268
|
+
expect($stdout).to receive(:write).with("\e[33m").ordered
|
269
|
+
expect($stdout).to receive(:write).with("some").ordered
|
270
|
+
expect($stdout).to receive(:write).
|
271
|
+
with("\n" "\e[22;39m" "[the prefix] " "\e[33m").ordered
|
272
|
+
expect($stdout).to receive(:write).with("data").ordered
|
273
|
+
expect($stdout).to receive(:write).
|
274
|
+
with("\n" "\e[22;39m" "[the prefix] " "\e[33m").ordered
|
275
|
+
expect($stdout).to receive(:write).with("with").ordered
|
276
|
+
expect($stdout).to receive(:write).
|
277
|
+
with("\n" "\e[22;39m" "[the prefix] " "\e[33m").ordered
|
278
|
+
expect($stdout).to receive(:write).with("NLs").ordered
|
279
|
+
expect($stdout).to receive(:write).with("\n").ordered
|
280
|
+
expect($stdout).to receive(:write).with("\e[22;39m").ordered
|
281
|
+
tell
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
context "when data does not end with newline" do
|
288
|
+
let(:data) { "foo" }
|
289
|
+
|
290
|
+
it "writes prefix on next output nevertheless" do
|
291
|
+
expect($stdout).to receive(:write).with("[the prefix] ").ordered
|
292
|
+
expect($stdout).to receive(:write).with("foo").ordered
|
293
|
+
tell
|
294
|
+
expect($stdout).to receive(:write).with("[the prefix] next\n")
|
295
|
+
user_io.tell("next")
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
context "when no newline was printed before" do
|
300
|
+
before do
|
301
|
+
expect($stdout).to receive(:write).with("[the prefix] no newline").ordered
|
302
|
+
user_io.tell("no newline", newline: false)
|
303
|
+
end
|
304
|
+
|
305
|
+
it "does not prepend prefix" do
|
306
|
+
expect($stdout).to receive(:write).with("test data").ordered
|
307
|
+
tell
|
308
|
+
end
|
309
|
+
|
310
|
+
it "prints prefix on following output" do
|
311
|
+
expect($stdout).to receive(:write).with("test data").ordered
|
312
|
+
tell
|
313
|
+
expect($stdout).to receive(:write).with("[the prefix] next\n")
|
314
|
+
user_io.tell("next")
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
context "when in background" do
|
320
|
+
let(:color_options) { {color: :yellow} }
|
321
|
+
let(:options) { {output_prefix: "foo"} }
|
322
|
+
let(:data) { ["data\n", "in\nchunks", "", "yo\n", ""] }
|
323
|
+
|
324
|
+
let!(:foreground_thread) do
|
325
|
+
@finished = false
|
326
|
+
Thread.new do
|
327
|
+
user_io.background_other_threads
|
328
|
+
sleep 0.1 until @finished
|
329
|
+
user_io.foreground
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
after { foreground_thread.kill.join }
|
334
|
+
|
335
|
+
it "holds back the output until coming back to foreground" do
|
336
|
+
expect($stdout).to_not receive(:write)
|
337
|
+
tell
|
338
|
+
RSpec::Mocks.space.proxy_for($stdout).reset
|
339
|
+
expect($stdout).to receive(:write).with("[foo] ").ordered
|
340
|
+
expect($stdout).to receive(:write).with("\e[33m").ordered
|
341
|
+
expect($stdout).to receive(:write).with("data").ordered
|
342
|
+
expect($stdout).to receive(:write).with("\n" "\e[22;39m" "[foo] " "\e[33m").ordered
|
343
|
+
expect($stdout).to receive(:write).
|
344
|
+
with("in\n" "\e[22;39m" "[foo] " "\e[33m" "chunks").ordered
|
345
|
+
expect($stdout).to receive(:write).with("yo").ordered
|
346
|
+
expect($stdout).to receive(:write).with("\n").ordered
|
347
|
+
expect($stdout).to receive(:write).with("\e[22;39m").ordered
|
348
|
+
@finished = true
|
349
|
+
foreground_thread.join
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
133
353
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: infopark-user_io
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tilo Prütz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-07-
|
11
|
+
date: 2017-07-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|