tty-file 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +18 -2
  3. data/README.md +58 -16
  4. data/Rakefile +1 -1
  5. data/lib/tty-file.rb +0 -2
  6. data/lib/tty/file.rb +35 -9
  7. data/lib/tty/file/create_file.rb +3 -1
  8. data/lib/tty/file/differ.rb +3 -2
  9. data/lib/tty/file/digest_file.rb +1 -1
  10. data/lib/tty/file/download_file.rb +3 -3
  11. data/lib/tty/file/read_backward_file.rb +0 -1
  12. data/lib/tty/file/version.rb +2 -2
  13. data/spec/fixtures/cli_app/%name%_cli.rb +2 -0
  14. data/spec/fixtures/cli_app/commands/subcommand.rb +2 -0
  15. data/spec/fixtures/cli_app/excluded/%name%_cli.rb +2 -0
  16. data/spec/fixtures/templates/%file_name%.rb +1 -0
  17. data/spec/fixtures/templates/unit_test.rb +1 -0
  18. data/spec/spec_helper.rb +90 -0
  19. data/spec/unit/append_to_file_spec.rb +85 -0
  20. data/spec/unit/binary_spec.rb +206 -0
  21. data/spec/unit/checksum_file_spec.rb +39 -0
  22. data/spec/unit/chmod_spec.rb +78 -0
  23. data/spec/unit/copy_directory_spec.rb +106 -0
  24. data/spec/unit/copy_file_spec.rb +157 -0
  25. data/spec/unit/create_directory_spec.rb +79 -0
  26. data/spec/unit/create_file_spec.rb +116 -0
  27. data/spec/unit/diff_spec.rb +93 -0
  28. data/spec/unit/differ/call_spec.rb +101 -0
  29. data/spec/unit/download_file_spec.rb +54 -0
  30. data/spec/unit/escape_glob_path_spec.rb +14 -0
  31. data/spec/unit/inject_into_file_spec.rb +162 -0
  32. data/spec/unit/prepend_to_file_spec.rb +98 -0
  33. data/spec/unit/remove_file_spec.rb +53 -0
  34. data/spec/unit/replace_in_file_spec.rb +126 -0
  35. data/spec/unit/tail_file_spec.rb +63 -0
  36. data/tasks/console.rake +1 -1
  37. data/tasks/coverage.rake +1 -1
  38. data/tasks/spec.rake +1 -1
  39. data/tty-file.gemspec +5 -4
  40. metadata +30 -13
  41. data/.gitignore +0 -10
  42. data/.rspec +0 -3
  43. data/.travis.yml +0 -26
  44. data/CODE_OF_CONDUCT.md +0 -49
  45. data/Gemfile +0 -9
  46. 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