tty-file 0.6.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.