tty-file 0.6.0 → 0.7.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 +18 -2
- data/README.md +58 -16
- data/Rakefile +1 -1
- data/lib/tty-file.rb +0 -2
- data/lib/tty/file.rb +35 -9
- data/lib/tty/file/create_file.rb +3 -1
- data/lib/tty/file/differ.rb +3 -2
- data/lib/tty/file/digest_file.rb +1 -1
- data/lib/tty/file/download_file.rb +3 -3
- data/lib/tty/file/read_backward_file.rb +0 -1
- data/lib/tty/file/version.rb +2 -2
- data/spec/fixtures/cli_app/%name%_cli.rb +2 -0
- data/spec/fixtures/cli_app/commands/subcommand.rb +2 -0
- data/spec/fixtures/cli_app/excluded/%name%_cli.rb +2 -0
- data/spec/fixtures/templates/%file_name%.rb +1 -0
- data/spec/fixtures/templates/unit_test.rb +1 -0
- data/spec/spec_helper.rb +90 -0
- data/spec/unit/append_to_file_spec.rb +85 -0
- data/spec/unit/binary_spec.rb +206 -0
- data/spec/unit/checksum_file_spec.rb +39 -0
- data/spec/unit/chmod_spec.rb +78 -0
- data/spec/unit/copy_directory_spec.rb +106 -0
- data/spec/unit/copy_file_spec.rb +157 -0
- data/spec/unit/create_directory_spec.rb +79 -0
- data/spec/unit/create_file_spec.rb +116 -0
- data/spec/unit/diff_spec.rb +93 -0
- data/spec/unit/differ/call_spec.rb +101 -0
- data/spec/unit/download_file_spec.rb +54 -0
- data/spec/unit/escape_glob_path_spec.rb +14 -0
- data/spec/unit/inject_into_file_spec.rb +162 -0
- data/spec/unit/prepend_to_file_spec.rb +98 -0
- data/spec/unit/remove_file_spec.rb +53 -0
- data/spec/unit/replace_in_file_spec.rb +126 -0
- data/spec/unit/tail_file_spec.rb +63 -0
- data/tasks/console.rake +1 -1
- data/tasks/coverage.rake +1 -1
- data/tasks/spec.rake +1 -1
- data/tty-file.gemspec +5 -4
- metadata +30 -13
- 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/appveyor.yml +0 -26
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe TTY::File, '#copy_directory' do
|
4
|
+
it "copies directory of files recursively" do
|
5
|
+
app = tmp_path('cli_app')
|
6
|
+
apps = tmp_path('apps')
|
7
|
+
|
8
|
+
variables = OpenStruct.new
|
9
|
+
variables[:name] = 'tty'
|
10
|
+
variables[:foo] = 'Foo'
|
11
|
+
variables[:bar] = 'Bar'
|
12
|
+
|
13
|
+
TTY::File.copy_directory(app, apps, context: variables, verbose: false)
|
14
|
+
|
15
|
+
expect(Find.find(apps).to_a).to eq([
|
16
|
+
tmp_path('apps'),
|
17
|
+
tmp_path('apps/README'),
|
18
|
+
tmp_path('apps/command.rb'),
|
19
|
+
tmp_path('apps/commands'),
|
20
|
+
tmp_path('apps/commands/subcommand.rb'),
|
21
|
+
tmp_path('apps/excluded'),
|
22
|
+
tmp_path('apps/excluded/command.rb'),
|
23
|
+
tmp_path('apps/excluded/tty_cli.rb'),
|
24
|
+
tmp_path('apps/tty_cli.rb')
|
25
|
+
])
|
26
|
+
|
27
|
+
expect(File.read(tmp_path('apps/command.rb'))).to eq("class FooCommand\nend\n")
|
28
|
+
expect(File.read(tmp_path('apps/excluded/command.rb'))).to eq("class BarCommand\nend\n")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "copies top level directory of files and evalutes templates" do
|
32
|
+
app = tmp_path('cli_app')
|
33
|
+
apps = tmp_path('apps')
|
34
|
+
|
35
|
+
variables = OpenStruct.new
|
36
|
+
variables[:name] = 'tty'
|
37
|
+
variables[:foo] = 'Foo'
|
38
|
+
variables[:bar] = 'Bar'
|
39
|
+
|
40
|
+
TTY::File.copy_directory(app, apps, recursive: false,
|
41
|
+
context: variables,
|
42
|
+
verbose: false)
|
43
|
+
|
44
|
+
expect(Find.find(apps).to_a).to eq([
|
45
|
+
tmp_path('apps'),
|
46
|
+
tmp_path('apps/README'),
|
47
|
+
tmp_path('apps/command.rb'),
|
48
|
+
tmp_path('apps/tty_cli.rb')
|
49
|
+
])
|
50
|
+
end
|
51
|
+
|
52
|
+
it "handles glob characters in the path" do
|
53
|
+
src = tmp_path("foo[1]")
|
54
|
+
dest = tmp_path("foo1")
|
55
|
+
TTY::File.copy_directory(src, dest, verbose: false)
|
56
|
+
|
57
|
+
expect(Find.find(dest).to_a).to eq([
|
58
|
+
tmp_path('foo1'),
|
59
|
+
tmp_path('foo1/README.md')
|
60
|
+
])
|
61
|
+
end
|
62
|
+
|
63
|
+
it "ignores excluded directories" do
|
64
|
+
src = tmp_path('cli_app')
|
65
|
+
dest = tmp_path('ignored')
|
66
|
+
|
67
|
+
variables = OpenStruct.new
|
68
|
+
variables[:name] = 'tty'
|
69
|
+
variables[:foo] = 'Foo'
|
70
|
+
variables[:bar] = 'Bar'
|
71
|
+
|
72
|
+
TTY::File.copy_directory(src, dest, context: variables,
|
73
|
+
exclude: %r{excluded/},
|
74
|
+
verbose: false)
|
75
|
+
|
76
|
+
expect(Find.find(dest).to_a).to eq([
|
77
|
+
tmp_path('ignored'),
|
78
|
+
tmp_path('ignored/README'),
|
79
|
+
tmp_path('ignored/command.rb'),
|
80
|
+
tmp_path('ignored/commands'),
|
81
|
+
tmp_path('ignored/commands/subcommand.rb'),
|
82
|
+
tmp_path('ignored/tty_cli.rb')
|
83
|
+
])
|
84
|
+
end
|
85
|
+
|
86
|
+
it "raises error when source directory doesn't exist" do
|
87
|
+
expect {
|
88
|
+
TTY::File.copy_directory('unknown')
|
89
|
+
}.to raise_error(ArgumentError, %r{File path "unknown" does not exist.})
|
90
|
+
end
|
91
|
+
|
92
|
+
it "logs status" do
|
93
|
+
app = tmp_path('cli_app')
|
94
|
+
apps = tmp_path('apps')
|
95
|
+
|
96
|
+
variables = OpenStruct.new
|
97
|
+
variables[:name] = 'tty'
|
98
|
+
variables[:class_name] = 'TTY'
|
99
|
+
|
100
|
+
expect {
|
101
|
+
TTY::File.copy_directory(app, apps, context: variables, verbose: true)
|
102
|
+
}.to output(
|
103
|
+
%r{create(.*)apps/tty_cli.rb\n(.*)create(.*)apps/README\n(.*)create(.*)apps/command.rb\n}m
|
104
|
+
).to_stdout_from_any_process
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
RSpec.describe TTY::File, '#copy_file' do
|
5
|
+
def exists_and_identical?(source, dest)
|
6
|
+
dest_path = File.join(tmp_path, dest)
|
7
|
+
expect(::File.exist?(dest_path)).to be(true)
|
8
|
+
|
9
|
+
source_path = File.join(fixtures_path, source)
|
10
|
+
expect(::FileUtils).to be_identical(source_path, dest_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "copies file without destination" do
|
14
|
+
src = tmp_path('Gemfile')
|
15
|
+
|
16
|
+
TTY::File.copy_file(src, verbose: false)
|
17
|
+
|
18
|
+
exists_and_identical?('Gemfile', 'Gemfile')
|
19
|
+
end
|
20
|
+
|
21
|
+
it "copies file to the destination" do
|
22
|
+
src = tmp_path('Gemfile')
|
23
|
+
dest = tmp_path('app/Makefile')
|
24
|
+
|
25
|
+
TTY::File.copy_file(src, dest, verbose: false)
|
26
|
+
|
27
|
+
exists_and_identical?('Gemfile', 'app/Makefile')
|
28
|
+
end
|
29
|
+
|
30
|
+
it "copies file to existing destination" do
|
31
|
+
src = tmp_path('Gemfile')
|
32
|
+
dest = tmp_path('app/Gemfile')
|
33
|
+
|
34
|
+
TTY::File.copy_file(src, dest, verbose: false)
|
35
|
+
|
36
|
+
exists_and_identical?('Gemfile', 'app/Gemfile')
|
37
|
+
end
|
38
|
+
|
39
|
+
it "copies file with block content" do
|
40
|
+
src = tmp_path('Gemfile')
|
41
|
+
dest = tmp_path('app/Gemfile')
|
42
|
+
|
43
|
+
TTY::File.copy_file(src, dest, verbose: false) do |content|
|
44
|
+
"https://rubygems.org\n" + content
|
45
|
+
end
|
46
|
+
expect(File.read(dest)).to eq("https://rubygems.org\ngem 'nokogiri'\ngem 'rails', '5.0.0'\ngem 'rack', '>=1.0'\n")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "copies file and preservs metadata" do
|
50
|
+
src = tmp_path('Gemfile')
|
51
|
+
dest = tmp_path('app/Gemfile')
|
52
|
+
|
53
|
+
expect {
|
54
|
+
TTY::File.copy_file(src, dest, verbose: false, preserve: true)
|
55
|
+
}.to output('').to_stdout_from_any_process
|
56
|
+
|
57
|
+
expect(File.stat(src)).to eq(File.stat(dest))
|
58
|
+
end
|
59
|
+
|
60
|
+
it "doesn't copy file if :noop is true" do
|
61
|
+
src = tmp_path('Gemfile')
|
62
|
+
dest = tmp_path('app/Gemfile')
|
63
|
+
|
64
|
+
TTY::File.copy_file(src, dest, verbose: false, noop: true)
|
65
|
+
|
66
|
+
expect(File.exist?(dest)).to eq(false)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "logs status" do
|
70
|
+
src = tmp_path('Gemfile')
|
71
|
+
dest = tmp_path('app/Gemfile')
|
72
|
+
|
73
|
+
expect {
|
74
|
+
TTY::File.copy_file(src, dest)
|
75
|
+
}.to output(/\e\[32mcreate\e\[0m.*Gemfile/).to_stdout_from_any_process
|
76
|
+
end
|
77
|
+
|
78
|
+
it "logs status without color" do
|
79
|
+
src = tmp_path('Gemfile')
|
80
|
+
dest = tmp_path('app/Gemfile')
|
81
|
+
|
82
|
+
expect {
|
83
|
+
TTY::File.copy_file(src, dest, color: false)
|
84
|
+
}.to output(/\s+create.*Gemfile/).to_stdout_from_any_process
|
85
|
+
end
|
86
|
+
|
87
|
+
it "removes template .erb extension" do
|
88
|
+
src = tmp_path('templates/application.html.erb')
|
89
|
+
|
90
|
+
TTY::File.copy_file(src, verbose: false)
|
91
|
+
|
92
|
+
exists_and_identical?('templates/application.html.erb',
|
93
|
+
'templates/application.html')
|
94
|
+
end
|
95
|
+
|
96
|
+
it "converts filename based on context" do
|
97
|
+
src = tmp_path('templates/%file_name%.rb')
|
98
|
+
dest = tmp_path('app/%file_name%.rb')
|
99
|
+
|
100
|
+
variables = OpenStruct.new
|
101
|
+
variables[:foo] = 'bar'
|
102
|
+
variables[:file_name] = 'expected'
|
103
|
+
|
104
|
+
TTY::File.copy_file(src, dest, context: variables, verbose: false)
|
105
|
+
|
106
|
+
expected = tmp_path('app/expected.rb')
|
107
|
+
expect(File.read(expected)).to eq("bar\n")
|
108
|
+
end
|
109
|
+
|
110
|
+
it "converts filename based on class context" do
|
111
|
+
src = tmp_path('templates/%file_name%.rb')
|
112
|
+
dest = tmp_path('templates/expected.rb')
|
113
|
+
|
114
|
+
stub_const('TestCase', Class.new {
|
115
|
+
def foo
|
116
|
+
'bar'
|
117
|
+
end
|
118
|
+
|
119
|
+
def file_name
|
120
|
+
'expected'
|
121
|
+
end
|
122
|
+
})
|
123
|
+
TestCase.send(:include, TTY::File)
|
124
|
+
|
125
|
+
TestCase.new.send(:copy_file, src, verbose: false)
|
126
|
+
|
127
|
+
expect(File.read(dest)).to eq("bar\n")
|
128
|
+
end
|
129
|
+
|
130
|
+
it "copies file with custom class context" do
|
131
|
+
src = tmp_path('templates/unit_test.rb')
|
132
|
+
dest = tmp_path('test/unit_test.rb')
|
133
|
+
|
134
|
+
stub_const('TestCase', Class.new {
|
135
|
+
def self.class_name
|
136
|
+
'Example'
|
137
|
+
end
|
138
|
+
})
|
139
|
+
TestCase.extend(TTY::File)
|
140
|
+
|
141
|
+
TestCase.send(:copy_file, src, dest, verbose: false)
|
142
|
+
|
143
|
+
expect(File.read(dest)).to eq("class ExampleTest; end\n")
|
144
|
+
end
|
145
|
+
|
146
|
+
it "copies template with custom context binding" do
|
147
|
+
src = tmp_path('templates/unit_test.rb')
|
148
|
+
dest = tmp_path('test/unit_test.rb')
|
149
|
+
|
150
|
+
variables = OpenStruct.new
|
151
|
+
variables[:class_name] = 'Example'
|
152
|
+
|
153
|
+
TTY::File.copy_file(src, dest, context: variables, verbose: false)
|
154
|
+
|
155
|
+
expect(File.read(dest)).to eq("class ExampleTest; end\n")
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe TTY::File, '#create_directory' do
|
4
|
+
it "creates empty directory" do
|
5
|
+
app_dir = tmp_path('app')
|
6
|
+
|
7
|
+
TTY::File.create_directory(app_dir, verbose: false)
|
8
|
+
|
9
|
+
expect(File.exist?(app_dir)).to eq(true)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "logs status" do
|
13
|
+
doc_dir = tmp_path('doc')
|
14
|
+
|
15
|
+
expect {
|
16
|
+
TTY::File.create_dir(doc_dir, verbose: true)
|
17
|
+
}.to output(%r{ \e\[32mcreate\e\[0m(.*)doc\n}).to_stdout_from_any_process
|
18
|
+
end
|
19
|
+
|
20
|
+
it "logs status wihtout color" do
|
21
|
+
doc_dir = tmp_path('doc')
|
22
|
+
|
23
|
+
expect {
|
24
|
+
TTY::File.create_dir(doc_dir, verbose: true, color: false)
|
25
|
+
}.to output(%r{ create(.*)doc\n}).to_stdout_from_any_process
|
26
|
+
end
|
27
|
+
|
28
|
+
it "creates tree of dirs and files" do
|
29
|
+
app_dir = tmp_path('app')
|
30
|
+
|
31
|
+
tree = {
|
32
|
+
tmp_path('app') => [
|
33
|
+
'empty_file',
|
34
|
+
['full_file', 'File with contents'],
|
35
|
+
'subdir' => [
|
36
|
+
'empty_file_subdir',
|
37
|
+
['full_file_subdir', 'File with contents']
|
38
|
+
],
|
39
|
+
'empty' => []
|
40
|
+
]
|
41
|
+
}
|
42
|
+
|
43
|
+
TTY::File.create_directory(tree, verbose: false)
|
44
|
+
|
45
|
+
expect(Find.find(app_dir).to_a).to eq([
|
46
|
+
tmp_path('app'),
|
47
|
+
tmp_path('app/empty'),
|
48
|
+
tmp_path('app/empty_file'),
|
49
|
+
tmp_path('app/full_file'),
|
50
|
+
tmp_path('app/subdir'),
|
51
|
+
tmp_path('app/subdir/empty_file_subdir'),
|
52
|
+
tmp_path('app/subdir/full_file_subdir'),
|
53
|
+
])
|
54
|
+
|
55
|
+
expect(::File.read(tmp_path('app/subdir/full_file_subdir'))).to eq('File with contents')
|
56
|
+
end
|
57
|
+
|
58
|
+
it "creates tree of dirs in parent directory" do
|
59
|
+
app_dir = tmp_path('parent')
|
60
|
+
|
61
|
+
tree = {
|
62
|
+
'app' => [
|
63
|
+
['file', "File multi\nline contents"],
|
64
|
+
'subdir' => ['file1', 'file2']
|
65
|
+
]
|
66
|
+
}
|
67
|
+
|
68
|
+
TTY::File.create_dir(tree, app_dir, verbose: false)
|
69
|
+
|
70
|
+
expect(Find.find(app_dir).to_a).to eq([
|
71
|
+
tmp_path('parent'),
|
72
|
+
tmp_path('parent/app'),
|
73
|
+
tmp_path('parent/app/file'),
|
74
|
+
tmp_path('parent/app/subdir'),
|
75
|
+
tmp_path('parent/app/subdir/file1'),
|
76
|
+
tmp_path('parent/app/subdir/file2')
|
77
|
+
])
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe TTY::File, '#create_file' do
|
4
|
+
context 'when new file' do
|
5
|
+
it "creates file" do
|
6
|
+
expect {
|
7
|
+
TTY::File.create_file(tmp_path('doc/README.md'))
|
8
|
+
}.to output(/create/).to_stdout_from_any_process
|
9
|
+
|
10
|
+
expect(File.exist?(tmp_path('doc/README.md'))).to eq(true)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "creates file with content" do
|
14
|
+
file = tmp_path('doc/README.md')
|
15
|
+
TTY::File.create_file(file, '# Title', verbose: false)
|
16
|
+
|
17
|
+
expect(File.read(file)).to eq('# Title')
|
18
|
+
end
|
19
|
+
|
20
|
+
it "creates file with content in a block" do
|
21
|
+
file = tmp_path('doc/README.md')
|
22
|
+
TTY::File.create_file(file, verbose: false) do
|
23
|
+
"# Title"
|
24
|
+
end
|
25
|
+
|
26
|
+
expect(File.read(file)).to eq('# Title')
|
27
|
+
end
|
28
|
+
|
29
|
+
it "doesn't create file if :noop is true" do
|
30
|
+
file = tmp_path('doc/README.md')
|
31
|
+
TTY::File.create_file(file, '# Title', noop: true, verbose: false)
|
32
|
+
|
33
|
+
expect(File.exist?(file)).to eq(false)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when file exists' do
|
38
|
+
context 'and is identical' do
|
39
|
+
it "logs identical status" do
|
40
|
+
file = tmp_path('README.md')
|
41
|
+
TTY::File.create_file(file, '# Title', verbose: false)
|
42
|
+
expect {
|
43
|
+
TTY::File.create_file(file, '# Title', verbose: true)
|
44
|
+
}.to output(/identical/).to_stdout_from_any_process
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'and is not identical' do
|
49
|
+
context 'and :force is true' do
|
50
|
+
it "logs forced status to stdout" do
|
51
|
+
file = tmp_path('README.md')
|
52
|
+
TTY::File.create_file(file, '# Title', verbose: false)
|
53
|
+
expect {
|
54
|
+
TTY::File.create_file(file, '# Header', verbose: true, force: true)
|
55
|
+
}.to output(/force/).to_stdout_from_any_process
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'overrides the previous file' do
|
59
|
+
file = tmp_path('README.md')
|
60
|
+
TTY::File.create_file(file, '# Title', verbose: false)
|
61
|
+
TTY::File.create_file(file, '# Header', force: true, verbose: false)
|
62
|
+
content = File.read(file)
|
63
|
+
expect(content).to eq('# Header')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "displays collision menu and overwrites" do
|
68
|
+
test_prompt = TTY::TestPrompt.new
|
69
|
+
test_prompt.input << "\n"
|
70
|
+
test_prompt.input.rewind
|
71
|
+
allow(TTY::Prompt).to receive(:new).and_return(test_prompt)
|
72
|
+
|
73
|
+
file = tmp_path('README.md')
|
74
|
+
TTY::File.create_file(file, '# Title', verbose: false)
|
75
|
+
|
76
|
+
expect {
|
77
|
+
TTY::File.create_file(file, '# Header', verbose: true)
|
78
|
+
}.to output(/collision/).to_stdout_from_any_process
|
79
|
+
|
80
|
+
expect(File.read(file)).to eq('# Header')
|
81
|
+
end
|
82
|
+
|
83
|
+
it "displays collision menu and doesn't overwrite" do
|
84
|
+
test_prompt = TTY::TestPrompt.new
|
85
|
+
test_prompt.input << "n\n"
|
86
|
+
test_prompt.input.rewind
|
87
|
+
allow(TTY::Prompt).to receive(:new).and_return(test_prompt)
|
88
|
+
|
89
|
+
file = tmp_path('README.md')
|
90
|
+
TTY::File.create_file(file, '# Title', verbose: false)
|
91
|
+
|
92
|
+
expect {
|
93
|
+
TTY::File.create_file(file, '# Header', verbose: true)
|
94
|
+
}.to output(/collision/).to_stdout_from_any_process
|
95
|
+
|
96
|
+
expect(File.read(file)).to eq('# Title')
|
97
|
+
end
|
98
|
+
|
99
|
+
it "displays collision menu and aborts" do
|
100
|
+
test_prompt = TTY::TestPrompt.new
|
101
|
+
test_prompt.input << "q\n"
|
102
|
+
test_prompt.input.rewind
|
103
|
+
allow(TTY::Prompt).to receive(:new).and_return(test_prompt)
|
104
|
+
|
105
|
+
file = tmp_path('README.md')
|
106
|
+
TTY::File.create_file(file, '# Title', verbose: false)
|
107
|
+
|
108
|
+
expect {
|
109
|
+
TTY::File.create_file(file, '# Header', verbose: false)
|
110
|
+
}.to raise_error(SystemExit)
|
111
|
+
|
112
|
+
expect(File.read(file)).to eq('# Title')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|