tty-file 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1 +1 @@
1
- require_relative 'tty/file'
1
+ require_relative "tty/file"
@@ -5,10 +5,10 @@ require "erb"
5
5
  require "tempfile"
6
6
  require "pathname"
7
7
 
8
+ require_relative "file/compare_files"
8
9
  require_relative "file/create_file"
9
10
  require_relative "file/digest_file"
10
11
  require_relative "file/download_file"
11
- require_relative "file/differ"
12
12
  require_relative "file/read_backward_file"
13
13
  require_relative "file/version"
14
14
 
@@ -56,7 +56,7 @@ module TTY
56
56
  buffer = read_to_char(relative_path, bytes, 0)
57
57
 
58
58
  begin
59
- return buffer !~ /\A[\s[[:print:]]]*\z/m
59
+ buffer !~ /\A[\s[[:print:]]]*\z/m
60
60
  rescue ArgumentError => error
61
61
  return true if error.message =~ /invalid byte sequence/
62
62
  raise
@@ -98,9 +98,8 @@ module TTY
98
98
  # @param [File, IO, String, Pathname] source
99
99
  # the source to generate checksum for
100
100
  # @param [String] mode
101
- # @param [Hash[Symbol]] options
102
- # @option options [String] :noop
103
- # No operation
101
+ # @param [Boolean] noop
102
+ # when true skip this action
104
103
  #
105
104
  # @example
106
105
  # checksum_file("/path/to/file")
@@ -112,21 +111,25 @@ module TTY
112
111
  # the generated hex value
113
112
  #
114
113
  # @api public
115
- def checksum_file(source, *args, **options)
114
+ def checksum_file(source, *args, noop: false)
116
115
  mode = args.size.zero? ? "sha256" : args.pop
117
116
  digester = DigestFile.new(source, mode)
118
- digester.call unless options[:noop]
117
+ digester.call unless noop
119
118
  end
120
119
  module_function :checksum_file
121
120
 
122
121
  # Change file permissions
123
122
  #
124
123
  # @param [String, Pathname] relative_path
124
+ # the string or path to a file
125
125
  # @param [Integer,String] permisssions
126
- # @param [Hash[Symbol]] options
127
- # @option options [Symbol] :noop
128
- # @option options [Symbol] :verbose
129
- # @option options [Symbol] :force
126
+ # the string or octal number for permissoins
127
+ # @param [Boolean] noop
128
+ # when true skips this action
129
+ # @param [Boolean] verbose
130
+ # when true displays logging information
131
+ # @param [Symbol] color
132
+ # the name for the color to format display message, :green by default
130
133
  #
131
134
  # @example
132
135
  # chmod("Gemfile", 0755)
@@ -138,10 +141,9 @@ module TTY
138
141
  # chmod("Gemfile", "u+x,g+x")
139
142
  #
140
143
  # @api public
141
- def chmod(relative_path, permissions, **options)
142
- log_status(:chmod, relative_path, options.fetch(:verbose, true),
143
- options.fetch(:color, :green))
144
- ::FileUtils.chmod_R(permissions, relative_path) unless options[:noop]
144
+ def chmod(relative_path, permissions, verbose: true, color: :green, noop: false)
145
+ log_status(:chmod, relative_path, verbose: verbose, color: color)
146
+ ::FileUtils.chmod_R(permissions, relative_path) unless noop
145
147
  end
146
148
  module_function :chmod
147
149
 
@@ -149,6 +151,18 @@ module TTY
149
151
  #
150
152
  # @param [String, Pathname, Hash] destination
151
153
  # the path or data structure describing directory tree
154
+ # @param [Object] context
155
+ # the context for template evaluation
156
+ # @param [Boolean] quiet
157
+ # when true leaves prompt output, otherwise clears
158
+ # @param [Boolean] force
159
+ # when true overwrites existing files, false by default
160
+ # @param [Boolean] noop
161
+ # when true skips this action
162
+ # @param [Boolean] verbose
163
+ # when true displays logging information
164
+ # @param [Symbol] color
165
+ # the name for the color to format display message, :green by default
152
166
  #
153
167
  # @example
154
168
  # create_directory("/path/to/dir")
@@ -170,7 +184,9 @@ module TTY
170
184
  # @return [void]
171
185
  #
172
186
  # @api public
173
- def create_directory(destination, *args, **options)
187
+ def create_directory(destination, *args, context: nil, verbose: true,
188
+ color: :green, noop: false, force: false, skip: false,
189
+ quiet: true)
174
190
  parent = args.size.nonzero? ? args.pop : nil
175
191
  if destination.is_a?(String) || destination.is_a?(Pathname)
176
192
  destination = { destination.to_s => [] }
@@ -180,15 +196,18 @@ module TTY
180
196
  path = parent.nil? ? dir : ::File.join(parent, dir)
181
197
  unless ::File.exist?(path)
182
198
  ::FileUtils.mkdir_p(path)
183
- log_status(:create, path, options.fetch(:verbose, true),
184
- options.fetch(:color, :green))
199
+ log_status(:create, path, verbose: verbose, color: color)
185
200
  end
186
201
 
187
202
  files.each do |filename, contents|
188
203
  if filename.respond_to?(:each_pair)
189
- create_directory(filename, path, **options)
204
+ create_directory(filename, path, context: context,
205
+ verbose: verbose, color: color, noop: noop,
206
+ force: force, skip: skip, quiet: quiet)
190
207
  else
191
- create_file(::File.join(path, filename), contents, **options)
208
+ create_file(::File.join(path, filename), contents, context: context,
209
+ verbose: verbose, color: color, noop: noop, force: force,
210
+ skip: skip, quiet: quiet)
192
211
  end
193
212
  end
194
213
  end
@@ -203,9 +222,20 @@ module TTY
203
222
  # @param [String, Pathname] relative_path
204
223
  # @param [String|nil] content
205
224
  # the content to add to file
206
- # @param [Hash] options
207
- # @option options [Symbol] :force
225
+ # @param [Object] context
226
+ # the binding to use for the template
227
+ # @param [Symbol] color
228
+ # the color name to use for logging
229
+ # @param [Boolean] force
208
230
  # forces ovewrite if conflict present
231
+ # @param [Boolean] verbose
232
+ # when true log the action status to stdout
233
+ # @param [Boolean] noop
234
+ # when true do not execute the action
235
+ # @param [Boolean] skip
236
+ # when true skip the action
237
+ # @param [Boolean] quiet
238
+ # when true leaves prompt output, otherwise clears
209
239
  #
210
240
  # @example
211
241
  # create_file("doc/README.md", "# Title header")
@@ -216,11 +246,14 @@ module TTY
216
246
  # end
217
247
  #
218
248
  # @api public
219
- def create_file(relative_path, *args, **options, &block)
249
+ def create_file(relative_path, *args, context: nil, force: false, skip: false,
250
+ verbose: true, color: :green, noop: false, quiet: true, &block)
220
251
  relative_path = relative_path.to_s
221
252
  content = block_given? ? block[] : args.join
222
253
 
223
- CreateFile.new(self, relative_path, content, options).call
254
+ CreateFile.new(self, relative_path, content, context: context, force: force,
255
+ skip: skip, verbose: verbose, color: color, noop: noop,
256
+ quiet: quiet).call
224
257
  end
225
258
  module_function :create_file
226
259
 
@@ -239,29 +272,33 @@ module TTY
239
272
  # copy_file "templates/%name%.rb", "app/%name%.rb", context: vars
240
273
  #
241
274
  # @param [String, Pathname] source_path
242
- # @param [Hash] options
243
- # @option options [Symbol] :context
275
+ # the file path to copy file from
276
+ # @param [Object] context
244
277
  # the binding to use for the template
245
- # @option options [Symbol] :preserve
246
- # If true, the owner, group, permissions and modified time
247
- # are preserved on the copied file, defaults to false.
248
- # @option options [Symbol] :noop
249
- # If true do not execute the action.
250
- # @option options [Symbol] :verbose
251
- # If true log the action status to stdout
278
+ # @param [Boolean] preserve
279
+ # when true, the owner, group, permissions and modified time
280
+ # are preserved on the copied file, defaults to false
281
+ # @param [Boolean] noop
282
+ # when true does not execute the action
283
+ # @param [Boolean] verbose
284
+ # when true log the action status to stdout
285
+ # @param [Symbol] color
286
+ # the color name to use for logging
252
287
  #
253
288
  # @api public
254
- def copy_file(source_path, *args, **options, &block)
289
+ def copy_file(source_path, *args, context: nil, force: false, skip: false,
290
+ verbose: true, color: :green, noop: false, preserve: nil, &block)
255
291
  source_path = source_path.to_s
256
292
  dest_path = (args.first || source_path).to_s.sub(/\.erb$/, "")
257
293
 
258
- ctx = if (vars = options[:context])
259
- vars.instance_eval("binding")
294
+ ctx = if context
295
+ context.instance_eval("binding")
260
296
  else
261
297
  instance_eval("binding")
262
298
  end
263
299
 
264
- create_file(dest_path, **options) do
300
+ create_file(dest_path, context: context, force: force, skip: skip,
301
+ verbose: verbose, color: color, noop: noop) do
265
302
  version = ERB.version.scan(/\d+\.\d+\.\d+/)[0]
266
303
  template = if version.to_f >= 2.2
267
304
  ERB.new(::File.binread(source_path), trim_mode: "-", eoutvar: "@output_buffer")
@@ -272,9 +309,10 @@ module TTY
272
309
  content = block[content] if block
273
310
  content
274
311
  end
275
- return unless options[:preserve]
312
+ return unless preserve
276
313
 
277
- copy_metadata(source_path, dest_path, **options)
314
+ copy_metadata(source_path, dest_path, verbose: verbose, noop: noop,
315
+ color: color)
278
316
  end
279
317
  module_function :copy_file
280
318
 
@@ -314,38 +352,43 @@ module TTY
314
352
  # command.rb
315
353
  # README
316
354
  #
317
- # @param [String, Pathname] source_path
318
- # @param [Hash[Symbol]] options
319
- # @option options [Symbol] :preserve
320
- # If true, the owner, group, permissions and modified time
321
- # are preserved on the copied file, defaults to false.
322
- # @option options [Symbol] :recursive
323
- # If false, copies only top level files, defaults to true.
324
- # @option options [Symbol] :exclude
325
- # A regex that specifies files to ignore when copying.
326
- #
327
355
  # @example
328
356
  # copy_directory("app", "new_app", recursive: false)
357
+ #
358
+ # @example
329
359
  # copy_directory("app", "new_app", exclude: /docs/)
330
360
  #
361
+ # @param [String, Pathname] source_path
362
+ # the source directory to copy files from
363
+ # @param [Boolean] preserve
364
+ # when true, the owner, group, permissions and modified time
365
+ # are preserved on the copied file, defaults to false.
366
+ # @param [Boolean] recursive
367
+ # when false, copies only top level files, defaults to true
368
+ # @param [Regexp] exclude
369
+ # a regex that specifies files to ignore when copying
370
+ #
331
371
  # @api public
332
- def copy_directory(source_path, *args, **options, &block)
372
+ def copy_directory(source_path, *args, context: nil, force: false, skip: false,
373
+ verbose: true, color: :green, noop: false, preserve: nil,
374
+ recursive: true, exclude: nil, &block)
333
375
  source_path = source_path.to_s
334
376
  check_path(source_path)
335
377
  source = escape_glob_path(source_path)
336
378
  dest_path = (args.first || source).to_s
337
- opts = { recursive: true }.merge(options)
338
- pattern = opts[:recursive] ? ::File.join(source, "**") : source
379
+ pattern = recursive ? ::File.join(source, "**") : source
339
380
  glob_pattern = ::File.join(pattern, "*")
340
381
 
341
382
  Dir.glob(glob_pattern, ::File::FNM_DOTMATCH).sort.each do |file_source|
342
383
  next if ::File.directory?(file_source)
343
- next if opts[:exclude] && file_source.match(opts[:exclude])
384
+ next if exclude && file_source.match(exclude)
344
385
 
345
386
  dest = ::File.join(dest_path, file_source.gsub(source_path, "."))
346
387
  file_dest = ::Pathname.new(dest).cleanpath.to_s
347
388
 
348
- copy_file(file_source, file_dest, **options, &block)
389
+ copy_file(file_source, file_dest, context: context, force: force,
390
+ skip: skip, verbose: verbose, color: color, noop: noop,
391
+ preserve: preserve, &block)
349
392
  end
350
393
  end
351
394
  module_function :copy_directory
@@ -356,57 +399,88 @@ module TTY
356
399
  # Diff files line by line
357
400
  #
358
401
  # @param [String, Pathname] path_a
402
+ # the path to the original file
359
403
  # @param [String, Pathname] path_b
360
- # @param [Hash[Symbol]] options
361
- # @option options [Symbol] :format
404
+ # the path to a new file
405
+ # @param [Symbol] format
362
406
  # the diffining output format
363
- # @option options [Symbol] :context_lines
407
+ # @param [Intger] lines
364
408
  # the number of extra lines for the context
365
- # @option options [Symbol] :threshold
409
+ # @param [Integer] threshold
366
410
  # maximum file size in bytes
367
411
  #
368
412
  # @example
369
413
  # diff(file_a, file_b, format: :old)
370
414
  #
371
415
  # @api public
372
- def diff(path_a, path_b, **options)
373
- threshold = options[:threshold] || 10_000_000
374
- output = []
416
+ def diff(path_a, path_b, threshold: 10_000_000, format: :unified, lines: 3,
417
+ header: true, verbose: true, color: :green, noop: false)
418
+ open_tempfile_if_missing(path_a) do |file_a, temp_a|
419
+ message = check_binary_or_large(file_a, threshold)
420
+ return message if message
375
421
 
376
- open_tempfile_if_missing(path_a) do |file_a|
377
- if ::File.size(file_a) > threshold
378
- raise ArgumentError, "(file size of #{file_a.path} exceeds #{threshold} bytes, diff output suppressed)"
379
- end
380
- if binary?(file_a)
381
- raise ArgumentError, "(#{file_a.path} is binary, diff output suppressed)"
382
- end
383
- open_tempfile_if_missing(path_b) do |file_b|
384
- if binary?(file_b)
385
- raise ArgumentError, "(#{file_a.path} is binary, diff output suppressed)"
386
- end
387
- if ::File.size(file_b) > threshold
388
- return "(file size of #{file_b.path} exceeds #{threshold} bytes, diff output suppressed)"
389
- end
422
+ open_tempfile_if_missing(path_b) do |file_b, temp_b|
423
+ message = check_binary_or_large(file_b, threshold)
424
+ return message if message
390
425
 
391
- log_status(:diff, "#{file_a.path} - #{file_b.path}",
392
- options.fetch(:verbose, true), options.fetch(:color, :green))
393
- return output.join if options[:noop]
426
+ file_a_path, file_b_path = *diff_paths(file_a, file_b, temp_a, temp_b)
427
+ .map { |path| ::File.join(*path) }
394
428
 
395
- block_size = file_a.lstat.blksize
396
- while !file_a.eof? && !file_b.eof?
397
- output << Differ.new(file_a.read(block_size),
398
- file_b.read(block_size),
399
- options).call
400
- end
429
+ log_status(:diff, "#{file_a_path} and #{file_b_path}",
430
+ verbose: verbose, color: color)
431
+
432
+ return "" if noop
433
+
434
+ diff_files = CompareFiles.new(format: format, context_lines: lines,
435
+ header: header, verbose: verbose,
436
+ color: color, noop: noop,
437
+ diff_colors: diff_colors)
438
+
439
+ return diff_files.call(file_a, file_b, file_a_path, file_b_path)
401
440
  end
402
441
  end
403
- output.join
404
442
  end
405
443
  module_function :diff
406
444
 
407
445
  alias diff_files diff
408
446
  module_function :diff_files
409
447
 
448
+ # @api private
449
+ def diff_paths(file_a, file_b, temp_a, temp_b)
450
+ if temp_a && !temp_b
451
+ [["a", file_b.path], ["b", file_b.path]]
452
+ elsif !temp_a && temp_b
453
+ [["a", file_a.path], ["b", file_a.path]]
454
+ elsif temp_a && temp_b
455
+ [["a"], ["b"]]
456
+ else
457
+ [file_a.path, file_b.path]
458
+ end
459
+ end
460
+ private_module_function :diff_paths
461
+
462
+ def diff_colors
463
+ {
464
+ green: @pastel.green.detach,
465
+ red: @pastel.red.detach,
466
+ cyan: @pastel.cyan.detach
467
+ }
468
+ end
469
+ private_module_function :diff_colors
470
+
471
+ # Check if file is binary or exceeds threshold size
472
+ #
473
+ # @api private
474
+ def check_binary_or_large(file, threshold)
475
+ if binary?(file)
476
+ "#{file.path} is binary, diff output suppressed"
477
+ elsif ::File.size(file) > threshold
478
+ "file size of #{file.path} exceeds #{threshold} bytes, " \
479
+ " diff output suppressed"
480
+ end
481
+ end
482
+ private_module_function :check_binary_or_large
483
+
410
484
  # Download the content from a given address and
411
485
  # save at the given relative destination. If block
412
486
  # is provided in place of destination, the content of
@@ -416,9 +490,8 @@ module TTY
416
490
  # the URI address
417
491
  # @param [String, Pathname] dest
418
492
  # the relative path to save
419
- # @param [Hash[Symbol]] options
420
- # @param options [Symbol] :limit
421
- # the limit of redirects
493
+ # @param [Integer] limit
494
+ # the number of maximium redirects
422
495
  #
423
496
  # @example
424
497
  # download_file("https://gist.github.com/4701967",
@@ -439,7 +512,7 @@ module TTY
439
512
  return
440
513
  end
441
514
 
442
- content = DownloadFile.new(uri, dest_path, options).call
515
+ content = DownloadFile.new(uri, dest_path, limit: options[:limit]).call
443
516
 
444
517
  if block_given?
445
518
  content = (block.arity.nonzero? ? block[content] : block[])
@@ -467,11 +540,11 @@ module TTY
467
540
  # end
468
541
  #
469
542
  # @api public
470
- def prepend_to_file(relative_path, *args, **options, &block)
471
- log_status(:prepend, relative_path, options.fetch(:verbose, true),
472
- options.fetch(:color, :green))
473
- options.merge!(before: /\A/, verbose: false)
474
- inject_into_file(relative_path, *args, **options, &block)
543
+ def prepend_to_file(relative_path, *args, verbose: true, color: :green,
544
+ force: true, noop: false, &block)
545
+ log_status(:prepend, relative_path, verbose: verbose, color: color)
546
+ inject_into_file(relative_path, *args, before: /\A/, verbose: false,
547
+ color: color, force: force, noop: noop, &block)
475
548
  end
476
549
  module_function :prepend_to_file
477
550
 
@@ -498,11 +571,11 @@ module TTY
498
571
  # end
499
572
  #
500
573
  # @api public
501
- def append_to_file(relative_path, *args, **options, &block)
502
- log_status(:append, relative_path, options.fetch(:verbose, true),
503
- options.fetch(:color, :green))
504
- options.merge!(after: /\z/, verbose: false)
505
- inject_into_file(relative_path, *args, **options, &block)
574
+ def append_to_file(relative_path, *args, verbose: true, color: :green,
575
+ force: true, noop: false, &block)
576
+ log_status(:append, relative_path, verbose: verbose, color: color)
577
+ inject_into_file(relative_path, *args, after: /\z/, verbose: false,
578
+ force: force, noop: noop, color: color, &block)
506
579
  end
507
580
  module_function :append_to_file
508
581
 
@@ -520,16 +593,18 @@ module TTY
520
593
  # Inject content into file at a given location
521
594
  #
522
595
  # @param [String, Pathname] relative_path
523
- #
524
- # @param [Hash] options
525
- # @option options [Symbol] :before
596
+ # @param [String] before
526
597
  # the matching line to insert content before
527
- # @option options [Symbol] :after
598
+ # @param [String] after
528
599
  # the matching line to insert content after
529
- # @option options [Symbol] :force
600
+ # @param [Boolean] force
530
601
  # insert content more than once
531
- # @option options [Symbol] :verbose
532
- # log status
602
+ # @param [Boolean] verbose
603
+ # when true log status
604
+ # @param [Symbol] color
605
+ # the color name used in displaying this action
606
+ # @param [Boolean] noop
607
+ # when true skip perfomring this action
533
608
  #
534
609
  # @example
535
610
  # inject_into_file("Gemfile", "gem 'tty'", after: "gem 'rack'\n")
@@ -543,15 +618,12 @@ module TTY
543
618
  # end
544
619
  #
545
620
  # @api public
546
- def inject_into_file(relative_path, *args, **options, &block)
621
+ def inject_into_file(relative_path, *args, verbose: true, color: :green,
622
+ after: nil, before: nil, force: true, noop: false, &block)
547
623
  check_path(relative_path)
548
624
  replacement = block_given? ? block[] : args.join
549
625
 
550
- flag, match = if options.key?(:after)
551
- [:after, options.delete(:after)]
552
- else
553
- [:before, options.delete(:before)]
554
- end
626
+ flag, match = after ? [:after, after] : [:before, before]
555
627
 
556
628
  match = match.is_a?(Regexp) ? match : Regexp.escape(match)
557
629
  content = if flag == :after
@@ -560,10 +632,9 @@ module TTY
560
632
  replacement + '\0'
561
633
  end
562
634
 
563
- log_status(:inject, relative_path, options.fetch(:verbose, true),
564
- options.fetch(:color, :green))
565
- replace_in_file(relative_path, /#{match}/, content,
566
- **options.merge(verbose: false))
635
+ log_status(:inject, relative_path, verbose: verbose, color: color)
636
+ replace_in_file(relative_path, /#{match}/, content, verbose: false,
637
+ color: color, force: force, noop: noop)
567
638
  end
568
639
  module_function :inject_into_file
569
640
 
@@ -582,11 +653,14 @@ module TTY
582
653
  # when no substitutions were performed, true otherwise.
583
654
  #
584
655
  # @param [String, Pathname] relative_path
585
- # @options [Hash[String]] options
586
- # @option options [Symbol] :force
656
+ # @param [Boolean] force
587
657
  # replace content even if present
588
- # @option options [Symbol] :verbose
589
- # log status
658
+ # @param [Boolean] verbose
659
+ # when true log status to stdout
660
+ # @param [Boolean] noop
661
+ # when true skip executing this action
662
+ # @param [Symbol] color
663
+ # the name of the color used for displaying action
590
664
  #
591
665
  # @example
592
666
  # replace_in_file("Gemfile", /gem 'rails'/, "gem 'hanami'")
@@ -600,18 +674,18 @@ module TTY
600
674
  # true when replaced content, false otherwise
601
675
  #
602
676
  # @api public
603
- def replace_in_file(relative_path, *args, **options, &block)
677
+ def replace_in_file(relative_path, *args, verbose: true, color: :green,
678
+ noop: false, force: true, &block)
604
679
  check_path(relative_path)
605
680
  contents = ::File.read(relative_path)
606
681
  replacement = (block ? block[] : args[1..-1].join).gsub('\0', "")
607
682
  match = Regexp.escape(replacement)
608
683
  status = nil
609
684
 
610
- log_status(:replace, relative_path, options.fetch(:verbose, true),
611
- options.fetch(:color, :green))
612
- return false if options[:noop]
685
+ log_status(:replace, relative_path, verbose: verbose, color: color)
686
+ return false if noop
613
687
 
614
- if options.fetch(:force, true) || !(contents =~ /^#{match}(\r?\n)*/m)
688
+ if force || !(contents =~ /^#{match}(\r?\n)*/m)
615
689
  status = contents.gsub!(*args, &block)
616
690
  if !status.nil?
617
691
  ::File.open(relative_path, "w") do |file|
@@ -629,29 +703,27 @@ module TTY
629
703
  # Remove a file or a directory at specified relative path.
630
704
  #
631
705
  # @param [String, Pathname] relative_path
632
- # @param [Hash[:Symbol]] options
633
- # @option options [Symbol] :noop
634
- # pretend removing file
635
- # @option options [Symbol] :force
636
- # remove file ignoring errors
637
- # @option options [Symbol] :verbose
638
- # log status
639
- # @option options [Symbol] :secure
640
- # for secure removing
706
+ # @param [Boolean] noop
707
+ # when true pretend to remove file
708
+ # @param [Boolean] force
709
+ # when true remove file ignoring errors
710
+ # @param [Boolean] verbose
711
+ # when true log status
712
+ # @param [Boolean] secure
713
+ # when true check for secure removing
641
714
  #
642
715
  # @example
643
716
  # remove_file "doc/README.md"
644
717
  #
645
718
  # @api public
646
- def remove_file(relative_path, *args, **options)
719
+ def remove_file(relative_path, *args, verbose: true, color: :red, noop: false,
720
+ force: nil, secure: true)
647
721
  relative_path = relative_path.to_s
648
- log_status(:remove, relative_path, options.fetch(:verbose, true),
649
- options.fetch(:color, :red))
722
+ log_status(:remove, relative_path, verbose: verbose, color: color)
650
723
 
651
- return if options[:noop] || !::File.exist?(relative_path)
724
+ return if noop || !::File.exist?(relative_path)
652
725
 
653
- ::FileUtils.rm_r(relative_path, force: options[:force],
654
- secure: options.fetch(:secure, true))
726
+ ::FileUtils.rm_r(relative_path, force: force, secure: secure)
655
727
  end
656
728
  module_function :remove_file
657
729
 
@@ -659,44 +731,44 @@ module TTY
659
731
  #
660
732
  # @param [String, Pathname] relative_path
661
733
  # the relative path to a file
662
- #
663
- # @param [Integer] num_lines
734
+ # @param [Integer] lines
664
735
  # the number of lines to return from file
736
+ # @param [Integer] chunk_size
737
+ # the size of the chunk to read
665
738
  #
666
739
  # @example
667
740
  # tail_file "filename"
668
741
  # # => ["line 19", "line20", ... ]
669
742
  #
670
743
  # @example
671
- # tail_file "filename", 15
744
+ # tail_file "filename", lines: 15
672
745
  # # => ["line 19", "line20", ... ]
673
746
  #
674
747
  # @return [Array[String]]
675
748
  #
676
749
  # @api public
677
- def tail_file(relative_path, num_lines = 10, **options, &block)
678
- file = ::File.open(relative_path)
679
- chunk_size = options.fetch(:chunk_size, 512)
680
- line_sep = $/
681
- lines = []
750
+ def tail_file(relative_path, lines: 10, chunk_size: 512, &block)
751
+ file = ::File.open(relative_path)
752
+ line_sep = $/
753
+ output = []
682
754
  newline_count = 0
683
755
 
684
756
  ReadBackwardFile.new(file, chunk_size).each_chunk do |chunk|
685
757
  # look for newline index counting from right of chunk
686
758
  while (nl_index = chunk.rindex(line_sep, (nl_index || chunk.size) - 1))
687
759
  newline_count += 1
688
- break if newline_count > num_lines || nl_index.zero?
760
+ break if newline_count > lines || nl_index.zero?
689
761
  end
690
762
 
691
- if newline_count > num_lines
692
- lines.insert(0, chunk[(nl_index + 1)..-1])
763
+ if newline_count > lines
764
+ output.insert(0, chunk[(nl_index + 1)..-1])
693
765
  break
694
766
  else
695
- lines.insert(0, chunk)
767
+ output.insert(0, chunk)
696
768
  end
697
769
  end
698
770
 
699
- lines.join.split(line_sep).each(&block).to_a
771
+ output.join.split(line_sep).each(&block).to_a
700
772
  end
701
773
  module_function :tail_file
702
774
 
@@ -741,7 +813,7 @@ module TTY
741
813
  # Log file operation
742
814
  #
743
815
  # @api private
744
- def log_status(cmd, message, verbose, color = false)
816
+ def log_status(cmd, message, verbose: true, color: false)
745
817
  return unless verbose
746
818
 
747
819
  cmd = cmd.to_s.rjust(12)
@@ -773,7 +845,7 @@ module TTY
773
845
  tempfile << object
774
846
  tempfile.rewind
775
847
 
776
- block[tempfile]
848
+ block[tempfile, ::File.basename(tempfile)]
777
849
 
778
850
  unless tempfile.nil?
779
851
  tempfile.close
@@ -782,5 +854,15 @@ module TTY
782
854
  end
783
855
  end
784
856
  private_module_function :open_tempfile_if_missing
857
+
858
+ # Check if IO is attached to a terminal
859
+ #
860
+ # return [Boolean]
861
+ #
862
+ # @api public
863
+ def tty?
864
+ @output.respond_to?(:tty?) && @output.tty?
865
+ end
866
+ private_module_function :tty?
785
867
  end # File
786
868
  end # TTY