infopark-user_io 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 788fbbd5a3940295bcdce71286f3cd3ffc1dba49
4
- data.tar.gz: 9147dc314df032cb8cb9b0c461a6d374d9620597
3
+ metadata.gz: 4a723c208d08faafcf16ab1a69f3d1a0258626a2
4
+ data.tar.gz: 18a653156ce1b8bbbcff53bdc7808924951d813a
5
5
  SHA512:
6
- metadata.gz: f8400ac9352308270eb09c367836eac8417101398b43712837494fe81612cc12a84ad4426ec692803c270b61134c26a3b1797128b4abb06dbba2203458732110
7
- data.tar.gz: 07c1a06cdcfec38ec4d6fc7c98f63221c33941ca7f9335da98abc083fadb32ce0bb6c141e6451d4f6858fb9c7979053d607b7e7845f9d941b3e68d1991ca28d7
6
+ metadata.gz: f8d775c13cdca54d5dcfaf57fea54c6c7384df57297a48025548ef4209ebfc7c4e2b1bf0e6020a4a2e121eb471137973b4612de0d33bcea57585eb9fb954ab8f
7
+ data.tar.gz: e9977f5ff38901242d98fe5c84ef2ee18c321c8bf1d20d6d6d21d8ad5741dda401c045d1a0f82df15d508ad0528ea42a48b1e88c80cfda89bb843219e0528fb9
@@ -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
- @background_lines = []
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
- @background_lines.each(&STDOUT.method(:write))
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
- if tty?
227
- if line_prefix = text_color(**color_options)
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
- @background_lines << out_line
256
+ @background_data << bytes
237
257
  else
238
- STDOUT.write(out_line)
258
+ STDOUT.write(bytes)
239
259
  end
260
+ end
240
261
 
241
- @line_pending[background?] = !newline
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)
@@ -1,5 +1,5 @@
1
1
  module Infopark
2
2
  class UserIO
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
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.1.1
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-10 00:00:00.000000000 Z
11
+ date: 2017-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler