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.
@@ -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
@@ -1,21 +1,26 @@
1
- # encoding: utf-8
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, :options, :prompt
9
+ attr_reader :base, :relative_path, :content, :prompt, :context
8
10
 
9
- def initialize(base, relative_path, content, options = {})
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
- @options = options
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, 'wb') { |f| f.write(content) }
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 options[:force]
65
+ elsif @force
61
66
  notify(:force, :yellow)
62
- yield unless options[:noop]
63
- elsif options[:skip]
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 options[:noop]
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
- options.fetch(:verbose, true), options.fetch(:color, color))
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: 'y', name: 'yes, overwrite', value: :yes },
88
- { key: 'n', name: 'no, do not overwrite', value: :no },
89
- { key: 'q', name: 'quit, abort', value: :quit }
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
@@ -1,8 +1,7 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- require 'diff/lcs'
4
- require 'diff/lcs/hunk'
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(string_a, string_b, options = {})
14
- @string_a = string_a
15
- @string_b = string_b
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
- diffs = Diff::LCS.diff(string_a_lines, string_b_lines)
29
- return '' if diffs.empty?
30
- hunks = extract_hunks(diffs)
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
- private
35
-
36
- def convert_to_lines(string)
37
- string.split(/\n/).map(&:chomp)
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
- def string_a_lines
41
- convert_to_lines(@string_a)
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
- def string_b_lines
45
- convert_to_lines(@string_b)
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 public
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 public
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
@@ -1,21 +1,21 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- require 'stringio'
4
- require 'openssl'
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, options)
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, 'rb') { |f| checksum_io(f, @digest) }
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
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- require 'uri'
4
- require 'net/http'
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, options = {})
17
+ def initialize(url, dest_path, limit: nil)
18
18
  @uri = URI.parse(url)
19
19
  @dest_path = dest_path
20
- @limit = options.fetch(:limit) { DEFAULT_REDIRECTS }
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, 'Redirect limit reached!' if limit.zero?
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 == 'https') do |http|
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['location']), path, limit - 1)
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
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  class ReadBackwardFile
@@ -1,7 +1,7 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module TTY
4
4
  module File
5
- VERSION = '0.6.0'.freeze
5
+ VERSION = "0.10.0"
6
6
  end # File
7
7
  end # TTY
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.6.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-21 00:00:00.000000000 Z
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.7.2
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.7.2
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.16.1
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.16.1
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.0
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.0
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: '10.0'
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: '10.0'
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.0
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.0
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
- - tasks/console.rake
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
- rubyforge_project:
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.