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 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