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.
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