tty-file 0.6.0 → 0.10.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 +5 -5
- data/CHANGELOG.md +63 -2
- data/README.md +191 -80
- data/lib/tty-file.rb +1 -3
- data/lib/tty/file.rb +383 -242
- data/lib/tty/file/compare_files.rb +70 -0
- data/lib/tty/file/create_file.rb +35 -19
- data/lib/tty/file/differ.rb +47 -25
- data/lib/tty/file/digest_file.rb +5 -5
- data/lib/tty/file/download_file.rb +10 -10
- data/lib/tty/file/read_backward_file.rb +0 -1
- data/lib/tty/file/version.rb +2 -2
- metadata +32 -50
- data/.gitignore +0 -10
- data/.rspec +0 -3
- data/.travis.yml +0 -26
- data/CODE_OF_CONDUCT.md +0 -49
- data/Gemfile +0 -9
- data/Rakefile +0 -10
- data/appveyor.yml +0 -26
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/tasks/console.rake +0 -10
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -29
- data/tty-file.gemspec +0 -32
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "differ"
|
4
|
+
|
5
|
+
module TTY
|
6
|
+
module File
|
7
|
+
class CompareFiles
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def initialize(format: :unified, header: true, context_lines: 5,
|
11
|
+
verbose: true, color: :green, noop: false, diff_colors: nil)
|
12
|
+
@format = format
|
13
|
+
@header = header
|
14
|
+
@context_lines = context_lines
|
15
|
+
@verbose = verbose
|
16
|
+
@color = color
|
17
|
+
@noop = noop
|
18
|
+
@diff_colors = diff_colors
|
19
|
+
end
|
20
|
+
|
21
|
+
# Compare files
|
22
|
+
#
|
23
|
+
# @api public
|
24
|
+
def call(file_a, file_b, file_a_path, file_b_path)
|
25
|
+
differ = Differ.new(format: @format, context_lines: @context_lines)
|
26
|
+
block_size = file_a.lstat.blksize
|
27
|
+
file_a_chunk = file_a.read(block_size)
|
28
|
+
file_b_chunk = file_b.read(block_size)
|
29
|
+
hunks = differ.(file_a_chunk, file_b_chunk)
|
30
|
+
|
31
|
+
return "" if file_a_chunk.empty? && file_b_chunk.empty?
|
32
|
+
return "No differences found\n" if hunks.empty?
|
33
|
+
|
34
|
+
output = []
|
35
|
+
|
36
|
+
if %i[unified context old].include?(@format) && @header
|
37
|
+
output << "#{differ.delete_char * 3} #{file_a_path}\n"
|
38
|
+
output << "#{differ.add_char * 3} #{file_b_path}"
|
39
|
+
end
|
40
|
+
|
41
|
+
output << "\n" unless hunks =~ /\A\n+@@/
|
42
|
+
output << hunks
|
43
|
+
while !file_a.eof? && !file_b.eof?
|
44
|
+
output << differ.(file_a.read(block_size), file_b.read(block_size))
|
45
|
+
end
|
46
|
+
color_diff_lines(output.join)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# @api private
|
52
|
+
def color_diff_lines(hunks)
|
53
|
+
return hunks unless @color && @format == :unified
|
54
|
+
|
55
|
+
newline = "\n"
|
56
|
+
hunks.lines.map do |line|
|
57
|
+
if matched = line.to_s.match(/^(\+[^+]*?)\n/)
|
58
|
+
@diff_colors[:green].(matched[1]) + newline
|
59
|
+
elsif matched = line.to_s.match(/^(\-[^-].*?)\n/)
|
60
|
+
@diff_colors[:red].(matched[1]) + newline
|
61
|
+
elsif matched = line.to_s.match(/^(@@.+?@@)\n/)
|
62
|
+
@diff_colors[:cyan].(matched[1]) + newline
|
63
|
+
else
|
64
|
+
line
|
65
|
+
end
|
66
|
+
end.join
|
67
|
+
end
|
68
|
+
end # CompareFiles
|
69
|
+
end # File
|
70
|
+
end # TTY
|
data/lib/tty/file/create_file.rb
CHANGED
@@ -1,21 +1,26 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tty-prompt"
|
2
4
|
|
3
5
|
module TTY
|
4
6
|
module File
|
5
7
|
class CreateFile
|
6
8
|
|
7
|
-
attr_reader :base, :relative_path, :content, :
|
9
|
+
attr_reader :base, :relative_path, :content, :prompt, :context
|
8
10
|
|
9
|
-
def initialize(base, relative_path, content,
|
11
|
+
def initialize(base, relative_path, content, context: nil, force: false,
|
12
|
+
skip: false, verbose: true, noop: false, color: :green,
|
13
|
+
quiet: true)
|
10
14
|
@base = base
|
11
15
|
@content = content
|
12
|
-
@
|
16
|
+
@context = context || @base
|
17
|
+
@force = force
|
18
|
+
@skip = skip
|
19
|
+
@noop = noop
|
20
|
+
@verbose = verbose
|
21
|
+
@color = color
|
13
22
|
@relative_path = convert_encoded_path(relative_path)
|
14
|
-
@prompt = TTY::Prompt.new
|
15
|
-
end
|
16
|
-
|
17
|
-
def context
|
18
|
-
options[:context] || @base
|
23
|
+
@prompt = TTY::Prompt.new(quiet: quiet)
|
19
24
|
end
|
20
25
|
|
21
26
|
def exist?
|
@@ -32,7 +37,7 @@ module TTY
|
|
32
37
|
def call
|
33
38
|
detect_collision do
|
34
39
|
FileUtils.mkdir_p(::File.dirname(relative_path))
|
35
|
-
::File.open(relative_path,
|
40
|
+
::File.open(relative_path, "wb") { |f| f.write(content) }
|
36
41
|
end
|
37
42
|
relative_path
|
38
43
|
end
|
@@ -57,10 +62,10 @@ module TTY
|
|
57
62
|
if exist?
|
58
63
|
if identical?
|
59
64
|
notify(:identical, :cyan)
|
60
|
-
elsif
|
65
|
+
elsif @force
|
61
66
|
notify(:force, :yellow)
|
62
|
-
yield unless
|
63
|
-
elsif
|
67
|
+
yield unless @noop
|
68
|
+
elsif @skip
|
64
69
|
notify(:skip, :yellow)
|
65
70
|
else
|
66
71
|
notify(:collision, :red)
|
@@ -68,15 +73,16 @@ module TTY
|
|
68
73
|
end
|
69
74
|
else
|
70
75
|
notify(:create, :green)
|
71
|
-
yield unless
|
76
|
+
yield unless @noop
|
72
77
|
end
|
73
78
|
end
|
74
79
|
|
75
80
|
# Notify console about performed action
|
81
|
+
#
|
76
82
|
# @api private
|
77
83
|
def notify(name, color)
|
78
84
|
base.__send__(:log_status, name, relative_path,
|
79
|
-
|
85
|
+
verbose: @verbose, color: @color ? color : false)
|
80
86
|
end
|
81
87
|
|
82
88
|
# Display conflict resolution menu and gather answer
|
@@ -84,14 +90,24 @@ module TTY
|
|
84
90
|
# @api private
|
85
91
|
def file_collision(relative_path, content)
|
86
92
|
choices = [
|
87
|
-
{ key:
|
88
|
-
{ key:
|
89
|
-
{ key:
|
93
|
+
{ key: "y", name: "yes, overwrite", value: :yes },
|
94
|
+
{ key: "d", name: "diff, compare files", value: :diff },
|
95
|
+
{ key: "n", name: "no, do not overwrite", value: :no },
|
96
|
+
{ key: "q", name: "quit, abort", value: :quit }
|
90
97
|
]
|
91
|
-
answer = prompt.expand("Overwrite #{relative_path}?", choices)
|
98
|
+
while (answer = prompt.expand("Overwrite #{relative_path}?", choices)) == :diff do
|
99
|
+
show_diff
|
100
|
+
end
|
92
101
|
interpret_answer(answer)
|
93
102
|
end
|
94
103
|
|
104
|
+
# Display difference between old and new file
|
105
|
+
#
|
106
|
+
# @api private
|
107
|
+
def show_diff
|
108
|
+
print base.__send__(:diff_files, relative_path, content, verbose: @verbose)
|
109
|
+
end
|
110
|
+
|
95
111
|
# @api private
|
96
112
|
def interpret_answer(answer)
|
97
113
|
case answer
|
data/lib/tty/file/differ.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require 'enumerator'
|
3
|
+
require "diff/lcs"
|
4
|
+
require "diff/lcs/hunk"
|
6
5
|
|
7
6
|
module TTY
|
8
7
|
module File
|
@@ -10,11 +9,9 @@ module TTY
|
|
10
9
|
# Create a Differ
|
11
10
|
#
|
12
11
|
# @api public
|
13
|
-
def initialize(
|
14
|
-
@
|
15
|
-
@
|
16
|
-
@format = options.fetch(:format, :unified)
|
17
|
-
@context_lines = options.fetch(:context_lines, 3)
|
12
|
+
def initialize(format: :unified, context_lines: 3)
|
13
|
+
@format = format
|
14
|
+
@context_lines = context_lines
|
18
15
|
end
|
19
16
|
|
20
17
|
# Find character difference between two strings
|
@@ -24,29 +21,53 @@ module TTY
|
|
24
21
|
# difference found
|
25
22
|
#
|
26
23
|
# @api public
|
27
|
-
def call
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
def call(string_a, string_b)
|
25
|
+
string_a_lines = convert_to_lines(string_a)
|
26
|
+
string_b_lines = convert_to_lines(string_b)
|
27
|
+
diffs = Diff::LCS.diff(string_a_lines, string_b_lines)
|
28
|
+
return "" if diffs.empty?
|
29
|
+
|
30
|
+
hunks = extract_hunks(diffs, string_a_lines, string_b_lines)
|
31
31
|
format_hunks(hunks)
|
32
32
|
end
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
# Diff add char
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
def add_char
|
38
|
+
case @format
|
39
|
+
when :old
|
40
|
+
">"
|
41
|
+
when :unified
|
42
|
+
"+"
|
43
|
+
else
|
44
|
+
"*"
|
45
|
+
end
|
38
46
|
end
|
39
47
|
|
40
|
-
|
41
|
-
|
48
|
+
# Diff delete char
|
49
|
+
#
|
50
|
+
# @api public
|
51
|
+
def delete_char
|
52
|
+
case @format
|
53
|
+
when :old
|
54
|
+
"<"
|
55
|
+
when :unified
|
56
|
+
"-"
|
57
|
+
else
|
58
|
+
"*"
|
59
|
+
end
|
42
60
|
end
|
43
61
|
|
44
|
-
|
45
|
-
|
62
|
+
private
|
63
|
+
|
64
|
+
# @api private
|
65
|
+
def convert_to_lines(string)
|
66
|
+
string.split(/\n/).map(&:chomp)
|
46
67
|
end
|
47
68
|
|
48
|
-
# @api
|
49
|
-
def extract_hunks(diffs)
|
69
|
+
# @api private
|
70
|
+
def extract_hunks(diffs, string_a_lines, string_b_lines)
|
50
71
|
file_length_difference = 0
|
51
72
|
|
52
73
|
diffs.map do |piece|
|
@@ -57,9 +78,9 @@ module TTY
|
|
57
78
|
end
|
58
79
|
end
|
59
80
|
|
60
|
-
# @api
|
81
|
+
# @api private
|
61
82
|
def format_hunks(hunks)
|
62
|
-
output =
|
83
|
+
output = []
|
63
84
|
hunks.each_cons(2) do |prev_hunk, current_hunk|
|
64
85
|
begin
|
65
86
|
if current_hunk.overlaps?(prev_hunk)
|
@@ -72,6 +93,7 @@ module TTY
|
|
72
93
|
end
|
73
94
|
end
|
74
95
|
output << hunks.last.diff(@format) << "\n" if hunks.last
|
96
|
+
output.join
|
75
97
|
end
|
76
98
|
end # Differ
|
77
99
|
end # File
|
data/lib/tty/file/digest_file.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "stringio"
|
4
|
+
require "openssl"
|
5
5
|
|
6
6
|
module TTY
|
7
7
|
module File
|
8
8
|
class DigestFile
|
9
9
|
attr_reader :source
|
10
10
|
|
11
|
-
def initialize(source, mode
|
11
|
+
def initialize(source, mode)
|
12
12
|
@source = source
|
13
13
|
@digest = OpenSSL::Digest.new(mode)
|
14
14
|
end
|
15
15
|
|
16
16
|
def call
|
17
17
|
if ::FileTest.file?(source.to_s)
|
18
|
-
::File.open(source,
|
18
|
+
::File.open(source, "rb") { |f| checksum_io(f, @digest) }
|
19
19
|
else
|
20
20
|
non_file = source
|
21
21
|
if non_file.is_a?(String)
|
@@ -1,7 +1,7 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "uri"
|
4
|
+
require "net/http"
|
5
5
|
|
6
6
|
module TTY
|
7
7
|
module File
|
@@ -14,10 +14,10 @@ module TTY
|
|
14
14
|
|
15
15
|
# @options
|
16
16
|
#
|
17
|
-
def initialize(url, dest_path,
|
17
|
+
def initialize(url, dest_path, limit: nil)
|
18
18
|
@uri = URI.parse(url)
|
19
19
|
@dest_path = dest_path
|
20
|
-
@limit =
|
20
|
+
@limit = limit || DEFAULT_REDIRECTS
|
21
21
|
end
|
22
22
|
|
23
23
|
# Download a file
|
@@ -31,11 +31,11 @@ module TTY
|
|
31
31
|
|
32
32
|
# @api private
|
33
33
|
def download(uri, path, limit)
|
34
|
-
raise DownloadError,
|
35
|
-
content =
|
34
|
+
raise DownloadError, "Redirect limit reached!" if limit.zero?
|
35
|
+
content = []
|
36
36
|
|
37
37
|
Net::HTTP.start(uri.host, uri.port,
|
38
|
-
use_ssl: uri.scheme ==
|
38
|
+
use_ssl: uri.scheme == "https") do |http|
|
39
39
|
http.request_get(uri.request_uri) do |response|
|
40
40
|
case response
|
41
41
|
when Net::HTTPSuccess
|
@@ -43,13 +43,13 @@ module TTY
|
|
43
43
|
content << seg
|
44
44
|
end
|
45
45
|
when Net::HTTPRedirection
|
46
|
-
download(URI.parse(response[
|
46
|
+
download(URI.parse(response["location"]), path, limit - 1)
|
47
47
|
else
|
48
48
|
response.error!
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
52
|
-
content
|
52
|
+
content.join
|
53
53
|
end
|
54
54
|
end # DownloadFile
|
55
55
|
end # File
|
data/lib/tty/file/version.rb
CHANGED
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.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Murach
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-07-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pastel
|
@@ -16,82 +16,68 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: '0.8'
|
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.
|
26
|
+
version: '0.8'
|
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.
|
33
|
+
version: '0.22'
|
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.
|
40
|
+
version: '0.22'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: diff-lcs
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 1.3
|
47
|
+
version: '1.3'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.3
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: bundler
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '1.5'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '1.5'
|
54
|
+
version: '1.3'
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: rake
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
72
58
|
requirements:
|
73
|
-
- - "
|
59
|
+
- - ">="
|
74
60
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
61
|
+
version: '0'
|
76
62
|
type: :development
|
77
63
|
prerelease: false
|
78
64
|
version_requirements: !ruby/object:Gem::Requirement
|
79
65
|
requirements:
|
80
|
-
- - "
|
66
|
+
- - ">="
|
81
67
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
68
|
+
version: '0'
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
70
|
name: rspec
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
86
72
|
requirements:
|
87
|
-
- - "
|
73
|
+
- - ">="
|
88
74
|
- !ruby/object:Gem::Version
|
89
75
|
version: '3.0'
|
90
76
|
type: :development
|
91
77
|
prerelease: false
|
92
78
|
version_requirements: !ruby/object:Gem::Requirement
|
93
79
|
requirements:
|
94
|
-
- - "
|
80
|
+
- - ">="
|
95
81
|
- !ruby/object:Gem::Version
|
96
82
|
version: '3.0'
|
97
83
|
- !ruby/object:Gem::Dependency
|
@@ -100,49 +86,46 @@ dependencies:
|
|
100
86
|
requirements:
|
101
87
|
- - "~>"
|
102
88
|
- !ruby/object:Gem::Version
|
103
|
-
version: 3.4
|
89
|
+
version: '3.4'
|
104
90
|
type: :development
|
105
91
|
prerelease: false
|
106
92
|
version_requirements: !ruby/object:Gem::Requirement
|
107
93
|
requirements:
|
108
94
|
- - "~>"
|
109
95
|
- !ruby/object:Gem::Version
|
110
|
-
version: 3.4
|
96
|
+
version: '3.4'
|
111
97
|
description: File manipulation utility methods.
|
112
98
|
email:
|
113
|
-
-
|
99
|
+
- piotr@piotrmurach.com
|
114
100
|
executables: []
|
115
101
|
extensions: []
|
116
|
-
extra_rdoc_files:
|
102
|
+
extra_rdoc_files:
|
103
|
+
- README.md
|
104
|
+
- CHANGELOG.md
|
105
|
+
- LICENSE.txt
|
117
106
|
files:
|
118
|
-
- ".gitignore"
|
119
|
-
- ".rspec"
|
120
|
-
- ".travis.yml"
|
121
107
|
- CHANGELOG.md
|
122
|
-
- CODE_OF_CONDUCT.md
|
123
|
-
- Gemfile
|
124
108
|
- LICENSE.txt
|
125
109
|
- README.md
|
126
|
-
- Rakefile
|
127
|
-
- appveyor.yml
|
128
|
-
- bin/console
|
129
|
-
- bin/setup
|
130
110
|
- lib/tty-file.rb
|
131
111
|
- lib/tty/file.rb
|
112
|
+
- lib/tty/file/compare_files.rb
|
132
113
|
- lib/tty/file/create_file.rb
|
133
114
|
- lib/tty/file/differ.rb
|
134
115
|
- lib/tty/file/digest_file.rb
|
135
116
|
- lib/tty/file/download_file.rb
|
136
117
|
- lib/tty/file/read_backward_file.rb
|
137
118
|
- lib/tty/file/version.rb
|
138
|
-
|
139
|
-
- tasks/coverage.rake
|
140
|
-
- tasks/spec.rake
|
141
|
-
- tty-file.gemspec
|
142
|
-
homepage: https://piotrmurach.github.io/tty
|
119
|
+
homepage: https://ttytoolkit.org
|
143
120
|
licenses:
|
144
121
|
- MIT
|
145
|
-
metadata:
|
122
|
+
metadata:
|
123
|
+
allowed_push_host: https://rubygems.org
|
124
|
+
bug_tracker_uri: https://github.com/piotrmurach/tty-file/issues
|
125
|
+
changelog_uri: https://github.com/piotrmurach/tty-file/blob/master/CHANGELOG.md
|
126
|
+
documentation_uri: https://www.rubydoc.info/gems/tty-file
|
127
|
+
homepage_uri: https://ttytoolkit.org
|
128
|
+
source_code_uri: https://github.com/piotrmurach/tty-file
|
146
129
|
post_install_message:
|
147
130
|
rdoc_options: []
|
148
131
|
require_paths:
|
@@ -158,8 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
158
141
|
- !ruby/object:Gem::Version
|
159
142
|
version: '0'
|
160
143
|
requirements: []
|
161
|
-
|
162
|
-
rubygems_version: 2.5.1
|
144
|
+
rubygems_version: 3.1.2
|
163
145
|
signing_key:
|
164
146
|
specification_version: 4
|
165
147
|
summary: File manipulation utility methods.
|