tty-file 0.3.0 → 0.4.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: 1b396379259eca3c11fd09a27eb97b9a777ae747
4
- data.tar.gz: c6094ec23477f0ab1e36af36f507d0b36f110ef1
3
+ metadata.gz: 23702ac1a40d811c8c23a524265585b0f6fecb26
4
+ data.tar.gz: fdc10c2af852b2c6d935d81439e90c54eaa2550b
5
5
  SHA512:
6
- metadata.gz: 45cbe892f8379b9a81f3e220aa874a6d49a06097616c826f917972663c681e4faf72b0c06c28727716eda35613d51e5669799f98bcf7e075fe742b02396e865c
7
- data.tar.gz: 087d20a6a7100e30c5c419a80534f51f88ce954a69dbf76f3a687e82d52dddd6b54cc8472731ee8830d9cfdbb58efeb7e550dc04e131728a1a9e7adece58089d
6
+ metadata.gz: 805acf272babab1d6dd610e500502573a2d85ab0c1ed738cde3974fe03f4134b5a447314c8f44ed7da2f8ed26a88f0060ea6547bae8f3c40a177a9ea99ab0639
7
+ data.tar.gz: 59f161b5ba098f482a2de30a1cbbda0dd42bf63509f0d7118e37c36af0e94f31a0c73cefb8a0854213259126c4d5df9e903c54f7e94aeb91d8399acd3fdcd907
@@ -8,17 +8,15 @@ rvm:
8
8
  - 2.1.10
9
9
  - 2.2.5
10
10
  - 2.3.1
11
- - 2.4.0
11
+ - 2.4.1
12
12
  - ruby-head
13
13
  - jruby-9.1.1.0
14
- - rbx-3
15
14
  env:
16
15
  global:
17
16
  - JRUBY_OPTS=''
18
17
  matrix:
19
18
  allow_failures:
20
19
  - rvm: ruby-head
21
- - rvm: rbx-3
22
20
  - rvm: jruby-9.1.1.0
23
21
  fast_finish: true
24
22
  branches:
@@ -1,5 +1,18 @@
1
1
  # Change log
2
2
 
3
+ ## [v0.4.0] - 2017-09-16
4
+
5
+ ### Added
6
+ * Add tail_file for reading a given number of lines from end of a file
7
+
8
+ ### Changed
9
+ * Change api calls to accept :color option for disabling/coloring log status
10
+ * Update tty-prompt dependency
11
+
12
+ ### Fixed
13
+ * Fix #log_status to properly handle wrapping of keywords in color
14
+ * Fix #binary? to work correctly on Windows
15
+
3
16
  ## [v0.3.0] - 2017-03-26
4
17
 
5
18
  ### Changed
@@ -32,6 +45,7 @@
32
45
 
33
46
  * Initial implementation and release
34
47
 
48
+ [v0.4.0]: https://github.com/piotrmurach/tty-file/compare/v0.3.0...v0.4.0
35
49
  [v0.3.0]: https://github.com/piotrmurach/tty-file/compare/v0.2.1...v0.3.0
36
50
  [v0.2.1]: https://github.com/piotrmurach/tty-file/compare/v0.2.0...v0.2.1
37
51
  [v0.2.0]: https://github.com/piotrmurach/tty-file/compare/v0.1.0...v0.2.0
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # TTY::File [![Gitter](https://badges.gitter.im/Join%20Chat.svg)][gitter]
2
+
2
3
  [![Gem Version](https://badge.fury.io/rb/tty-file.svg)][gem]
3
4
  [![Build Status](https://secure.travis-ci.org/piotrmurach/tty-file.svg?branch=master)][travis]
5
+ [![Build status](https://ci.appveyor.com/api/projects/status/og69rn550s4mt1q3?svg=true)][appveyor]
4
6
  [![Code Climate](https://codeclimate.com/github/piotrmurach/tty-file/badges/gpa.svg)][codeclimate]
5
7
  [![Coverage Status](https://coveralls.io/repos/github/piotrmurach/tty-file/badge.svg)][coverage]
6
8
  [![Inline docs](http://inch-ci.org/github/piotrmurach/tty-file.svg?branch=master)][inchpages]
@@ -8,6 +10,7 @@
8
10
  [gitter]: https://gitter.im/piotrmurach/tty
9
11
  [gem]: http://badge.fury.io/rb/tty-file
10
12
  [travis]: http://travis-ci.org/piotrmurach/tty-file
13
+ [appveyor]: https://ci.appveyor.com/project/piotrmurach/tty-file
11
14
  [codeclimate]: https://codeclimate.com/github/piotrmurach/tty-file
12
15
  [coverage]: https://coveralls.io/github/piotrmurach/tty-file
13
16
  [inchpages]: http://inch-ci.org/github/piotrmurach/tty-file
@@ -52,6 +55,7 @@ Or install it yourself as:
52
55
  * [2.12. append_to_file](#212-apend_to_file)
53
56
  * [2.13. prepend_to_file](#213-prepend_to_file)
54
57
  * [2.14. remove_file](#214-remove_file)
58
+ * [2.15. tail_file](#215-tail_file)
55
59
 
56
60
  ## 1. Usage
57
61
 
@@ -323,6 +327,12 @@ TTY::File.download_file("https://gist.github.com/4701967", "doc/README.md", limi
323
327
 
324
328
  Inject content into a file at a given location
325
329
 
330
+ ```ruby
331
+ TTY::File.inject_into_file 'filename.rb', "text to add", after: "Code below this line\n"
332
+ ```
333
+
334
+ or using a block
335
+
326
336
  ```ruby
327
337
  TTY::File.inject_into_file 'filename.rb', after: "Code below this line\n" do
328
338
  "text to add"
@@ -393,6 +403,29 @@ You can also pass in `:force` to remove file ignoring any errors:
393
403
  TTY::File.remove_file 'doc/README.md', force: true
394
404
  ```
395
405
 
406
+ ### 2.15. tail_file
407
+
408
+ To read the last 10 lines from a file do:
409
+
410
+ ```ruby
411
+ TTY::File.tail_file 'doc/README.md'
412
+ # => ['## Copyright', 'Copyright (c) 2016-2017', ...]
413
+ ```
414
+
415
+ You can also pass a block:
416
+
417
+ ```ruby
418
+ TTY::File.tail_file('doc/README.md') do |line|
419
+ puts line
420
+ end
421
+ ```
422
+
423
+ To change how many lines are read pass a second argument:
424
+
425
+ ```ruby
426
+ TTY::File.tail_file('doc/README.md', 15)
427
+ ```
428
+
396
429
  ## Development
397
430
 
398
431
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,25 @@
1
+ ---
2
+ install:
3
+ - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
4
+ - ruby --version
5
+ - gem --version
6
+ - bundle install
7
+ build: off
8
+ test_script:
9
+ - bundle exec rake ci
10
+ environment:
11
+ matrix:
12
+ - ruby_version: "193"
13
+ - ruby_version: "200"
14
+ - ruby_version: "200-x64"
15
+ - ruby_version: "21"
16
+ - ruby_version: "21-x64"
17
+ - ruby_version: "22"
18
+ - ruby_version: "22-x64"
19
+ - ruby_version: "23"
20
+ - ruby_version: "23-x64"
21
+ - ruby_version: "24"
22
+ - ruby_version: "24-x64"
23
+ matrix:
24
+ allow_failures:
25
+ - ruby_version: "193"
@@ -10,6 +10,7 @@ require_relative 'file/create_file'
10
10
  require_relative 'file/digest_file'
11
11
  require_relative 'file/download_file'
12
12
  require_relative 'file/differ'
13
+ require_relative 'file/read_backward_file'
13
14
  require_relative 'file/version'
14
15
 
15
16
  module TTY
@@ -51,8 +52,8 @@ module TTY
51
52
  #
52
53
  # @api public
53
54
  def binary?(relative_path)
54
- bytes = ::File.stat(relative_path).blksize
55
- bytes = 4096 if bytes > 4096
55
+ bytes = ::File.new(relative_path).size
56
+ bytes = 2**12 if bytes > 2**12
56
57
  buffer = ::File.read(relative_path, bytes, 0) || ''
57
58
  buffer = buffer.force_encoding(Encoding.default_external)
58
59
  begin
@@ -122,7 +123,8 @@ module TTY
122
123
  end
123
124
  end
124
125
  end
125
- log_status(:chmod, relative_path, options.fetch(:verbose, true), :green)
126
+ log_status(:chmod, relative_path, options.fetch(:verbose, true),
127
+ options.fetch(:color, :green))
126
128
  ::FileUtils.chmod_R(mode, relative_path) unless options[:noop]
127
129
  end
128
130
  module_function :chmod
@@ -162,7 +164,8 @@ module TTY
162
164
  path = parent.nil? ? dir : ::File.join(parent, dir)
163
165
  unless ::File.exist?(path)
164
166
  ::FileUtils.mkdir_p(path)
165
- log_status(:create, path, options.fetch(:verbose, true), :green)
167
+ log_status(:create, path, options.fetch(:verbose, true),
168
+ options.fetch(:color, :green))
166
169
  end
167
170
 
168
171
  files.each do |filename, contents|
@@ -359,7 +362,7 @@ module TTY
359
362
  end
360
363
 
361
364
  log_status(:diff, "#{file_a.path} - #{file_b.path}",
362
- options.fetch(:verbose, true), :green)
365
+ options.fetch(:verbose, true), options.fetch(:color, :green))
363
366
  return output if options[:noop]
364
367
 
365
368
  block_size = file_a.lstat.blksize
@@ -437,7 +440,8 @@ module TTY
437
440
  #
438
441
  # @api public
439
442
  def prepend_to_file(relative_path, *args, **options, &block)
440
- log_status(:prepend, relative_path, options.fetch(:verbose, true), :green)
443
+ log_status(:prepend, relative_path, options.fetch(:verbose, true),
444
+ options.fetch(:color, :green))
441
445
  options.merge!(before: /\A/, verbose: false)
442
446
  inject_into_file(relative_path, *(args << options), &block)
443
447
  end
@@ -459,7 +463,8 @@ module TTY
459
463
  #
460
464
  # @api public
461
465
  def append_to_file(relative_path, *args, **options, &block)
462
- log_status(:append, relative_path, options.fetch(:verbose, true), :green)
466
+ log_status(:append, relative_path, options.fetch(:verbose, true),
467
+ options.fetch(:color, :green))
463
468
  options.merge!(after: /\z/, verbose: false)
464
469
  inject_into_file(relative_path, *(args << options), &block)
465
470
  end
@@ -512,7 +517,8 @@ module TTY
512
517
 
513
518
  replace_in_file(relative_path, /#{match}/, content, options.merge(verbose: false))
514
519
 
515
- log_status(:inject, relative_path, options.fetch(:verbose, true), :green)
520
+ log_status(:inject, relative_path, options.fetch(:verbose, true),
521
+ options.fetch(:color, :green))
516
522
  end
517
523
  module_function :inject_into_file
518
524
 
@@ -541,7 +547,8 @@ module TTY
541
547
  contents = IO.read(relative_path)
542
548
  replacement = (block ? block[] : args[1..-1].join).gsub('\0', '')
543
549
 
544
- log_status(:replace, relative_path, options.fetch(:verbose, true), :green)
550
+ log_status(:replace, relative_path, options.fetch(:verbose, true),
551
+ options.fetch(:color, :green))
545
552
 
546
553
  return if options[:noop]
547
554
 
@@ -575,7 +582,8 @@ module TTY
575
582
  #
576
583
  # @api public
577
584
  def remove_file(relative_path, *args, **options)
578
- log_status(:remove, relative_path, options.fetch(:verbose, true), :red)
585
+ log_status(:remove, relative_path, options.fetch(:verbose, true),
586
+ options.fetch(:color, :red))
579
587
 
580
588
  return if options[:noop]
581
589
 
@@ -583,6 +591,51 @@ module TTY
583
591
  end
584
592
  module_function :remove_file
585
593
 
594
+ # Provide the last number of lines from a file
595
+ #
596
+ # @param [String] relative_path
597
+ # the relative path to a file
598
+ #
599
+ # @param [Integer] num_lines
600
+ # the number of lines to return from file
601
+ #
602
+ # @example
603
+ # tail_file 'filename'
604
+ # # => ['line 19', 'line20', ... ]
605
+ #
606
+ # @example
607
+ # tail_file 'filename', 15
608
+ # # => ['line 19', 'line20', ... ]
609
+ #
610
+ # @return [Array[String]]
611
+ #
612
+ # @api public
613
+ def tail_file(relative_path, num_lines = 10, **options, &block)
614
+ file = ::File.open(relative_path)
615
+ chunk_size = options.fetch(:chunk_size, 512)
616
+ line_sep = $/
617
+ lines = []
618
+ newline_count = 0
619
+
620
+ ReadBackwardFile.new(file, chunk_size).each_chunk do |chunk|
621
+ # look for newline index counting from right of chunk
622
+ while (nl_index = chunk.rindex(line_sep, (nl_index || chunk.size) - 1))
623
+ newline_count += 1
624
+ break if newline_count > num_lines || nl_index.zero?
625
+ end
626
+
627
+ if newline_count > num_lines
628
+ lines.insert(0, chunk[(nl_index + 1)..-1])
629
+ break
630
+ else
631
+ lines.insert(0, chunk)
632
+ end
633
+ end
634
+
635
+ lines.join.split(line_sep).each(&block).to_a
636
+ end
637
+ module_function :tail_file
638
+
586
639
  # Escape glob character in a path
587
640
  #
588
641
  # @param [String] path
@@ -627,9 +680,12 @@ module TTY
627
680
  return unless verbose
628
681
 
629
682
  cmd = cmd.to_s.rjust(12)
630
- cmd = decorate(cmd, color) if color
683
+ if color
684
+ i = cmd.index(/[a-z]/)
685
+ cmd = cmd[0...i] + decorate(cmd[i..-1], color)
686
+ end
631
687
 
632
- message = "#{cmd} #{message}"
688
+ message = "#{cmd} #{message}"
633
689
  message += "\n" unless message.end_with?("\n")
634
690
 
635
691
  @output.print(message)
@@ -1,15 +1,10 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'forwardable'
4
-
5
3
  module TTY
6
4
  module File
7
5
  class CreateFile
8
- extend Forwardable
9
-
10
- attr_reader :relative_path, :content, :options, :prompt
11
6
 
12
- def_delegators "@base", :log_status
7
+ attr_reader :base, :relative_path, :content, :options, :prompt
13
8
 
14
9
  def initialize(base, relative_path, content, options = {})
15
10
  @base = base
@@ -61,22 +56,29 @@ module TTY
61
56
  def detect_collision
62
57
  if exist?
63
58
  if identical?
64
- log_status(:identical, relative_path, options.fetch(:verbose, true), :blue)
59
+ notify(:identical, :blue)
65
60
  elsif options[:force]
66
- log_status(:force, relative_path, options.fetch(:verbose, true), :yellow)
61
+ notify(:force, :yellow)
67
62
  yield unless options[:noop]
68
63
  elsif options[:skip]
69
- log_status(:skip, relative_path, options.fetch(:verbose, true), :yellow)
64
+ notify(:skip, :yellow)
70
65
  else
71
- log_status(:collision, relative_path, options.fetch(:verbose, true), :red)
66
+ notify(:collision, :red)
72
67
  yield if file_collision(relative_path, content)
73
68
  end
74
69
  else
75
- log_status(:create, relative_path, options.fetch(:verbose, true), :green)
70
+ notify(:create, :green)
76
71
  yield unless options[:noop]
77
72
  end
78
73
  end
79
74
 
75
+ # Notify console about performed action
76
+ # @api private
77
+ def notify(name, color)
78
+ base.__send__(:log_status, name, relative_path,
79
+ options.fetch(:verbose, true), options.fetch(:color, color))
80
+ end
81
+
80
82
  # Display conflict resolution menu and gather answer
81
83
  #
82
84
  # @api private
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ class ReadBackwardFile
5
+ attr_reader :file,
6
+ :chunk_size
7
+
8
+ # Create a ReadBackwardFile
9
+ #
10
+ # @param [File] file
11
+ # the file to read backward from
12
+ # @param [Integer] chunk_size
13
+ # the chunk size used to step through the file backwards
14
+ #
15
+ # @api public
16
+ def initialize(file, chunk_size = 512)
17
+ @file = file
18
+ @chunk_size = chunk_size
19
+ @file_size = ::File.stat(file).size
20
+ end
21
+
22
+ # Read file in chunks
23
+ #
24
+ # @yield [String]
25
+ # the chunk from file content
26
+ #
27
+ # @api public
28
+ def each_chunk
29
+ file.seek(0, IO::SEEK_END)
30
+ while file.tell > 0
31
+ if file.tell < @chunk_size # don't read beyond file size
32
+ @chunk_size = file.tell
33
+ end
34
+ file.seek(-@chunk_size, IO::SEEK_CUR)
35
+ chunk = file.read(@chunk_size)
36
+ yield(chunk)
37
+ file.seek(-@chunk_size, IO::SEEK_CUR)
38
+ end
39
+ end
40
+ end # ReadBackwardFile
@@ -2,6 +2,6 @@
2
2
 
3
3
  module TTY
4
4
  module File
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0"
6
6
  end # File
7
7
  end # TTY
@@ -19,11 +19,11 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_dependency 'pastel', '~> 0.7.0'
23
- spec.add_dependency 'tty-prompt', '~> 0.12.0'
22
+ spec.add_dependency 'pastel', '~> 0.7.1'
23
+ spec.add_dependency 'tty-prompt', '~> 0.13.2'
24
24
  spec.add_dependency 'diff-lcs', '~> 1.3.0'
25
25
 
26
- spec.add_development_dependency 'bundler', '>= 1.5.0', '< 2.0'
26
+ spec.add_development_dependency 'bundler', '~> 1.5'
27
27
  spec.add_development_dependency 'rake', '~> 10.0'
28
28
  spec.add_development_dependency 'rspec', '~> 3.0'
29
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tty-file
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-03-26 00:00:00.000000000 Z
11
+ date: 2017-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pastel
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.7.0
19
+ version: 0.7.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.7.0
26
+ version: 0.7.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: tty-prompt
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.12.0
33
+ version: 0.13.2
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.12.0
40
+ version: 0.13.2
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: diff-lcs
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -56,22 +56,16 @@ dependencies:
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: 1.5.0
62
- - - "<"
59
+ - - "~>"
63
60
  - !ruby/object:Gem::Version
64
- version: '2.0'
61
+ version: '1.5'
65
62
  type: :development
66
63
  prerelease: false
67
64
  version_requirements: !ruby/object:Gem::Requirement
68
65
  requirements:
69
- - - ">="
70
- - !ruby/object:Gem::Version
71
- version: 1.5.0
72
- - - "<"
66
+ - - "~>"
73
67
  - !ruby/object:Gem::Version
74
- version: '2.0'
68
+ version: '1.5'
75
69
  - !ruby/object:Gem::Dependency
76
70
  name: rake
77
71
  requirement: !ruby/object:Gem::Requirement
@@ -116,6 +110,7 @@ files:
116
110
  - LICENSE.txt
117
111
  - README.md
118
112
  - Rakefile
113
+ - appveyor.yml
119
114
  - bin/console
120
115
  - bin/setup
121
116
  - lib/tty-file.rb
@@ -124,6 +119,7 @@ files:
124
119
  - lib/tty/file/differ.rb
125
120
  - lib/tty/file/digest_file.rb
126
121
  - lib/tty/file/download_file.rb
122
+ - lib/tty/file/read_backward_file.rb
127
123
  - lib/tty/file/version.rb
128
124
  - tasks/console.rake
129
125
  - tasks/coverage.rake