tty-file 0.1.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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +3 -0
- data/.travis.yml +27 -0
- data/CHANGELOG.md +7 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +21 -0
- data/README.md +302 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/tty-file.rb +1 -0
- data/lib/tty/file.rb +497 -0
- data/lib/tty/file/create_file.rb +104 -0
- data/lib/tty/file/differ.rb +78 -0
- data/lib/tty/file/download_file.rb +56 -0
- data/lib/tty/file/version.rb +7 -0
- data/tasks/console.rake +10 -0
- data/tasks/coverage.rake +11 -0
- data/tasks/spec.rake +29 -0
- data/tty-file.gemspec +29 -0
- metadata +155 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
module File
|
5
|
+
class CreateFile
|
6
|
+
attr_reader :relative_path, :content, :options, :prompt
|
7
|
+
|
8
|
+
def initialize(relative_path, content, options = {})
|
9
|
+
@content = content
|
10
|
+
@options = options
|
11
|
+
@relative_path = convert_encoded_path(relative_path)
|
12
|
+
@prompt = TTY::Prompt.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def exist?
|
16
|
+
::File.exist?(relative_path)
|
17
|
+
end
|
18
|
+
|
19
|
+
def identical?
|
20
|
+
::File.binread(relative_path) == content
|
21
|
+
end
|
22
|
+
|
23
|
+
def log_status(*args)
|
24
|
+
TTY::File.log_status(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create a file
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def call
|
31
|
+
detect_collision do
|
32
|
+
FileUtils.mkdir_p(::File.dirname(relative_path))
|
33
|
+
::File.open(relative_path, 'wb') { |f| f.write(content) }
|
34
|
+
end
|
35
|
+
relative_path
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def context
|
41
|
+
options[:context]
|
42
|
+
end
|
43
|
+
|
44
|
+
def convert_encoded_path(filename)
|
45
|
+
filename.gsub(/%(.*?)%/) do |match|
|
46
|
+
method = $1.strip
|
47
|
+
if context.respond_to?(method, true)
|
48
|
+
context.public_send(method)
|
49
|
+
else
|
50
|
+
match
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Check if file already exists and ask for user input on collision
|
56
|
+
#
|
57
|
+
# @api private
|
58
|
+
def detect_collision
|
59
|
+
if exist?
|
60
|
+
if identical?
|
61
|
+
log_status(:identical, relative_path, options.fetch(:verbose, true), :blue)
|
62
|
+
elsif options[:force]
|
63
|
+
log_status(:force, relative_path, options.fetch(:verbose, true), :yellow)
|
64
|
+
elsif options[:skip]
|
65
|
+
log_status(:skip, relative_path, options.fetch(:verbose, true), :yellow)
|
66
|
+
else
|
67
|
+
log_status(:collision, relative_path, options.fetch(:verbose, true), :red)
|
68
|
+
yield if file_collision(relative_path, content)
|
69
|
+
end
|
70
|
+
else
|
71
|
+
log_status(:create, relative_path, options.fetch(:verbose, true), :green)
|
72
|
+
return if options[:noop]
|
73
|
+
yield
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Display conflict resolution menu and gather answer
|
78
|
+
#
|
79
|
+
# @api private
|
80
|
+
def file_collision(relative_path, content)
|
81
|
+
choices = [
|
82
|
+
{ key: 'y', name: 'yes, overwrite', value: :yes },
|
83
|
+
{ key: 'n', name: 'no, do not overwrite', value: :no },
|
84
|
+
{ key: 'q', name: 'quit, abort', value: :quit },
|
85
|
+
{ key: 'd', name: 'diff, compare files line by line', value: :diff }
|
86
|
+
]
|
87
|
+
answer = prompt.expand("Overwrite #{relative_path}?", choices)
|
88
|
+
interpret_answer(answer)
|
89
|
+
end
|
90
|
+
|
91
|
+
# @api private
|
92
|
+
def interpret_answer(answer)
|
93
|
+
case answer
|
94
|
+
when :yes
|
95
|
+
true
|
96
|
+
when :no
|
97
|
+
false
|
98
|
+
when :quit
|
99
|
+
abort
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end # CreateFile
|
103
|
+
end # File
|
104
|
+
end # TTY
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'diff/lcs'
|
4
|
+
require 'diff/lcs/hunk'
|
5
|
+
require 'enumerator'
|
6
|
+
|
7
|
+
module TTY
|
8
|
+
module File
|
9
|
+
class Differ
|
10
|
+
# Create a Differ
|
11
|
+
#
|
12
|
+
# @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)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Find character difference between two strings
|
21
|
+
#
|
22
|
+
# @return [String]
|
23
|
+
# the difference between content or empty if no
|
24
|
+
# difference found
|
25
|
+
#
|
26
|
+
# @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)
|
31
|
+
format_hunks(hunks)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def convert_to_lines(string)
|
37
|
+
string.split(/\n/).map(&:chomp)
|
38
|
+
end
|
39
|
+
|
40
|
+
def string_a_lines
|
41
|
+
convert_to_lines(@string_a)
|
42
|
+
end
|
43
|
+
|
44
|
+
def string_b_lines
|
45
|
+
convert_to_lines(@string_b)
|
46
|
+
end
|
47
|
+
|
48
|
+
# @api public
|
49
|
+
def extract_hunks(diffs)
|
50
|
+
file_length_difference = 0
|
51
|
+
|
52
|
+
diffs.map do |piece|
|
53
|
+
hunk = Diff::LCS::Hunk.new(string_a_lines, string_b_lines, piece,
|
54
|
+
@context_lines, file_length_difference)
|
55
|
+
file_length_difference = hunk.file_length_difference
|
56
|
+
hunk
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# @api public
|
61
|
+
def format_hunks(hunks)
|
62
|
+
output = ""
|
63
|
+
hunks.each_cons(2) do |prev_hunk, current_hunk|
|
64
|
+
begin
|
65
|
+
if current_hunk.overlaps?(prev_hunk)
|
66
|
+
current_hunk.unshift(prev_hunk)
|
67
|
+
else
|
68
|
+
output << prev_hunk.diff(@format).to_s
|
69
|
+
end
|
70
|
+
ensure
|
71
|
+
output << "\n"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
output << hunks.last.diff(@format) << "\n" if hunks.last
|
75
|
+
end
|
76
|
+
end # Differ
|
77
|
+
end # File
|
78
|
+
end # TTY
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
module TTY
|
7
|
+
module File
|
8
|
+
DownloadError = Class.new(StandardError)
|
9
|
+
|
10
|
+
class DownloadFile
|
11
|
+
attr_reader :uri, :dest_path, :limit
|
12
|
+
|
13
|
+
DEFAULT_REDIRECTS = 3
|
14
|
+
|
15
|
+
# @options
|
16
|
+
#
|
17
|
+
def initialize(url, dest_path, options = {})
|
18
|
+
@uri = URI.parse(url)
|
19
|
+
@dest_path = dest_path
|
20
|
+
@limit = options.fetch(:limit) { DEFAULT_REDIRECTS }
|
21
|
+
end
|
22
|
+
|
23
|
+
# Download a file
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
def call
|
27
|
+
download(uri, dest_path, limit)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# @api private
|
33
|
+
def download(uri, path, limit)
|
34
|
+
raise DownloadError, 'Redirect limit reached!' if limit.zero?
|
35
|
+
content = ''
|
36
|
+
|
37
|
+
Net::HTTP.start(uri.host, uri.port,
|
38
|
+
use_ssl: uri.scheme == 'https') do |http|
|
39
|
+
http.request_get(uri.path) do |response|
|
40
|
+
case response
|
41
|
+
when Net::HTTPSuccess
|
42
|
+
response.read_body do |seg|
|
43
|
+
content << seg
|
44
|
+
end
|
45
|
+
when Net::HTTPRedirection
|
46
|
+
download(URI.parse(response['location']), path, limit - 1)
|
47
|
+
else
|
48
|
+
response.error!
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
content
|
53
|
+
end
|
54
|
+
end # DownloadFile
|
55
|
+
end # File
|
56
|
+
end # TTY
|
data/tasks/console.rake
ADDED
data/tasks/coverage.rake
ADDED
data/tasks/spec.rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
desc 'Run all specs'
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |task|
|
8
|
+
task.pattern = 'spec/{unit,integration}{,/*/**}/*_spec.rb'
|
9
|
+
end
|
10
|
+
|
11
|
+
namespace :spec do
|
12
|
+
desc 'Run unit specs'
|
13
|
+
RSpec::Core::RakeTask.new(:unit) do |task|
|
14
|
+
task.pattern = 'spec/unit{,/*/**}/*_spec.rb'
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Run integration specs'
|
18
|
+
RSpec::Core::RakeTask.new(:integration) do |task|
|
19
|
+
task.pattern = 'spec/integration{,/*/**}/*_spec.rb'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
rescue LoadError
|
24
|
+
%w[spec spec:unit spec:integration].each do |name|
|
25
|
+
task name do
|
26
|
+
$stderr.puts "In order to run #{name}, do `gem install rspec`"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/tty-file.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tty/file/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "tty-file"
|
8
|
+
spec.version = TTY::File::VERSION
|
9
|
+
spec.authors = ["Piotr Murach"]
|
10
|
+
spec.email = [""]
|
11
|
+
|
12
|
+
spec.summary = %q{File manipulation utility methods}
|
13
|
+
spec.description = %q{File manipulation utility methods}
|
14
|
+
spec.homepage = "https://piotrmurach.github.io/tty"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency 'pastel', '~> 0.6.0'
|
23
|
+
spec.add_dependency 'tty-prompt', '~> 0.7.0'
|
24
|
+
spec.add_dependency 'diff-lcs', '~> 1.2.5'
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '>= 1.5.0', '< 2.0'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tty-file
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Piotr Murach
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-11-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pastel
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.6.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.6.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: tty-prompt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.7.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.7.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: diff-lcs
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.2.5
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.2.5
|
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.0
|
62
|
+
- - "<"
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '2.0'
|
65
|
+
type: :development
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: 1.5.0
|
72
|
+
- - "<"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '2.0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rake
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '10.0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '10.0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rspec
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '3.0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '3.0'
|
103
|
+
description: File manipulation utility methods
|
104
|
+
email:
|
105
|
+
- ''
|
106
|
+
executables: []
|
107
|
+
extensions: []
|
108
|
+
extra_rdoc_files: []
|
109
|
+
files:
|
110
|
+
- ".gitignore"
|
111
|
+
- ".rspec"
|
112
|
+
- ".travis.yml"
|
113
|
+
- CHANGELOG.md
|
114
|
+
- CODE_OF_CONDUCT.md
|
115
|
+
- Gemfile
|
116
|
+
- LICENSE.txt
|
117
|
+
- README.md
|
118
|
+
- Rakefile
|
119
|
+
- bin/console
|
120
|
+
- bin/setup
|
121
|
+
- lib/tty-file.rb
|
122
|
+
- lib/tty/file.rb
|
123
|
+
- lib/tty/file/create_file.rb
|
124
|
+
- lib/tty/file/differ.rb
|
125
|
+
- lib/tty/file/download_file.rb
|
126
|
+
- lib/tty/file/version.rb
|
127
|
+
- tasks/console.rake
|
128
|
+
- tasks/coverage.rake
|
129
|
+
- tasks/spec.rake
|
130
|
+
- tty-file.gemspec
|
131
|
+
homepage: https://piotrmurach.github.io/tty
|
132
|
+
licenses:
|
133
|
+
- MIT
|
134
|
+
metadata: {}
|
135
|
+
post_install_message:
|
136
|
+
rdoc_options: []
|
137
|
+
require_paths:
|
138
|
+
- lib
|
139
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
requirements: []
|
150
|
+
rubyforge_project:
|
151
|
+
rubygems_version: 2.5.1
|
152
|
+
signing_key:
|
153
|
+
specification_version: 4
|
154
|
+
summary: File manipulation utility methods
|
155
|
+
test_files: []
|