infopark-user_io 1.3.0 → 1.4.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/global.rb +1 -1
- data/lib/infopark/user_io/version.rb +1 -1
- data/lib/infopark/user_io.rb +112 -111
- data/spec/user_io_spec.rb +175 -28
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b20bdfdfb2bd29180da2445be83e82295a71b4a30276450351c08ef114ed9da5
|
4
|
+
data.tar.gz: cfe242fc1e3a48b1c0e2b7d0ab40d8663178b6425f9e8b4215c13a67bef85b29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d75731717e150e66512d1aecab2193938aa159cb276eaa9cae1abbae05753d88559e01b57216f210dfca8f3163a5cb5bee699593e1d5f740e51e7e95c6c3037
|
7
|
+
data.tar.gz: cb512e8b47045e191bcda99ce905b0c5e8a8dffefbd9f489ee0b543841bd75cef8e473e31caa9eddb5caebd97a67c7b5b85a9e91b1ef032894fa443e1ecd9bc2
|
data/lib/infopark/user_io.rb
CHANGED
@@ -76,88 +76,39 @@ module Infopark
|
|
76
76
|
@line_pending = {}
|
77
77
|
end
|
78
78
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
lines[0...-1].each {|line| tell_line(line, **line_options) }
|
83
|
-
tell_line(lines.last, newline:, **line_options)
|
84
|
-
end
|
85
|
-
|
86
|
-
def tell_pty_stream(stream, **color_options)
|
87
|
-
color_prefix, color_postfix = compute_color(**color_options)
|
88
|
-
write_raw(output_prefix) unless line_pending?
|
89
|
-
write_raw(color_prefix)
|
90
|
-
nl_pending = false
|
91
|
-
uncolored_prefix = "#{color_postfix}#{output_prefix}#{color_prefix}"
|
92
|
-
until stream.eof?
|
93
|
-
chunk = stream.read_nonblock(100)
|
94
|
-
next if chunk.empty?
|
95
|
-
|
96
|
-
write_raw("\n#{uncolored_prefix}") if nl_pending
|
97
|
-
chunk.chop! if (nl_pending = chunk.end_with?("\n"))
|
98
|
-
chunk.gsub!(/([\r\n])/, "\\1#{uncolored_prefix}")
|
99
|
-
write_raw(chunk)
|
100
|
-
end
|
101
|
-
write_raw("\n") if nl_pending
|
102
|
-
write_raw(color_postfix)
|
103
|
-
line_pending!(false)
|
104
|
-
end
|
105
|
-
|
106
|
-
def warn(*text)
|
107
|
-
tell(*text, color: :yellow, bright: true)
|
108
|
-
end
|
109
|
-
|
110
|
-
def tell_error(e, **options)
|
111
|
-
tell(e, **options, color: :red, bright: true)
|
112
|
-
tell(e.backtrace, **options, color: :red) if Exception === e
|
79
|
+
def <<(msg)
|
80
|
+
tell(msg.chomp, newline: msg.end_with?("\n"))
|
113
81
|
end
|
114
82
|
|
115
|
-
def acknowledge(*
|
116
|
-
tell("-" * 80)
|
117
|
-
tell(*
|
118
|
-
tell("-" * 80)
|
119
|
-
tell("Please press ENTER to continue.")
|
83
|
+
def acknowledge(*texts, **options)
|
84
|
+
tell("-" * 80, **options)
|
85
|
+
tell(*texts, **options, color: :cyan, bright: true)
|
86
|
+
tell("-" * 80, **options)
|
87
|
+
tell("Please press ENTER to continue.", **options)
|
120
88
|
read_line
|
89
|
+
nil
|
121
90
|
end
|
122
91
|
|
123
|
-
def ask(*
|
92
|
+
def ask(*texts, default: nil, expected: "yes", **tell_options)
|
124
93
|
# TODO
|
125
94
|
# - implementation error if default not boolean or nil
|
126
95
|
# - implementation error if expected not "yes" or "no"
|
127
|
-
tell("-" * 80)
|
128
|
-
tell(*
|
129
|
-
tell("-" * 80)
|
96
|
+
tell("-" * 80, **tell_options)
|
97
|
+
tell(*texts, **tell_options, color: :cyan, bright: true)
|
98
|
+
tell("-" * 80, **tell_options)
|
130
99
|
default_answer = default ? "yes" : "no" unless default.nil?
|
131
|
-
tell("(yes/no) #{default_answer && "[#{default_answer}] "}> ", newline: false)
|
132
|
-
until %w(yes no).include?(answer = read_line.strip.downcase)
|
100
|
+
tell("(yes/no) #{default_answer && "[#{default_answer}] "}> ", **tell_options, newline: false)
|
101
|
+
until %w(yes no).include?((answer = read_line.strip.downcase))
|
133
102
|
if answer.empty?
|
134
103
|
answer = default_answer
|
135
104
|
break
|
136
105
|
end
|
137
|
-
tell("I couldn't understand “#{answer}”.", newline: false, color: :red, bright: true)
|
138
|
-
tell(" > ", newline: false)
|
106
|
+
tell("I couldn't understand “#{answer}”.", **tell_options, newline: false, color: :red, bright: true)
|
107
|
+
tell(" > ", **tell_options, newline: false)
|
139
108
|
end
|
140
109
|
answer == expected
|
141
110
|
end
|
142
111
|
|
143
|
-
def listen(prompt = nil, **options)
|
144
|
-
prompt << " " if prompt
|
145
|
-
tell("#{prompt}> ", **options, newline: false)
|
146
|
-
read_line.strip
|
147
|
-
end
|
148
|
-
|
149
|
-
def confirm(*text)
|
150
|
-
ask(*text) or raise(Aborted)
|
151
|
-
end
|
152
|
-
|
153
|
-
def new_progress(label)
|
154
|
-
Progress.new(label, self)
|
155
|
-
end
|
156
|
-
|
157
|
-
def start_progress(label)
|
158
|
-
new_progress(label).tap(&:start)
|
159
|
-
end
|
160
|
-
|
161
112
|
def background_other_threads
|
162
113
|
return if @foreground_thread
|
163
114
|
|
@@ -165,22 +116,8 @@ module Infopark
|
|
165
116
|
@foreground_thread = Thread.current
|
166
117
|
end
|
167
118
|
|
168
|
-
def
|
169
|
-
|
170
|
-
|
171
|
-
@background_data.each(&$stdout.method(:write))
|
172
|
-
@foreground_thread = nil
|
173
|
-
# take over line_pending from background
|
174
|
-
@line_pending[false] = @line_pending[true]
|
175
|
-
@line_pending[true] = false
|
176
|
-
end
|
177
|
-
|
178
|
-
def <<(msg)
|
179
|
-
tell(msg.chomp, newline: msg.end_with?("\n"))
|
180
|
-
end
|
181
|
-
|
182
|
-
def tty?
|
183
|
-
$stdout.tty?
|
119
|
+
def confirm(*text, **options)
|
120
|
+
ask(*text, **options) or raise(Aborted)
|
184
121
|
end
|
185
122
|
|
186
123
|
def edit_file(kind_of_data, filename = nil, template: nil)
|
@@ -200,6 +137,26 @@ module Infopark
|
|
200
137
|
File.read(filename)
|
201
138
|
end
|
202
139
|
|
140
|
+
def foreground
|
141
|
+
return unless @foreground_thread
|
142
|
+
|
143
|
+
@background_data.each(&$stdout.method(:write))
|
144
|
+
@foreground_thread = nil
|
145
|
+
# take over line_pending from background
|
146
|
+
@line_pending[false] = @line_pending[true]
|
147
|
+
@line_pending[true] = false
|
148
|
+
end
|
149
|
+
|
150
|
+
def listen(prompt = nil, **options)
|
151
|
+
prompt << " " if prompt
|
152
|
+
tell("#{prompt}> ", **options, newline: false)
|
153
|
+
read_line.strip
|
154
|
+
end
|
155
|
+
|
156
|
+
def new_progress(label)
|
157
|
+
Progress.new(label, self)
|
158
|
+
end
|
159
|
+
|
203
160
|
def select(description, items, item_describer: :to_s, default: nil)
|
204
161
|
return if items.empty?
|
205
162
|
|
@@ -247,50 +204,54 @@ module Infopark
|
|
247
204
|
choice
|
248
205
|
end
|
249
206
|
|
250
|
-
|
251
|
-
|
252
|
-
def background?
|
253
|
-
!!@foreground_thread && @foreground_thread != Thread.current
|
207
|
+
def start_progress(label)
|
208
|
+
new_progress(label).tap(&:start)
|
254
209
|
end
|
255
210
|
|
256
|
-
def
|
257
|
-
|
258
|
-
end
|
211
|
+
def tell(*texts, newline: true, **line_options)
|
212
|
+
lines = texts.flatten.map {|text| text.to_s.split("\n", -1) }.flatten
|
259
213
|
|
260
|
-
|
261
|
-
|
214
|
+
lines[0...-1].each {|line| tell_line(line, **line_options) }
|
215
|
+
tell_line(lines.last, newline:, **line_options)
|
262
216
|
end
|
263
217
|
|
264
|
-
def
|
265
|
-
|
266
|
-
|
267
|
-
$stdin.gets.chomp
|
218
|
+
def tell_error(e, **options)
|
219
|
+
tell(e, **options, color: :red, bright: true)
|
220
|
+
tell(e.backtrace, **options, color: :red) if Exception === e
|
268
221
|
end
|
269
222
|
|
270
|
-
def
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
223
|
+
def tell_pty_stream(stream, **color_options)
|
224
|
+
color_prefix, color_postfix = compute_color(**color_options)
|
225
|
+
write_raw(output_prefix) unless line_pending?
|
226
|
+
write_raw(color_prefix)
|
227
|
+
nl_pending = false
|
228
|
+
uncolored_prefix = "#{color_postfix}#{output_prefix}#{color_prefix}"
|
229
|
+
until stream.eof?
|
230
|
+
chunk = stream.read_nonblock(100)
|
231
|
+
next if chunk.empty?
|
276
232
|
|
277
|
-
|
233
|
+
write_raw("\n#{uncolored_prefix}") if nl_pending
|
234
|
+
chunk.chop! if (nl_pending = chunk.end_with?("\n"))
|
235
|
+
chunk.gsub!(/([\r\n])/, "\\1#{uncolored_prefix}")
|
236
|
+
write_raw(chunk)
|
237
|
+
end
|
238
|
+
write_raw("\n") if nl_pending
|
239
|
+
write_raw(color_postfix)
|
240
|
+
line_pending!(false)
|
278
241
|
end
|
279
242
|
|
280
|
-
def
|
281
|
-
|
282
|
-
@background_data << bytes
|
283
|
-
else
|
284
|
-
$stdout.write(bytes)
|
285
|
-
end
|
243
|
+
def tty?
|
244
|
+
$stdout.tty?
|
286
245
|
end
|
287
246
|
|
288
|
-
def
|
289
|
-
|
247
|
+
def warn(*texts, **options)
|
248
|
+
tell(*texts, **options, color: :yellow, bright: true)
|
290
249
|
end
|
291
250
|
|
292
|
-
|
293
|
-
|
251
|
+
private
|
252
|
+
|
253
|
+
def background?
|
254
|
+
!!@foreground_thread && @foreground_thread != Thread.current
|
294
255
|
end
|
295
256
|
|
296
257
|
def compute_color(**options)
|
@@ -305,11 +266,39 @@ module Infopark
|
|
305
266
|
"\033[#{parameters.join(';')}#{function}"
|
306
267
|
end
|
307
268
|
|
269
|
+
def line_pending?
|
270
|
+
@line_pending[background?]
|
271
|
+
end
|
272
|
+
|
273
|
+
def line_pending!(value)
|
274
|
+
@line_pending[background?] = value
|
275
|
+
end
|
276
|
+
|
277
|
+
def output_prefix
|
278
|
+
@output_prefix || @output_prefix_proc&.call
|
279
|
+
end
|
280
|
+
|
281
|
+
def read_line
|
282
|
+
wait_for_foreground if background?
|
283
|
+
@line_pending[false] = false
|
284
|
+
$stdin.gets.chomp
|
285
|
+
end
|
286
|
+
|
308
287
|
# SGR: Select Graphic Rendition … far too long for a function name ;)
|
309
288
|
def sgr_sequence(*parameters)
|
310
289
|
control_sequence(*parameters, :m)
|
311
290
|
end
|
312
291
|
|
292
|
+
def tell_line(line, newline: true, prefix: true, **color_options)
|
293
|
+
line_prefix, line_postfix = compute_color(**color_options)
|
294
|
+
prefix = false if line_pending?
|
295
|
+
|
296
|
+
out_line = "#{output_prefix if prefix}#{line_prefix}#{line}#{line_postfix}#{"\n" if newline}"
|
297
|
+
write_raw(out_line)
|
298
|
+
|
299
|
+
line_pending!(!newline)
|
300
|
+
end
|
301
|
+
|
313
302
|
def text_color(color: nil, bright: nil, faint: nil, italic: nil, underline: nil)
|
314
303
|
return if color.nil? && bright.nil?
|
315
304
|
|
@@ -348,6 +337,18 @@ module Infopark
|
|
348
337
|
end
|
349
338
|
sgr_sequence(*sequence)
|
350
339
|
end
|
340
|
+
|
341
|
+
def wait_for_foreground
|
342
|
+
sleep(0.1) while background?
|
343
|
+
end
|
344
|
+
|
345
|
+
def write_raw(bytes)
|
346
|
+
if background?
|
347
|
+
@background_data << bytes
|
348
|
+
else
|
349
|
+
$stdout.write(bytes)
|
350
|
+
end
|
351
|
+
end
|
351
352
|
end
|
352
353
|
end
|
353
354
|
|
data/spec/user_io_spec.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "securerandom"
|
4
|
+
|
3
5
|
module Infopark
|
4
6
|
RSpec.describe(UserIO) do
|
5
7
|
let(:options) { {} }
|
@@ -28,19 +30,35 @@ module Infopark
|
|
28
30
|
end
|
29
31
|
|
30
32
|
describe "#acknowledge" do
|
31
|
-
|
33
|
+
subject(:acknowledge) { user_io.acknowledge(*ack_texts, **ack_options) }
|
32
34
|
|
33
|
-
|
35
|
+
before do
|
36
|
+
allow($stdin).to(receive(:gets).and_return("\n"))
|
37
|
+
allow(user_io).to(receive(:tell))
|
38
|
+
end
|
34
39
|
|
35
|
-
|
40
|
+
let(:ack_options) do
|
41
|
+
[
|
42
|
+
{newline: true},
|
43
|
+
{prefix: "foo", newline: false},
|
44
|
+
{},
|
45
|
+
].sample
|
46
|
+
end
|
47
|
+
let(:ack_texts) do
|
48
|
+
[
|
49
|
+
["Some important statement."],
|
50
|
+
["Some", "important", "statement!"],
|
51
|
+
[],
|
52
|
+
].sample
|
53
|
+
end
|
36
54
|
|
37
|
-
it "
|
38
|
-
expect(
|
55
|
+
it "tells the message (colorized)" do
|
56
|
+
expect(user_io).to(receive(:tell).with(*ack_texts, **ack_options, color: :cyan, bright: true))
|
39
57
|
acknowledge
|
40
58
|
end
|
41
59
|
|
42
60
|
it "asks for pressing “Enter”" do
|
43
|
-
expect(
|
61
|
+
expect(user_io).to(receive(:tell).with("Please press ENTER to continue.", **ack_options))
|
44
62
|
acknowledge
|
45
63
|
end
|
46
64
|
|
@@ -48,21 +66,73 @@ module Infopark
|
|
48
66
|
expect($stdin).to(receive(:gets).and_return("\n"))
|
49
67
|
acknowledge
|
50
68
|
end
|
69
|
+
|
70
|
+
it "returns nil" do
|
71
|
+
expect(acknowledge).to(be_nil)
|
72
|
+
end
|
51
73
|
end
|
52
74
|
|
53
75
|
describe "#ask" do
|
54
|
-
|
76
|
+
subject(:ask) { user_io.ask(*ask_texts, **ask_options.merge(tell_options)) }
|
55
77
|
|
78
|
+
before do
|
79
|
+
allow($stdin).to(receive(:gets).and_return("#{answer}\n"))
|
80
|
+
allow(user_io).to(receive(:tell))
|
81
|
+
end
|
82
|
+
|
83
|
+
let(:answer) { "yes" }
|
84
|
+
let(:ask_texts) do
|
85
|
+
[
|
86
|
+
["do you want to?"],
|
87
|
+
["do", "you", "want", "to?"],
|
88
|
+
[],
|
89
|
+
].sample
|
90
|
+
end
|
56
91
|
let(:ask_options) { {} }
|
57
|
-
let(:
|
92
|
+
let(:tell_options) do
|
93
|
+
[
|
94
|
+
{italic: true},
|
95
|
+
{prefix: "foo", bold: false},
|
96
|
+
{},
|
97
|
+
].sample
|
98
|
+
end
|
58
99
|
|
59
|
-
|
100
|
+
shared_examples_for "any question" do |invert_answer: false|
|
101
|
+
it "tells the message (colorized)" do
|
102
|
+
expect(user_io).to(receive(:tell).with(*ask_texts, **tell_options, color: :cyan, bright: true))
|
103
|
+
ask
|
104
|
+
end
|
105
|
+
|
106
|
+
it "requests input" do
|
107
|
+
expect($stdin).to(receive(:gets).and_return("\n"))
|
108
|
+
ask
|
109
|
+
end
|
110
|
+
|
111
|
+
context "when answer is “yes”" do
|
112
|
+
let(:answer) { "yes" }
|
113
|
+
|
114
|
+
it { is_expected.to(be(invert_answer ? false : true))}
|
115
|
+
end
|
116
|
+
|
117
|
+
context "when answer is “no”" do
|
118
|
+
let(:answer) { "no" }
|
119
|
+
|
120
|
+
it { is_expected.to(be(invert_answer ? true : false))}
|
121
|
+
end
|
60
122
|
|
61
|
-
shared_examples_for "any question" do
|
62
123
|
# TODO
|
63
124
|
# it_behaves_like "handling valid answer"
|
64
125
|
# it_behaves_like "handling invalid input"
|
65
|
-
|
126
|
+
end
|
127
|
+
|
128
|
+
it "asks for answer" do
|
129
|
+
expect(user_io).to(receive(:tell).with("(yes/no) > ", **tell_options, newline: false))
|
130
|
+
ask
|
131
|
+
end
|
132
|
+
|
133
|
+
it "returns “false” on empty input" do
|
134
|
+
expect($stdin).to(receive(:gets).and_return("\n"))
|
135
|
+
expect(ask).to(be(false))
|
66
136
|
end
|
67
137
|
|
68
138
|
context "with default" do
|
@@ -72,7 +142,7 @@ module Infopark
|
|
72
142
|
let(:default_value) { true }
|
73
143
|
|
74
144
|
it "presents default answer “yes”" do
|
75
|
-
expect(
|
145
|
+
expect(user_io).to(receive(:tell).with("(yes/no) [yes] > ", **tell_options, newline: false))
|
76
146
|
ask
|
77
147
|
end
|
78
148
|
|
@@ -88,7 +158,7 @@ module Infopark
|
|
88
158
|
let(:default_value) { false }
|
89
159
|
|
90
160
|
it "presents default answer “no”" do
|
91
|
-
expect(
|
161
|
+
expect(user_io).to(receive(:tell).with("(yes/no) [no] > ", **tell_options, newline: false))
|
92
162
|
ask
|
93
163
|
end
|
94
164
|
|
@@ -127,7 +197,7 @@ module Infopark
|
|
127
197
|
context "“no”" do
|
128
198
|
let(:expected_value) { "no" }
|
129
199
|
|
130
|
-
it_behaves_like "any question"
|
200
|
+
it_behaves_like "any question", invert_answer: true
|
131
201
|
|
132
202
|
it "returns “true” when answering “no”" do
|
133
203
|
expect($stdin).to(receive(:gets).and_return("no\n"))
|
@@ -146,6 +216,53 @@ module Infopark
|
|
146
216
|
end
|
147
217
|
end
|
148
218
|
|
219
|
+
describe "#confirm" do
|
220
|
+
subject(:confirm) { user_io.confirm(*confirm_texts, **confirm_options) }
|
221
|
+
|
222
|
+
before { allow(user_io).to(receive(:ask)).and_return(ask_result) }
|
223
|
+
|
224
|
+
let(:ask_result) { true }
|
225
|
+
let(:confirm_options) do
|
226
|
+
[
|
227
|
+
{expected: "yes"},
|
228
|
+
{default: "foo", expected: "bar"},
|
229
|
+
{},
|
230
|
+
].sample
|
231
|
+
end
|
232
|
+
let(:confirm_texts) do
|
233
|
+
[
|
234
|
+
%w[foo bar],
|
235
|
+
%w[baz],
|
236
|
+
[],
|
237
|
+
].sample
|
238
|
+
end
|
239
|
+
|
240
|
+
it "delegates to #ask" do
|
241
|
+
confirm
|
242
|
+
if confirm_texts.empty? && confirm_options.empty?
|
243
|
+
expect(user_io).to(have_received(:ask).with(no_args))
|
244
|
+
else
|
245
|
+
expect(user_io).to(have_received(:ask).with(*confirm_texts, **confirm_options))
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
context "when #ask returns truthy" do
|
250
|
+
let(:ask_result) { [SecureRandom.hex, 1, true].sample }
|
251
|
+
|
252
|
+
it "returns the result" do
|
253
|
+
expect(confirm).to(be(ask_result))
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context "when #ask returns falsey" do
|
258
|
+
let(:ask_result) { [nil, false].sample }
|
259
|
+
|
260
|
+
it "aborts" do
|
261
|
+
expect { confirm }.to(raise_error(UserIO::Aborted))
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
149
266
|
describe "#select" do
|
150
267
|
before { allow($stdin).to(receive(:gets).and_return("1\n")) }
|
151
268
|
|
@@ -206,7 +323,7 @@ module Infopark
|
|
206
323
|
describe "#tell_pty_stream" do
|
207
324
|
let(:color_options) { {} }
|
208
325
|
let(:stream) { instance_double(IO) }
|
209
|
-
let(:data) { "test data" }
|
326
|
+
let(:data) { +"test data" }
|
210
327
|
|
211
328
|
subject(:tell) { user_io.tell_pty_stream(stream, **color_options) }
|
212
329
|
|
@@ -221,7 +338,7 @@ module Infopark
|
|
221
338
|
it "tells all data from stream in non blocking chunks" do
|
222
339
|
expect(stream).to(receive(:eof?).and_return(false, false, false, true))
|
223
340
|
expect(stream).to(receive(:read_nonblock).with(100)
|
224
|
-
.and_return("first\nchunk", "second chunk", "\nlast chunk"))
|
341
|
+
.and_return(+"first\nchunk", +"second chunk", +"\nlast chunk"))
|
225
342
|
expect($stdout).to(receive(:write).with("first\nchunk"))
|
226
343
|
expect($stdout).to(receive(:write).with("second chunk"))
|
227
344
|
expect($stdout).to(receive(:write).with("\nlast chunk"))
|
@@ -261,7 +378,7 @@ module Infopark
|
|
261
378
|
end
|
262
379
|
|
263
380
|
context "when stream contains carriage return" do
|
264
|
-
let(:data) { "some\rdata\rwith\rCRs" }
|
381
|
+
let(:data) { +"some\rdata\rwith\rCRs" }
|
265
382
|
|
266
383
|
it "writes the prefix right after the CR" do
|
267
384
|
expect($stdout).to(receive(:write).with("[the prefix] ").ordered)
|
@@ -289,7 +406,7 @@ module Infopark
|
|
289
406
|
end
|
290
407
|
|
291
408
|
context "when stream contains newline" do
|
292
|
-
let(:data) { "some\ndata\nwith\nNLs" }
|
409
|
+
let(:data) { +"some\ndata\nwith\nNLs" }
|
293
410
|
|
294
411
|
it "writes the prefix right after the NL" do
|
295
412
|
expect($stdout).to(receive(:write).with("[the prefix] ").ordered)
|
@@ -317,7 +434,7 @@ module Infopark
|
|
317
434
|
|
318
435
|
context "when stream ends with newline" do
|
319
436
|
# includes an empty chunk to verify, that they don't consume the pending NL
|
320
|
-
let(:data) { ["some\n", "data\n", "with\n", "", "NLs\n", ""] }
|
437
|
+
let(:data) { [+"some\n", +"data\n", +"with\n", +"", +"NLs\n", +""] }
|
321
438
|
|
322
439
|
it "does not write prefix after the last newline" do
|
323
440
|
expect($stdout).to(receive(:write).with("[the prefix] ").ordered)
|
@@ -354,7 +471,7 @@ module Infopark
|
|
354
471
|
end
|
355
472
|
|
356
473
|
context "when data does not end with newline" do
|
357
|
-
let(:data) { "foo" }
|
474
|
+
let(:data) { +"foo" }
|
358
475
|
|
359
476
|
it "writes prefix on next output nevertheless" do
|
360
477
|
expect($stdout).to(receive(:write).with("[the prefix] ").ordered)
|
@@ -388,20 +505,23 @@ module Infopark
|
|
388
505
|
context "when in background" do
|
389
506
|
let(:color_options) { {color: :yellow} }
|
390
507
|
let(:options) { {output_prefix: "foo"} }
|
391
|
-
let(:data) { ["data\n", "in\nchunks", "", "yo\n", ""] }
|
508
|
+
let(:data) { [+"data\n", +"in\nchunks", +"", +"yo\n", +""] }
|
392
509
|
|
393
|
-
|
394
|
-
@
|
395
|
-
Thread.new
|
510
|
+
before do
|
511
|
+
@fg_in = Thread::Queue.new
|
512
|
+
@fg_out = Thread::Queue.new
|
513
|
+
@fg_thread = Thread.new do
|
396
514
|
user_io.background_other_threads
|
397
|
-
|
515
|
+
@fg_out.push(:other_backgrounded)
|
516
|
+
@fg_in.pop
|
398
517
|
user_io.foreground
|
399
518
|
end
|
400
519
|
end
|
401
520
|
|
402
|
-
after {
|
521
|
+
after { @fg_thread.kill.join }
|
403
522
|
|
404
523
|
it "holds back the output until coming back to foreground" do
|
524
|
+
@fg_out.pop(timeout: 1)
|
405
525
|
expect($stdout).to_not(receive(:write))
|
406
526
|
tell
|
407
527
|
RSpec::Mocks.space.proxy_for($stdout).reset
|
@@ -413,10 +533,37 @@ module Infopark
|
|
413
533
|
expect($stdout).to(receive(:write).with("yo").ordered)
|
414
534
|
expect($stdout).to(receive(:write).with("\n").ordered)
|
415
535
|
expect($stdout).to(receive(:write).with("\e[22;39m").ordered)
|
416
|
-
@
|
417
|
-
|
536
|
+
@fg_in.push(:background_done)
|
537
|
+
@fg_thread.join
|
418
538
|
end
|
419
539
|
end
|
420
540
|
end
|
541
|
+
|
542
|
+
describe "#warn" do
|
543
|
+
subject(:warn) { user_io.warn(*warn_texts, **warn_options) }
|
544
|
+
|
545
|
+
before { allow(user_io).to(receive(:tell)).and_return(tell_result) }
|
546
|
+
|
547
|
+
let(:tell_result) { [SecureRandom.hex, nil].sample }
|
548
|
+
let(:warn_options) do
|
549
|
+
[
|
550
|
+
{newline: true},
|
551
|
+
{prefix: "foo", newline: false},
|
552
|
+
{},
|
553
|
+
].sample
|
554
|
+
end
|
555
|
+
let(:warn_texts) do
|
556
|
+
[
|
557
|
+
%w[foo bar],
|
558
|
+
%w[baz],
|
559
|
+
[],
|
560
|
+
].sample
|
561
|
+
end
|
562
|
+
|
563
|
+
it "delegates to #tell" do
|
564
|
+
warn
|
565
|
+
expect(user_io).to(have_received(:tell).with(*warn_texts, **warn_options, color: :yellow, bright: true))
|
566
|
+
end
|
567
|
+
end
|
421
568
|
end
|
422
569
|
end
|