cuprum-cli 0.1.0 → 0.2.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +43 -0
  3. data/README.md +10 -0
  4. data/lib/cuprum/cli/argument.rb +2 -2
  5. data/lib/cuprum/cli/command.rb +2 -4
  6. data/lib/cuprum/cli/commands/ci/rspec_each_command.rb +34 -8
  7. data/lib/cuprum/cli/commands/ci.rb +8 -0
  8. data/lib/cuprum/cli/commands/file/new_command.rb +42 -49
  9. data/lib/cuprum/cli/commands/file.rb +8 -5
  10. data/lib/cuprum/cli/dependencies/clock/mock.rb +28 -0
  11. data/lib/cuprum/cli/dependencies/clock.rb +29 -0
  12. data/lib/cuprum/cli/dependencies/file_system/mock.rb +150 -18
  13. data/lib/cuprum/cli/dependencies/file_system.rb +145 -10
  14. data/lib/cuprum/cli/dependencies.rb +2 -0
  15. data/lib/cuprum/cli/errors.rb +0 -1
  16. data/lib/cuprum/cli/{commands/file/generate_file.rb → files/create_file.rb} +6 -48
  17. data/lib/cuprum/cli/{commands/file → files/engines}/render_erb.rb +6 -6
  18. data/lib/cuprum/cli/files/engines.rb +76 -0
  19. data/lib/cuprum/cli/{errors/files → files/errors}/file_not_writeable.rb +3 -3
  20. data/lib/cuprum/cli/files/errors/generator_error.rb +46 -0
  21. data/lib/cuprum/cli/{errors/files → files/errors}/missing_parameter.rb +5 -5
  22. data/lib/cuprum/cli/{errors/files → files/errors}/missing_template.rb +3 -3
  23. data/lib/cuprum/cli/{errors/files → files/errors}/template_error.rb +3 -3
  24. data/lib/cuprum/cli/files/errors.rb +19 -0
  25. data/lib/cuprum/cli/files/generator.rb +431 -0
  26. data/lib/cuprum/cli/files/generators/basic_generator.rb +15 -0
  27. data/lib/cuprum/cli/files/generators/rspec_generator.rb +19 -0
  28. data/lib/cuprum/cli/files/generators/ruby_generator.rb +32 -0
  29. data/lib/cuprum/cli/files/generators.rb +25 -0
  30. data/lib/cuprum/cli/files/template.rb +62 -0
  31. data/lib/cuprum/cli/files/templates/basic_template.rb +39 -0
  32. data/lib/cuprum/cli/files/templates/file_template.rb +60 -0
  33. data/lib/cuprum/cli/files/templates/string_template.rb +29 -0
  34. data/lib/cuprum/cli/files/templates.rb +59 -0
  35. data/lib/cuprum/cli/files.rb +18 -0
  36. data/lib/cuprum/cli/integrations/async/commands/rspec_each_command.rb +32 -0
  37. data/lib/cuprum/cli/integrations/async/commands.rb +11 -0
  38. data/lib/cuprum/cli/integrations/async.rb +21 -0
  39. data/lib/cuprum/cli/integrations/thor/registry.rb +7 -29
  40. data/lib/cuprum/cli/integrations/thor/task.rb +37 -7
  41. data/lib/cuprum/cli/metadata.rb +54 -5
  42. data/lib/cuprum/cli/option.rb +10 -4
  43. data/lib/cuprum/cli/options/class_methods.rb +19 -1
  44. data/lib/cuprum/cli/registry.rb +131 -55
  45. data/lib/cuprum/cli/rspec/deferred/dependencies/file_system_examples.rb +1303 -1001
  46. data/lib/cuprum/cli/rspec/deferred/generators_examples.rb +112 -0
  47. data/lib/cuprum/cli/rspec/deferred/metadata_examples.rb +300 -0
  48. data/lib/cuprum/cli/rspec/deferred/options_examples.rb +8 -2
  49. data/lib/cuprum/cli/rspec/deferred/registry_examples.rb +343 -108
  50. data/lib/cuprum/cli/rspec/deferred/templates_examples.rb +183 -0
  51. data/lib/cuprum/cli/version.rb +1 -1
  52. data/lib/cuprum/cli.rb +1 -0
  53. metadata +31 -14
  54. data/lib/cuprum/cli/commands/file/resolve_template.rb +0 -136
  55. data/lib/cuprum/cli/commands/file/templates.rb +0 -71
  56. data/lib/cuprum/cli/errors/files/template_not_resolved.rb +0 -54
  57. data/lib/cuprum/cli/errors/files.rb +0 -19
  58. /data/lib/cuprum/cli/{commands/file → files}/templates/rspec.rb.erb +0 -0
  59. /data/lib/cuprum/cli/{commands/file → files}/templates/ruby.rb.erb +0 -0
@@ -9,1457 +9,1759 @@ module Cuprum::Cli::RSpec::Deferred::Dependencies
9
9
  module FileSystemExamples
10
10
  include RSpec::SleepingKingStudios::Deferred::Provider
11
11
 
12
- deferred_examples 'should implement the file_system dependency' do
13
- describe '#create_directory', :writeable_root_path do
14
- let(:base_path) { nil }
15
- let(:path) { nil }
12
+ define_method :absolute_path? do |path|
13
+ path.start_with?('/')
14
+ end
16
15
 
17
- include_deferred 'with valid file paths'
16
+ define_method :qualified_path? do |path|
17
+ path.start_with?('*')
18
+ end
18
19
 
19
- include_deferred 'when created directories are cleaned up'
20
+ deferred_context 'with fixture files and directories' do
21
+ let(:fixtures_path) do
22
+ defined?(super()) ? super() : '.'
23
+ end
24
+ let(:fixtures) do
25
+ {
26
+ 'root_dir' => {
27
+ 'child_dir' => {},
28
+ 'child_file.txt' => 'Contents of root_dir/child_file.txt'
29
+ },
30
+ 'root_file.txt' => 'Contents of root_file.txt'
31
+ }
32
+ end
33
+ let(:flattened_fixture_names) do
34
+ [
35
+ 'root_dir',
36
+ 'root_dir/child_dir',
37
+ 'root_dir/child_file.txt',
38
+ 'root_file.txt'
39
+ ]
40
+ end
20
41
 
21
- it 'should define the method' do
22
- expect(subject)
23
- .to respond_to(:create_directory)
24
- .with(1).argument
25
- .and_keywords(:recursive)
42
+ define_method :read_fixture do |path|
43
+ return super(path) if defined?(super(path))
44
+
45
+ fixtures.dig(*path.split(File::SEPARATOR))
46
+ end
47
+ end
48
+
49
+ deferred_examples 'should handle a directory when a file is expected' \
50
+ do |path_parameter = 'path', &block|
51
+ context "when the #{path_parameter.to_s.tr('_', ' ')} is a directory" do
52
+ let(path_parameter) do
53
+ if absolute_path?(super())
54
+ absolute_directory_path
55
+ elsif qualified_path?(super())
56
+ qualified_directory_path
57
+ else
58
+ relative_directory_path
59
+ end
60
+ end
61
+ let(:error_class) do
62
+ Cuprum::Cli::Dependencies::FileSystem::FileIsADirectoryError
63
+ end
64
+ let(:error_message) do
65
+ "#{error_reason} - file is a directory"
26
66
  end
27
67
 
28
- it 'should define the aliased method' do
29
- expect(subject)
30
- .to have_aliased_method(:create_directory)
31
- .as(:make_directory)
68
+ it 'should raise an exception' do
69
+ expect { call_method }.to raise_error(error_class, error_message)
32
70
  end
33
71
 
34
- describe 'with nil' do
35
- let(:error_message) do
36
- tools.assertions.error_message_for('presence', as: :path)
72
+ instance_exec(&block) if block
73
+ end
74
+ end
75
+
76
+ deferred_examples 'should handle a file that already exists' \
77
+ do |path_parameter = 'path', &block|
78
+ context "when the #{path_parameter.to_s.tr('_', ' ')} is a file" do
79
+ let(path_parameter) do
80
+ if absolute_path?(super())
81
+ absolute_file_path
82
+ elsif qualified_path?(super())
83
+ qualified_file_path
84
+ else
85
+ relative_file_path
37
86
  end
87
+ end
88
+ let(:error_class) do
89
+ Cuprum::Cli::Dependencies::FileSystem::FileAlreadyExistsError
90
+ end
91
+ let(:error_message) do
92
+ "#{error_reason} - file already exists"
93
+ end
38
94
 
39
- it 'should raise an exception' do
40
- expect { subject.create_directory(nil) }
41
- .to raise_error ArgumentError, error_message
95
+ it 'should raise an exception' do
96
+ expect { call_method }.to raise_error(error_class, error_message)
97
+ end
98
+
99
+ instance_exec(&block) if block
100
+ end
101
+ end
102
+
103
+ deferred_examples 'should handle a file when a directory is expected' \
104
+ do |path_parameter = 'path', &block|
105
+ context "when the #{path_parameter.to_s.tr('_', ' ')} is a file" do
106
+ let(path_parameter) do
107
+ if absolute_path?(super())
108
+ absolute_file_path
109
+ elsif qualified_path?(super())
110
+ qualified_file_path
111
+ else
112
+ relative_file_path
42
113
  end
43
114
  end
115
+ let(:error_class) do
116
+ Cuprum::Cli::Dependencies::FileSystem::DirectoryIsAFileError
117
+ end
118
+ let(:error_message) do
119
+ "#{error_reason} - directory is a file"
120
+ end
121
+
122
+ it 'should raise an exception' do
123
+ expect { call_method }.to raise_error(error_class, error_message)
124
+ end
125
+
126
+ instance_exec(&block) if block
127
+ end
128
+ end
129
+
130
+ deferred_examples 'should handle a path that includes a file' \
131
+ do |path_parameter = 'path', &block|
132
+ context "when the #{path_parameter.to_s.tr('_', ' ')} includes a file" do
133
+ let(path_parameter) do
134
+ dirname =
135
+ if absolute_path?(super())
136
+ absolute_file_path
137
+ elsif qualified_path?(super())
138
+ qualified_file_path
139
+ else
140
+ relative_file_path
141
+ end
142
+
143
+ File.join(dirname, File.basename(super()))
144
+ end
145
+ let(:error_class) do
146
+ Cuprum::Cli::Dependencies::FileSystem::DirectoryIsAFileError
147
+ end
148
+ let(:error_message) do
149
+ "#{error_reason} - directory is a file"
150
+ end
44
151
 
45
- describe 'with an Object' do
46
- let(:error_message) do
47
- tools.assertions.error_message_for('name', as: :path)
152
+ it 'should raise an exception' do
153
+ expect { call_method }.to raise_error(error_class, error_message)
154
+ end
155
+
156
+ instance_exec(&block) if block
157
+ end
158
+ end
159
+
160
+ deferred_examples 'should handle a path with missing directory' \
161
+ do |path_parameter = 'path', &block|
162
+ parameter_name = path_parameter.to_s.tr('_', ' ')
163
+
164
+ context "when the #{parameter_name} includes a missing directory" do
165
+ let(:error_class) do
166
+ Cuprum::Cli::Dependencies::FileSystem::DirectoryNotFoundError
167
+ end
168
+ let(:error_message) do
169
+ "#{error_reason} - directory not found"
170
+ end
171
+
172
+ context "when the #{parameter_name} is an absolute path" do
173
+ let(path_parameter) do
174
+ File.join(
175
+ absolute_directory_path,
176
+ 'missing_directory',
177
+ File.basename(super())
178
+ )
48
179
  end
49
180
 
50
181
  it 'should raise an exception' do
51
- expect { subject.create_directory(Object.new.freeze) }
52
- .to raise_error ArgumentError, error_message
182
+ expect { call_method }.to raise_error(error_class, error_message)
53
183
  end
184
+
185
+ instance_exec(&block) if block
54
186
  end
55
187
 
56
- describe 'with an empty String' do
57
- let(:error_message) do
58
- tools.assertions.error_message_for('presence', as: :path)
188
+ context "when the #{parameter_name} is a qualified path" do
189
+ let(path_parameter) do
190
+ File.join(
191
+ qualified_directory_path,
192
+ 'missing_directory',
193
+ File.basename(super())
194
+ )
59
195
  end
60
196
 
61
197
  it 'should raise an exception' do
62
- expect { subject.create_directory('') }
63
- .to raise_error ArgumentError, error_message
198
+ expect { call_method }.to raise_error(error_class, error_message)
64
199
  end
65
- end
66
-
67
- describe 'with an absolute path' do
68
- let(:base_path) { absolute_directory_path }
69
- let(:path) { File.join(base_path, 'custom_dir') }
70
200
 
71
- it { expect(subject.create_directory(path)).to be == path }
201
+ instance_exec(&block) if block
202
+ end
72
203
 
73
- it 'should create the directory' do
74
- expect { subject.create_directory(path) }.to(
75
- change { subject.directory?(path) }.to(be true)
204
+ context "when the #{parameter_name} is a relative path" do
205
+ let(path_parameter) do
206
+ File.join(
207
+ relative_directory_path,
208
+ 'missing_directory',
209
+ File.basename(super())
76
210
  )
77
211
  end
78
212
 
79
- describe 'with a multi-segment path' do
80
- let(:path) { File.join(base_path, 'custom_dir/inner/path') }
81
- let(:error_class) do
82
- Cuprum::Cli::Dependencies::FileSystem::DirectoryNotFoundError
83
- end
84
- let(:error_message) do
85
- "unable to create directory #{path} - directory not found"
86
- end
213
+ it 'should raise an exception' do
214
+ expect { call_method }.to raise_error(error_class, error_message)
215
+ end
87
216
 
88
- it 'should raise an exception' do
89
- expect { subject.create_directory(path) }
90
- .to raise_error(error_class, error_message)
91
- end
217
+ instance_exec(&block) if block
218
+ end
219
+ end
220
+ end
92
221
 
93
- describe 'with recursive: true' do
94
- it 'should return the path' do
95
- expect(subject.create_directory(path, recursive: true))
96
- .to be == path
97
- end
222
+ deferred_examples 'should handle a path with missing file' \
223
+ do |path_parameter = 'path', &block|
224
+ parameter_name = path_parameter.to_s.tr('_', ' ')
98
225
 
99
- it 'should create the directory' do
100
- expect { subject.create_directory(path, recursive: true) }.to(
101
- change { subject.directory?(path) }.to(be true)
102
- )
103
- end
226
+ context "when the #{parameter_name} includes a missing file" do
227
+ let(:error_class) do
228
+ Cuprum::Cli::Dependencies::FileSystem::FileNotFoundError
229
+ end
230
+ let(:error_message) do
231
+ "#{error_reason} - file not found"
232
+ end
104
233
 
105
- context 'when the directory already exists' do
106
- before(:example) do
107
- subject.create_directory(path, recursive: true)
108
- end
234
+ context "when the #{parameter_name} is an absolute path" do
235
+ let(path_parameter) { invalid_absolute_path }
109
236
 
110
- it { expect(subject.create_directory(path)).to be == path }
111
- end
237
+ it 'should raise an exception' do
238
+ expect { call_method }.to raise_error(error_class, error_message)
239
+ end
112
240
 
113
- context 'when the path includes a file' do
114
- let(:error_class) do
115
- Cuprum::Cli::Dependencies::FileSystem::DirectoryIsAFileError
116
- end
117
- let(:error_message) do
118
- "unable to create directory #{path} - directory is a file"
119
- end
120
- let(:file_path) { File.join(base_path, 'custom_dir') }
241
+ instance_exec(&block) if block
242
+ end
121
243
 
122
- before(:example) do
123
- subject.write_file(file_path, '')
124
- end
244
+ context "when the #{parameter_name} is a qualified path" do
245
+ let(path_parameter) { invalid_qualified_path }
125
246
 
126
- include_deferred 'when created files are cleaned up'
247
+ it 'should raise an exception' do
248
+ expect { call_method }.to raise_error(error_class, error_message)
249
+ end
127
250
 
128
- it 'should raise an exception' do
129
- expect { subject.create_directory(path, recursive: true) }
130
- .to raise_error(error_class, error_message)
131
- end
251
+ instance_exec(&block) if block
252
+ end
132
253
 
133
- it 'should not create the directory' do
134
- subject.create_directory(path, recursive: true)
135
- rescue error_class
136
- expect(subject.directory?(path)).to be false
137
- end
138
- end
139
- end
254
+ context "when the #{parameter_name} is a relative path" do
255
+ let(path_parameter) { invalid_relative_path }
256
+
257
+ it 'should raise an exception' do
258
+ expect { call_method }.to raise_error(error_class, error_message)
140
259
  end
141
260
 
142
- context 'when the directory already exists' do
143
- before(:example) { subject.create_directory(path) }
261
+ instance_exec(&block) if block
262
+ end
263
+ end
264
+ end
144
265
 
145
- it { expect(subject.create_directory(path)).to be == path }
146
- end
266
+ deferred_examples 'should validate the path' \
267
+ do |path_parameter = 'path', &block|
268
+ describe "with #{path_parameter.to_s.tr('_', ' ')}: nil" do
269
+ let(path_parameter) { nil }
270
+ let(:error_message) do
271
+ tools
272
+ .assertions
273
+ .error_message_for('presence', as: path_parameter)
274
+ end
147
275
 
148
- context 'when the path includes a file' do
149
- let(:error_class) do
150
- Cuprum::Cli::Dependencies::FileSystem::DirectoryIsAFileError
151
- end
152
- let(:error_message) do
153
- "unable to create directory #{path} - directory is a file"
154
- end
276
+ it 'should raise an exception' do
277
+ expect { call_method }
278
+ .to raise_error ArgumentError, error_message
279
+ end
280
+
281
+ instance_exec(&block) if block
282
+ end
155
283
 
156
- before(:example) { subject.write_file(path, '') }
284
+ describe "with #{path_parameter.to_s.tr('_', ' ')}: an Object" do
285
+ let(path_parameter) { Object.new.freeze }
286
+ let(:error_message) do
287
+ "#{path_parameter} is not an instance of String"
288
+ end
157
289
 
158
- include_deferred 'when created files are cleaned up'
290
+ it 'should raise an exception' do
291
+ expect { call_method }
292
+ .to raise_error ArgumentError, error_message
293
+ end
159
294
 
160
- it 'should raise an exception' do
161
- expect { subject.create_directory(path) }
162
- .to raise_error(error_class, error_message)
163
- end
295
+ instance_exec(&block) if block
296
+ end
164
297
 
165
- it 'should not create the directory' do
166
- subject.create_directory(path)
167
- rescue error_class
168
- expect(subject.directory?(path)).to be false
169
- end
170
- end
298
+ describe "with #{path_parameter.to_s.tr('_', ' ')}: an empty String" do
299
+ let(path_parameter) { '' }
300
+ let(:error_message) do
301
+ tools
302
+ .assertions
303
+ .error_message_for('presence', as: path_parameter)
171
304
  end
172
305
 
173
- describe 'with a qualified path' do
174
- let(:base_path) { qualified_directory_path }
175
- let(:path) { File.join(base_path, 'custom_dir') }
306
+ it 'should raise an exception' do
307
+ expect { call_method }
308
+ .to raise_error ArgumentError, error_message
309
+ end
176
310
 
177
- it { expect(subject.create_directory(path)).to be == path }
311
+ instance_exec(&block) if block
312
+ end
313
+ end
178
314
 
179
- describe 'with a multi-segment path' do
180
- let(:path) { File.join(base_path, 'custom_dir/inner/path') }
181
- let(:error_class) do
182
- Cuprum::Cli::Dependencies::FileSystem::DirectoryNotFoundError
183
- end
184
- let(:error_message) do
185
- "unable to create directory #{path} - directory not found"
186
- end
315
+ deferred_examples 'should validate the file or path' \
316
+ do |path_parameter = 'path', &block|
317
+ describe "with #{path_parameter.to_s.tr('_', ' ')}: nil" do
318
+ let(path_parameter) { nil }
319
+ let(:error_message) do
320
+ tools
321
+ .assertions
322
+ .error_message_for('presence', as: path_parameter)
323
+ end
187
324
 
188
- it 'should raise an exception' do
189
- expect { subject.create_directory(path) }
190
- .to raise_error(error_class, error_message)
191
- end
325
+ it 'should raise an exception' do
326
+ expect { call_method }
327
+ .to raise_error ArgumentError, error_message
328
+ end
192
329
 
193
- describe 'with recursive: true' do
194
- it 'should return the path' do
195
- expect(subject.create_directory(path, recursive: true))
196
- .to be == path
197
- end
330
+ instance_exec(&block) if block
331
+ end
198
332
 
199
- it 'should create the directory' do
200
- expect { subject.create_directory(path, recursive: true) }.to(
201
- change { subject.directory?(path) }.to(be true)
202
- )
203
- end
333
+ describe "with #{path_parameter.to_s.tr('_', ' ')}: an Object" do
334
+ let(path_parameter) { Object.new.freeze }
335
+ let(:error_message) do
336
+ "#{path_parameter} is not a String or IO stream"
337
+ end
204
338
 
205
- context 'when the directory already exists' do
206
- before(:example) do
207
- subject.create_directory(path, recursive: true)
208
- end
339
+ it 'should raise an exception' do
340
+ expect { call_method }
341
+ .to raise_error ArgumentError, error_message
342
+ end
209
343
 
210
- it { expect(subject.create_directory(path)).to be == path }
211
- end
344
+ instance_exec(&block) if block
345
+ end
212
346
 
213
- context 'when the path includes a file' do
214
- let(:error_class) do
215
- Cuprum::Cli::Dependencies::FileSystem::DirectoryIsAFileError
216
- end
217
- let(:error_message) do
218
- "unable to create directory #{path} - directory is a file"
219
- end
220
- let(:file_path) { File.join(base_path, 'custom_dir') }
347
+ describe "with #{path_parameter.to_s.tr('_', ' ')}: an empty String" do
348
+ let(path_parameter) { '' }
349
+ let(:error_message) do
350
+ tools
351
+ .assertions
352
+ .error_message_for('presence', as: path_parameter)
353
+ end
221
354
 
222
- before(:example) do
223
- subject.write_file(file_path, '')
224
- end
355
+ it 'should raise an exception' do
356
+ expect { call_method }
357
+ .to raise_error ArgumentError, error_message
358
+ end
225
359
 
226
- include_deferred 'when created files are cleaned up'
360
+ instance_exec(&block) if block
361
+ end
362
+ end
227
363
 
228
- it 'should raise an exception' do
229
- expect { subject.create_directory(path, recursive: true) }
230
- .to raise_error(error_class, error_message)
231
- end
364
+ deferred_examples 'should implement the file_system dependency' do
365
+ let(:invalid_absolute_path) do
366
+ defined?(super()) ? super() : '/invalid-absolute-path'
367
+ end
368
+ let(:invalid_qualified_path) do
369
+ defined?(super()) ? super() : '../invalid-qualified-path'
370
+ end
371
+ let(:invalid_relative_path) do
372
+ defined?(super()) ? super() : 'invalid-relative-path'
373
+ end
232
374
 
233
- it 'should not create the directory' do
234
- subject.create_directory(path, recursive: true)
235
- rescue error_class
236
- expect(subject.directory?(path)).to be false
237
- end
375
+ describe '#copy_file' do
376
+ deferred_context 'with destination_path: an absolute path' do
377
+ let(:destination_path) do
378
+ path = absolute_file_path
379
+ dirname = File.dirname(path)
380
+ basename = File.basename(path)
381
+ extname = File.extname(path)
382
+
383
+ File.join(
384
+ dirname,
385
+ basename.sub(/#{extname}\z/, "_copy#{extname}")
386
+ )
387
+ end
388
+ let(:path) { destination_path }
389
+ end
390
+
391
+ deferred_context 'with destination_path: a relative path' do
392
+ let(:destination_path) do
393
+ path = relative_file_path
394
+ dirname = File.dirname(path)
395
+ basename = File.basename(path)
396
+ extname = File.extname(path)
397
+
398
+ File.join(
399
+ dirname,
400
+ basename.sub(/#{extname}\z/, "_copy#{extname}")
401
+ )
402
+ end
403
+ let(:path) { destination_path }
404
+ end
405
+
406
+ deferred_context 'with destination_path: a qualified path' do
407
+ let(:destination_path) do
408
+ path =
409
+ if defined?(root_path) && root_path
410
+ "../#{root_path.split(File::SEPARATOR).last}/" \
411
+ "#{File.basename(qualified_file_path)}"
412
+ else
413
+ qualified_file_path
238
414
  end
239
- end
415
+
416
+ dirname = File.dirname(path)
417
+ basename = File.basename(path)
418
+ extname = File.extname(path)
419
+
420
+ File.join(
421
+ dirname,
422
+ basename.sub(/#{extname}\z/, "_copy#{extname}")
423
+ )
240
424
  end
425
+ let(:path) { destination_path }
426
+ end
241
427
 
242
- context 'when the directory already exists' do
243
- before(:example) { subject.create_directory(path) }
428
+ deferred_examples 'should not copy the file' do
429
+ it 'should not copy the file' do
430
+ expect { safe_copy_file }
431
+ .not_to(change { subject.file?(destination_path) })
432
+ end
433
+ end
434
+
435
+ deferred_examples 'should copy the file' do
436
+ it 'should not change the source file', :aggregate_failures do
437
+ copy_file
244
438
 
245
- it { expect(subject.create_directory(path)).to be == path }
439
+ expect(subject.file?(source_path)).to be true
440
+ expect(subject.read_file(source_path)).to eq expected_contents
246
441
  end
247
442
 
248
- context 'when the path includes a file' do
249
- let(:error_class) do
250
- Cuprum::Cli::Dependencies::FileSystem::DirectoryIsAFileError
251
- end
252
- let(:error_message) do
253
- "unable to create directory #{path} - directory is a file"
254
- end
443
+ it 'should create the destination file', :aggregate_failures do
444
+ copy_file
255
445
 
256
- before(:example) { subject.write_file(path, '') }
446
+ expect(subject.file?(destination_path)).to be true
447
+ expect(subject.read_file(destination_path))
448
+ .to eq expected_contents
449
+ end
257
450
 
258
- include_deferred 'when created files are cleaned up'
451
+ describe 'with a transform block' do
452
+ let(:block) { ->(data) { data.upcase } } # rubocop:disable Style/SymbolProc
453
+ let(:expected_contents) { super().upcase }
259
454
 
260
- it 'should raise an exception' do
261
- expect { subject.create_directory(path) }
262
- .to raise_error(error_class, error_message)
263
- end
455
+ it 'should create the destination file', :aggregate_failures do
456
+ copy_file
264
457
 
265
- it 'should not create the directory' do
266
- subject.create_directory(path)
267
- rescue error_class
268
- expect(subject.directory?(path)).to be false
458
+ expect(subject.file?(destination_path)).to be true
459
+ expect(subject.read_file(destination_path))
460
+ .to eq expected_contents
269
461
  end
270
462
  end
271
463
  end
272
464
 
273
- describe 'with a relative path' do
274
- let(:base_path) { relative_directory_path }
275
- let(:path) { File.join(base_path, 'custom_dir') }
465
+ deferred_examples 'should copy the file to a valid destination' do
466
+ include_deferred 'should copy the file'
467
+
468
+ include_deferred 'should handle a directory when a file is expected',
469
+ :destination_path \
470
+ do
471
+ include_deferred 'should not copy the file'
472
+ end
276
473
 
277
- it { expect(subject.create_directory(path)).to be == path }
474
+ include_deferred 'should handle a file that already exists',
475
+ :destination_path \
476
+ do
477
+ include_deferred 'should not copy the file'
278
478
 
279
- describe 'with a multi-segment path' do
280
- let(:path) { File.join(base_path, 'custom_dir/inner/path') }
281
- let(:error_class) do
282
- Cuprum::Cli::Dependencies::FileSystem::DirectoryNotFoundError
283
- end
284
- let(:error_message) do
285
- "unable to create directory #{path} - directory not found"
286
- end
479
+ describe 'with force: true' do
480
+ let(:copy_options) { super().merge(force: true) }
287
481
 
288
- it 'should raise an exception' do
289
- expect { subject.create_directory(path) }
290
- .to raise_error(error_class, error_message)
482
+ include_deferred 'should copy the file'
291
483
  end
484
+ end
292
485
 
293
- describe 'with recursive: true' do
294
- it 'should return the path' do
295
- expect(subject.create_directory(path, recursive: true))
296
- .to be == path
297
- end
486
+ include_deferred 'should handle a path that includes a file',
487
+ :destination_path \
488
+ do
489
+ include_deferred 'should not copy the file'
490
+ end
298
491
 
299
- it 'should create the directory' do
300
- expect { subject.create_directory(path, recursive: true) }.to(
301
- change { subject.directory?(path) }.to(be true)
302
- )
303
- end
492
+ include_deferred 'should handle a path with missing directory',
493
+ :destination_path \
494
+ do
495
+ include_deferred 'should not copy the file'
496
+ end
497
+ end
304
498
 
305
- context 'when the directory already exists' do
306
- before(:example) do
307
- subject.create_directory(path, recursive: true)
308
- end
499
+ let(:source_path) { 'path/to/source.txt' }
500
+ let(:destination_path) { 'path/to/destination.txt' }
501
+ let(:copy_options) { {} }
502
+ let(:block) { nil }
503
+ let(:expected_contents) { subject.read_file(source_path) }
504
+ let(:error_reason) { "unable to write file #{destination_path}" }
309
505
 
310
- it { expect(subject.create_directory(path)).to be == path }
311
- end
506
+ define_method :copy_file do
507
+ subject.copy_file(
508
+ source_path,
509
+ destination_path,
510
+ **copy_options,
511
+ &block
512
+ )
513
+ end
514
+ alias_method :call_method, :copy_file
312
515
 
313
- context 'when the path includes a file' do
314
- let(:error_class) do
315
- Cuprum::Cli::Dependencies::FileSystem::DirectoryIsAFileError
316
- end
317
- let(:error_message) do
318
- "unable to create directory #{path} - directory is a file"
319
- end
320
- let(:file_path) { File.join(base_path, 'custom_dir') }
516
+ define_method :safe_copy_file do
517
+ copy_file
518
+ rescue StandardError
519
+ nil
520
+ end
321
521
 
322
- before(:example) do
323
- subject.write_file(file_path, '')
324
- end
522
+ include_deferred 'with valid file paths'
523
+
524
+ it 'should define the method' do
525
+ expect(subject)
526
+ .to respond_to(:copy_file)
527
+ .with(2).arguments
528
+ .and_keywords(:force)
529
+ .and_a_block
530
+ end
325
531
 
326
- include_deferred 'when created files are cleaned up'
532
+ include_deferred 'should validate the path', :destination_path
327
533
 
328
- it 'should raise an exception' do
329
- expect { subject.create_directory(path, recursive: true) }
330
- .to raise_error(error_class, error_message)
331
- end
534
+ include_deferred 'should validate the path', :source_path
332
535
 
333
- it 'should not create the directory' do
334
- subject.create_directory(path, recursive: true)
335
- rescue error_class
336
- expect(subject.directory?(path)).to be false
337
- end
338
- end
339
- end
340
- end
536
+ include_deferred 'should handle a path with missing file',
537
+ :source_path \
538
+ do
539
+ let(:error_reason) { "unable to read file #{source_path}" }
341
540
 
342
- context 'when the directory already exists' do
343
- before(:example) { subject.create_directory(path) }
541
+ include_deferred 'should not copy the file'
542
+ end
344
543
 
345
- it { expect(subject.create_directory(path)).to be == path }
346
- end
544
+ wrap_deferred 'with valid file paths' do
545
+ describe 'with source_path: an absolute path' do
546
+ let(:source_path) { absolute_file_path }
347
547
 
348
- context 'when the path includes a file' do
349
- let(:error_class) do
350
- Cuprum::Cli::Dependencies::FileSystem::DirectoryIsAFileError
351
- end
352
- let(:error_message) do
353
- "unable to create directory #{path} - directory is a file"
354
- end
548
+ include_deferred \
549
+ 'should handle a directory when a file is expected',
550
+ :source_path \
551
+ do
552
+ let(:error_reason) { "unable to read file #{source_path}" }
355
553
 
356
- before(:example) { subject.write_file(path, '') }
554
+ include_deferred 'should not copy the file'
555
+ end
357
556
 
358
- include_deferred 'when created files are cleaned up'
557
+ wrap_deferred 'with destination_path: an absolute path' do
558
+ include_deferred 'should copy the file to a valid destination'
559
+ end
359
560
 
360
- it 'should raise an exception' do
361
- expect { subject.create_directory(path) }
362
- .to raise_error(error_class, error_message)
561
+ wrap_deferred 'with destination_path: a qualified path' do
562
+ include_deferred 'should copy the file to a valid destination'
363
563
  end
364
564
 
365
- it 'should not create the directory' do
366
- subject.create_directory(path)
367
- rescue error_class
368
- expect(subject.directory?(path)).to be false
565
+ wrap_deferred 'with destination_path: a relative path' do
566
+ include_deferred 'should copy the file to a valid destination'
369
567
  end
370
568
  end
371
- end
372
569
 
373
- wrap_deferred 'when initialized with root_path: value' do
374
- describe 'with a qualified path' do
375
- let(:base_path) { File.join('..', '..', 'tmp', 'files') }
376
- let(:path) { File.join(base_path, 'custom_dir') }
570
+ describe 'with source_path: a qualified path to a file' do
571
+ let(:source_path) { qualified_file_path }
377
572
 
378
- it { expect(subject.create_directory(path)).to be == path }
573
+ include_deferred \
574
+ 'should handle a directory when a file is expected',
575
+ :source_path \
576
+ do
577
+ let(:error_reason) { "unable to read file #{source_path}" }
379
578
 
380
- it 'should create the directory' do
381
- expect { subject.create_directory(path) }.to(
382
- change { subject.directory?(path) }.to(be true)
383
- )
579
+ include_deferred 'should not copy the file'
384
580
  end
385
581
 
386
- describe 'with a multi-segment path' do
387
- let(:path) { File.join(base_path, 'custom_dir/inner/path') }
582
+ wrap_deferred 'with destination_path: an absolute path' do
583
+ include_deferred 'should copy the file to a valid destination'
584
+ end
388
585
 
389
- describe 'with recursive: true' do
390
- it 'should return the path' do
391
- expect(subject.create_directory(path, recursive: true))
392
- .to be == path
393
- end
586
+ wrap_deferred 'with destination_path: a qualified path' do
587
+ include_deferred 'should copy the file to a valid destination'
588
+ end
394
589
 
395
- it 'should create the directory' do
396
- expect { subject.create_directory(path, recursive: true) }.to(
397
- change { subject.directory?(path) }.to(be true)
398
- )
399
- end
400
- end
590
+ wrap_deferred 'with destination_path: a relative path' do
591
+ include_deferred 'should copy the file to a valid destination'
401
592
  end
402
593
  end
403
594
 
404
- describe 'with a relative path' do
405
- let(:base_path) { 'nested' }
406
- let(:path) { File.join(base_path, 'custom_dir') }
595
+ describe 'with source_path: a relative path' do
596
+ let(:source_path) { relative_file_path }
407
597
 
408
- it { expect(subject.create_directory(path)).to be == path }
598
+ include_deferred \
599
+ 'should handle a directory when a file is expected',
600
+ :source_path \
601
+ do
602
+ let(:error_reason) { "unable to read file #{source_path}" }
409
603
 
410
- it 'should create the directory' do
411
- expect { subject.create_directory(path) }.to(
412
- change { subject.directory?(path) }.to(be true)
413
- )
604
+ include_deferred 'should not copy the file'
414
605
  end
415
606
 
416
- describe 'with a multi-segment path' do
417
- let(:path) { File.join(base_path, 'custom_dir/inner/path') }
607
+ wrap_deferred 'with destination_path: an absolute path' do
608
+ include_deferred 'should copy the file to a valid destination'
609
+ end
418
610
 
419
- describe 'with recursive: true' do
420
- it 'should return the path' do
421
- expect(subject.create_directory(path, recursive: true))
422
- .to be == path
423
- end
611
+ wrap_deferred 'with destination_path: a qualified path' do
612
+ include_deferred 'should copy the file to a valid destination'
613
+ end
424
614
 
425
- it 'should create the directory' do
426
- expect { subject.create_directory(path, recursive: true) }.to(
427
- change { subject.directory?(path) }.to(be true)
428
- )
429
- end
430
- end
615
+ wrap_deferred 'with destination_path: a relative path' do
616
+ include_deferred 'should copy the file to a valid destination'
431
617
  end
432
618
  end
433
- end
434
- end
435
619
 
436
- describe '#directory?' do
437
- let(:invalid_absolute_path) do
438
- defined?(super()) ? super() : '/invalid-absolute-path'
439
- end
440
- let(:invalid_qualified_path) do
441
- defined?(super()) ? super() : '../invalid-qualified-path'
442
- end
443
- let(:invalid_relative_path) do
444
- defined?(super()) ? super() : 'invalid-absolute-path'
445
- end
620
+ wrap_deferred 'when initialized with root_path: value' do
621
+ describe 'with source_path: a qualified path' do
622
+ let(:source_path) { qualified_file_path }
446
623
 
447
- include_deferred 'with valid file paths'
624
+ include_deferred \
625
+ 'should handle a directory when a file is expected',
626
+ :source_path \
627
+ do
628
+ let(:error_reason) { "unable to read file #{source_path}" }
448
629
 
449
- it 'should define the method' do
450
- expect(subject).to respond_to(:directory?).with(1).argument
451
- end
630
+ include_deferred 'should not copy the file'
631
+ end
452
632
 
453
- it 'should define the aliased method' do
454
- expect(subject)
455
- .to have_aliased_method(:directory?)
456
- .as(:directory_exists?)
457
- end
633
+ wrap_deferred 'with destination_path: an absolute path' do
634
+ include_deferred 'should copy the file to a valid destination'
635
+ end
458
636
 
459
- describe 'with nil' do
460
- let(:error_message) do
461
- tools.assertions.error_message_for('presence', as: 'path')
462
- end
637
+ wrap_deferred 'with destination_path: a qualified path' do
638
+ include_deferred 'should copy the file to a valid destination'
639
+ end
463
640
 
464
- it 'should raise an exception' do
465
- expect { subject.directory?(nil) }
466
- .to raise_error ArgumentError, error_message
641
+ wrap_deferred 'with destination_path: a relative path' do
642
+ include_deferred 'should copy the file to a valid destination'
643
+ end
644
+ end
645
+
646
+ describe 'with source_path: a relative path to a file' do
647
+ let(:source_path) { relative_file_path }
648
+
649
+ include_deferred \
650
+ 'should handle a directory when a file is expected',
651
+ :source_path \
652
+ do
653
+ let(:error_reason) { "unable to read file #{source_path}" }
654
+
655
+ include_deferred 'should not copy the file'
656
+ end
657
+
658
+ wrap_deferred 'with destination_path: an absolute path' do
659
+ include_deferred 'should copy the file to a valid destination'
660
+ end
661
+
662
+ wrap_deferred 'with destination_path: a qualified path' do
663
+ include_deferred 'should copy the file to a valid destination'
664
+ end
665
+
666
+ wrap_deferred 'with destination_path: a relative path' do
667
+ include_deferred 'should copy the file to a valid destination'
668
+ end
669
+ end
467
670
  end
468
671
  end
672
+ end
469
673
 
470
- describe 'with an Object' do
471
- let(:error_message) do
472
- tools.assertions.error_message_for('name', as: 'path')
674
+ describe '#create_directory' do
675
+ deferred_examples 'should not create the directory' do
676
+ it 'should not create the directory' do
677
+ expect { safe_create_directory }.not_to(
678
+ change { subject.directory?(path) }
679
+ )
473
680
  end
681
+ end
474
682
 
475
- it 'should raise an exception' do
476
- expect { subject.directory?(Object.new.freeze) }
477
- .to raise_error ArgumentError, error_message
683
+ deferred_examples 'should create the directory' do
684
+ it { expect(create_directory).to be == path }
685
+
686
+ it 'should create the directory' do
687
+ expect { create_directory }.to(
688
+ change { subject.directory?(path) }.to(be true)
689
+ )
478
690
  end
479
691
  end
480
692
 
481
- describe 'with an empty String' do
482
- let(:error_message) do
483
- tools.assertions.error_message_for('presence', as: 'path')
693
+ deferred_examples 'should create the directory for a valid path' do
694
+ include_deferred 'should create the directory'
695
+
696
+ context 'when the directory already exists' do
697
+ let(:path) do
698
+ if absolute_path?(super())
699
+ absolute_directory_path
700
+ elsif qualified_path?(super())
701
+ qualified_directory_path
702
+ else
703
+ relative_directory_path
704
+ end
705
+ end
706
+
707
+ it { expect(create_directory).to be == path }
708
+
709
+ include_deferred 'should not create the directory'
484
710
  end
485
711
 
486
- it 'should raise an exception' do
487
- expect { subject.directory?('') }
488
- .to raise_error ArgumentError, error_message
712
+ include_deferred 'should handle a file when a directory is expected' \
713
+ do
714
+ include_deferred 'should not create the directory'
489
715
  end
490
- end
491
716
 
492
- describe 'with an invalid absolute path' do
493
- let(:path) { invalid_absolute_path }
717
+ include_deferred 'should handle a path with missing directory' \
718
+ do
719
+ include_deferred 'should not create the directory'
494
720
 
495
- it { expect(subject.directory?(path)).to be false }
496
- end
721
+ describe 'with recursive: true' do
722
+ let(:create_options) { super().merge(recursive: true) }
497
723
 
498
- describe 'with an invalid qualified path' do
499
- let(:path) { invalid_qualified_path }
724
+ include_deferred 'should create the directory'
725
+ end
726
+ end
500
727
 
501
- it { expect(subject.directory?(path)).to be false }
728
+ include_deferred 'should handle a path that includes a file' do
729
+ include_deferred 'should not create the directory'
730
+ end
502
731
  end
503
732
 
504
- describe 'with an invalid relative path' do
505
- let(:path) { invalid_relative_path }
733
+ let(:path) { nil }
734
+ let(:create_options) { {} }
735
+ let(:error_reason) { "unable to create directory #{path}" }
506
736
 
507
- it { expect(subject.directory?(path)).to be false }
737
+ define_method :create_directory do
738
+ subject.create_directory(path, **create_options)
508
739
  end
740
+ alias_method :call_method, :create_directory
509
741
 
510
- describe 'with an absolute path to a directory' do
511
- let(:path) { absolute_directory_path }
512
-
513
- it { expect(subject.directory?(path)).to be true }
742
+ define_method :safe_create_directory do
743
+ create_directory
744
+ rescue StandardError
745
+ nil
514
746
  end
515
747
 
516
- describe 'with an absolute path to a file' do
517
- let(:path) { absolute_file_path }
748
+ include_deferred 'with valid file paths'
518
749
 
519
- it { expect(subject.directory?(path)).to be false }
750
+ it 'should define the method' do
751
+ expect(subject)
752
+ .to respond_to(:create_directory)
753
+ .with(1).argument
754
+ .and_keywords(:recursive)
520
755
  end
521
756
 
522
- describe 'with a qualified path to a directory' do
523
- let(:path) { qualified_directory_path }
524
-
525
- it { expect(subject.directory?(path)).to be true }
757
+ it 'should define the aliased method' do
758
+ expect(subject)
759
+ .to have_aliased_method(:create_directory)
760
+ .as(:make_directory)
526
761
  end
527
762
 
528
- describe 'with a qualified path to a file' do
529
- let(:path) { qualified_file_path }
763
+ include_deferred 'should validate the path'
530
764
 
531
- it { expect(subject.directory?(path)).to be false }
765
+ describe 'with an absolute path' do
766
+ let(:path) { File.join(absolute_directory_path, 'custom_dir') }
767
+
768
+ include_deferred 'should create the directory for a valid path'
532
769
  end
533
770
 
534
- describe 'with a relative path to a directory' do
535
- let(:path) { relative_directory_path }
771
+ describe 'with a relative path' do
772
+ let(:path) { File.join(relative_directory_path, 'custom_dir') }
536
773
 
537
- it { expect(subject.directory?(path)).to be true }
774
+ include_deferred 'should create the directory for a valid path'
538
775
  end
539
776
 
540
- describe 'with a relative path to a file' do
541
- let(:path) { relative_file_path }
777
+ describe 'with a qualified path' do
778
+ let(:path) { File.join(qualified_directory_path, 'custom_dir') }
542
779
 
543
- it { expect(subject.directory?(path)).to be false }
780
+ include_deferred 'should create the directory for a valid path'
544
781
  end
545
782
 
546
783
  wrap_deferred 'when initialized with root_path: value' do
547
- describe 'with a qualified path to a directory' do
548
- let(:path) { qualified_directory_path }
784
+ describe 'with a relative path' do
785
+ let(:path) { File.join(relative_directory_path, 'custom_dir') }
549
786
 
550
- it { expect(subject.directory?(path)).to be true }
787
+ include_deferred 'should create the directory for a valid path'
551
788
  end
552
789
 
553
- describe 'with a qualified path to a file' do
554
- let(:path) { qualified_file_path }
790
+ describe 'with a qualified path' do
791
+ let(:path) { File.join(qualified_directory_path, 'custom_dir') }
555
792
 
556
- it { expect(subject.directory?(path)).to be false }
793
+ include_deferred 'should create the directory for a valid path'
557
794
  end
795
+ end
796
+ end
558
797
 
559
- describe 'with a relative path to a directory' do
560
- let(:path) { relative_directory_path }
561
-
562
- it { expect(subject.directory?(path)).to be true }
798
+ describe '#delete_directory' do
799
+ deferred_examples 'should not delete the directory' do
800
+ it 'should not delete the directory' do
801
+ expect { safe_delete_directory }.not_to(
802
+ change { subject.directory?(path) }
803
+ )
563
804
  end
805
+ end
564
806
 
565
- describe 'with a relative path to a file' do
566
- let(:path) { relative_file_path }
807
+ deferred_examples 'should delete the directory' do
808
+ it { expect(delete_directory).to be == path }
567
809
 
568
- it { expect(subject.directory?(path)).to be false }
810
+ it 'should delete the directory' do
811
+ expect { delete_directory }.to(
812
+ change { subject.directory?(path) }.to(be false)
813
+ )
569
814
  end
570
815
  end
571
- end
572
816
 
573
- describe '#each_file' do
574
- deferred_examples 'should return or yield the matching file names' do
575
- describe 'without a block' do
576
- it { expect(subject.each_file(pattern)).to be_a Enumerator }
817
+ deferred_examples 'should delete the directory for a valid path' do
818
+ include_deferred 'should handle a path that includes a file' do
819
+ include_deferred 'should not delete the directory'
820
+ end
577
821
 
578
- it 'should return the matching file names' do
579
- expect(subject.each_file(pattern).to_a).to be == expected_files
580
- end
822
+ include_deferred 'should handle a path with missing directory' do
823
+ include_deferred 'should not delete the directory'
581
824
  end
582
825
 
583
- describe 'with a block' do
584
- it { expect(subject.each_file(pattern) { nil }).to be nil }
826
+ include_deferred 'should handle a path with missing directory' do
827
+ include_deferred 'should not delete the directory'
828
+ end
585
829
 
586
- it 'should yield the matching file names' do
587
- expect { |block| subject.each_file(pattern, &block) }.then \
588
- do |expectation|
589
- if expected_files.empty?
590
- expectation.not_to(yield_control)
591
- else
592
- expectation.to yield_successive_args(*expected_files)
593
- end
594
- end
595
- end
830
+ context 'when the directory is empty' do
831
+ let(:path) { File.join(super(), 'empty_dir') }
832
+
833
+ include_deferred 'should delete the directory'
596
834
  end
597
- end
598
835
 
599
- let(:pattern) { '**/*.rb' }
600
- let(:matching_files) do
601
- defined?(super()) ? super() : []
602
- end
603
- let(:expected_files) { matching_files }
836
+ context 'when the directory contains empty directories' do
837
+ let(:path) { File.join(super(), 'dir_with_directories') }
838
+ let(:error_class) do
839
+ Cuprum::Cli::Dependencies::FileSystem::DirectoryNotEmptyError
840
+ end
841
+ let(:error_message) do
842
+ "#{error_reason} - directory is not empty"
843
+ end
604
844
 
605
- it 'should define the method' do
606
- expect(subject)
607
- .to respond_to(:each_file)
608
- .with(1).argument
609
- .and_a_block
610
- end
845
+ it 'should raise an exception' do
846
+ expect { delete_directory }
847
+ .to raise_error(error_class, error_message)
848
+ end
611
849
 
612
- include_deferred 'should return or yield the matching file names'
850
+ include_deferred 'should not delete the directory'
613
851
 
614
- wrap_deferred 'with valid file paths' do
615
- describe 'with a pattern that does not match any files' do
616
- let(:pattern) { '*.xml' }
617
- let(:matching_files) { [] }
852
+ it 'should not change the directory contents' do
853
+ expect { safe_delete_directory }
854
+ .not_to(change { Dir[File.join(path, '**')] })
855
+ end
618
856
 
619
- include_deferred 'should return or yield the matching file names'
857
+ describe 'with force: true' do
858
+ let(:delete_options) { super().merge(force: true) }
859
+
860
+ include_deferred 'should delete the directory'
861
+ end
862
+
863
+ describe 'with recursive: true' do
864
+ let(:delete_options) { super().merge(recursive: true) }
865
+
866
+ include_deferred 'should delete the directory'
867
+ end
620
868
  end
621
869
 
622
- describe 'with a globbed pattern' do
623
- let(:pattern) { '*.txt' }
624
- let(:expected_files) do
625
- rxp = /\A\w+\.txt\z/
870
+ context 'when the directory contains files' do
871
+ let(:path) { File.join(super(), 'dir_with_files') }
872
+ let(:error_class) do
873
+ Cuprum::Cli::Dependencies::FileSystem::DirectoryNotEmptyError
874
+ end
875
+ let(:error_message) do
876
+ "#{error_reason} - directory is not empty"
877
+ end
626
878
 
627
- matching_files.select do |str|
628
- str[(1 + subject.root_path.size)..].match?(rxp)
629
- end
879
+ it 'should raise an exception' do
880
+ expect { delete_directory }
881
+ .to raise_error(error_class, error_message)
630
882
  end
631
883
 
632
- include_deferred 'should return or yield the matching file names'
633
- end
884
+ include_deferred 'should not delete the directory'
634
885
 
635
- describe 'with a globbed pattern with fixed segments' do
636
- let(:pattern) { File.join(files_directory, '*.txt') }
637
- let(:expected_files) do
638
- rxp = %r{\A#{files_directory}/\w+\.txt\z}
886
+ it 'should not change the directory contents' do
887
+ expect { safe_delete_directory }
888
+ .not_to(change { Dir[File.join(path, '**')] })
889
+ end
639
890
 
640
- matching_files.select do |str|
641
- str[(1 + subject.root_path.size)..].match?(rxp)
642
- end
891
+ describe 'with force: true' do
892
+ let(:delete_options) { super().merge(force: true) }
893
+
894
+ include_deferred 'should delete the directory'
643
895
  end
644
896
 
645
- include_deferred 'should return or yield the matching file names'
646
- end
897
+ describe 'with recursive: true' do
898
+ let(:delete_options) { super().merge(recursive: true) }
647
899
 
648
- describe 'with a recursive globbed pattern' do
649
- let(:pattern) { File.join('**', '*.txt') }
650
- let(:expected_files) do
651
- rxp = /\.txt\z/
900
+ it 'should raise an exception' do
901
+ expect { delete_directory }
902
+ .to raise_error(error_class, error_message)
903
+ end
652
904
 
653
- matching_files.select do |str|
654
- str[(1 + subject.root_path.size)..].match?(rxp)
905
+ include_deferred 'should not delete the directory'
906
+
907
+ it 'should not change the directory contents' do
908
+ expect { safe_delete_directory }
909
+ .not_to(change { Dir[File.join(path, '**')] })
655
910
  end
656
911
  end
657
-
658
- include_deferred 'should return or yield the matching file names'
659
912
  end
660
913
  end
661
- end
662
914
 
663
- describe '#file?' do
664
- let(:invalid_absolute_path) do
665
- defined?(super()) ? super() : '/invalid-absolute-path'
666
- end
667
- let(:invalid_qualified_path) do
668
- defined?(super()) ? super() : '../invalid-qualified-path'
669
- end
670
- let(:invalid_relative_path) do
671
- defined?(super()) ? super() : 'invalid-absolute-path'
915
+ let(:path) { nil }
916
+ let(:delete_options) { {} }
917
+ let(:error_reason) { "unable to delete directory #{path}" }
918
+
919
+ define_method :delete_directory do
920
+ subject.delete_directory(path, **delete_options)
672
921
  end
922
+ alias_method :call_method, :delete_directory
673
923
 
674
- include_deferred 'with valid file paths'
924
+ define_method :safe_delete_directory do
925
+ delete_directory
926
+ rescue StandardError
927
+ nil
928
+ end
675
929
 
676
930
  it 'should define the method' do
677
- expect(subject).to respond_to(:file?).with(1).argument
931
+ expect(subject)
932
+ .to respond_to(:delete_directory)
933
+ .with(1).argument
934
+ .and_keywords(:force, :recursive)
678
935
  end
679
936
 
680
- it 'should define the aliased method' do
937
+ it 'should alias the method' do
681
938
  expect(subject)
682
- .to have_aliased_method(:file?)
683
- .as(:file_exists?)
939
+ .to have_aliased_method(:delete_directory)
940
+ .as(:remove_directory)
684
941
  end
685
942
 
686
- describe 'with nil' do
687
- let(:error_message) do
688
- tools.assertions.error_message_for('presence', as: 'path')
689
- end
943
+ include_deferred 'should validate the path'
690
944
 
691
- it 'should raise an exception' do
692
- expect { subject.file?(nil) }
693
- .to raise_error ArgumentError, error_message
945
+ wrap_deferred 'with valid file paths' do
946
+ let(:directory_fixtures) do
947
+ {
948
+ 'dir_with_directories' => {
949
+ 'empty_child' => {},
950
+ 'non_empty_child' => {
951
+ 'empty_grandchild' => {}
952
+ }
953
+ },
954
+ 'dir_with_files' => {
955
+ 'empty_child' => {},
956
+ 'non_empty_child' => {
957
+ 'file.txt' => 'Existing contents.'
958
+ }
959
+ },
960
+ 'empty_dir' => {}
961
+ }
962
+ end
963
+ let(:fixtures) do
964
+ root_path = defined?(self.root_path) ? self.root_path : Dir.pwd
965
+ base_path = fixtures_path
966
+
967
+ if base_path.start_with?('.')
968
+ base_path = File.expand_path(File.join(root_path, base_path))
969
+ end
970
+
971
+ relative_path = absolute_directory_path[(1 + base_path.size)...]
972
+
973
+ merge_fixtures(
974
+ super(),
975
+ *relative_path.split(File::SEPARATOR),
976
+ directory_fixtures
977
+ )
694
978
  end
695
- end
696
979
 
697
- describe 'with an Object' do
698
- let(:error_message) do
699
- tools.assertions.error_message_for('name', as: 'path')
980
+ define_method :merge_fixtures do |fixtures, *path, value|
981
+ return fixtures.merge(value) if path.empty?
982
+
983
+ head, *tail = path
984
+
985
+ fixtures.merge(head => merge_fixtures(fixtures[head], *tail, value))
700
986
  end
701
987
 
702
- it 'should raise an exception' do
703
- expect { subject.file?(Object.new.freeze) }
704
- .to raise_error ArgumentError, error_message
988
+ describe 'with an absolute path' do
989
+ let(:path) { absolute_directory_path }
990
+
991
+ include_deferred 'should delete the directory for a valid path'
705
992
  end
706
- end
707
993
 
708
- describe 'with an empty String' do
709
- let(:error_message) do
710
- tools.assertions.error_message_for('presence', as: 'path')
994
+ describe 'with a qualified path' do
995
+ let(:path) { qualified_directory_path }
996
+
997
+ include_deferred 'should delete the directory for a valid path'
711
998
  end
712
999
 
713
- it 'should raise an exception' do
714
- expect { subject.file?('') }
715
- .to raise_error ArgumentError, error_message
1000
+ describe 'with a relative path' do
1001
+ let(:path) { relative_directory_path }
1002
+
1003
+ include_deferred 'should delete the directory for a valid path'
716
1004
  end
717
- end
718
1005
 
719
- describe 'with an invalid absolute path' do
720
- let(:path) { invalid_absolute_path }
1006
+ wrap_deferred 'when initialized with root_path: value' do
1007
+ describe 'with a relative path' do
1008
+ let(:path) { File.join(relative_directory_path) }
721
1009
 
722
- it { expect(subject.file?(path)).to be false }
723
- end
1010
+ include_deferred 'should delete the directory for a valid path'
1011
+ end
724
1012
 
725
- describe 'with an invalid qualified path' do
726
- let(:path) { invalid_qualified_path }
1013
+ describe 'with a qualified path' do
1014
+ let(:path) { File.join(qualified_directory_path) }
727
1015
 
728
- it { expect(subject.file?(path)).to be false }
1016
+ include_deferred 'should delete the directory for a valid path'
1017
+ end
1018
+ end
729
1019
  end
1020
+ end
730
1021
 
731
- describe 'with an invalid relative path' do
732
- let(:path) { invalid_relative_path }
733
-
734
- it { expect(subject.file?(path)).to be false }
1022
+ describe '#delete_file' do
1023
+ deferred_examples 'should not delete the file' do
1024
+ it 'should not delete the file' do
1025
+ expect { safe_delete_file }.not_to(
1026
+ change { subject.file?(path) }
1027
+ )
1028
+ end
735
1029
  end
736
1030
 
737
- describe 'with an absolute path to a directory' do
738
- let(:path) { absolute_directory_path }
1031
+ deferred_examples 'should delete the file' do
1032
+ it { expect(delete_file).to be == path }
739
1033
 
740
- it { expect(subject.file?(path)).to be false }
1034
+ it 'should delete the file' do
1035
+ expect { delete_file }.to(
1036
+ change { subject.file?(path) }.to(be false)
1037
+ )
1038
+ end
741
1039
  end
742
1040
 
743
- describe 'with an absolute path to a file' do
744
- let(:path) { absolute_file_path }
1041
+ deferred_examples 'should delete the file for a valid path' do
1042
+ include_deferred 'should delete the file'
745
1043
 
746
- it { expect(subject.file?(path)).to be true }
1044
+ include_deferred 'should handle a directory when a file is expected' \
1045
+ do
1046
+ include_deferred 'should not delete the file'
1047
+ end
747
1048
  end
748
1049
 
749
- describe 'with a qualified path to a directory' do
750
- let(:path) { qualified_directory_path }
1050
+ let(:path) { nil }
1051
+ let(:error_reason) { "unable to delete file #{path}" }
751
1052
 
752
- it { expect(subject.file?(path)).to be false }
1053
+ define_method :delete_file do
1054
+ subject.delete_file(path)
753
1055
  end
1056
+ alias_method :call_method, :delete_file
754
1057
 
755
- describe 'with a qualified path to a file' do
756
- let(:path) { qualified_file_path }
757
-
758
- it { expect(subject.file?(path)).to be true }
1058
+ define_method :safe_delete_file do
1059
+ delete_file
1060
+ rescue StandardError
1061
+ nil
759
1062
  end
760
1063
 
761
- describe 'with a relative path to a directory' do
762
- let(:path) { relative_directory_path }
1064
+ it { expect(subject).to respond_to(:delete_file).with(1).argument }
763
1065
 
764
- it { expect(subject.file?(path)).to be false }
1066
+ it 'should define the aliased method' do
1067
+ expect(subject).to have_aliased_method(:delete_file).as(:remove_file)
765
1068
  end
766
1069
 
767
- describe 'with a relative path to a file' do
768
- let(:path) { relative_file_path }
1070
+ include_deferred 'should validate the path'
769
1071
 
770
- it { expect(subject.file?(path)).to be true }
771
- end
1072
+ include_deferred 'should handle a path with missing file'
772
1073
 
773
- wrap_deferred 'when initialized with root_path: value' do
774
- describe 'with a qualified path to a directory' do
775
- let(:path) { qualified_directory_path }
1074
+ wrap_deferred 'with valid file paths' do
1075
+ describe 'with an absolute path' do
1076
+ let(:path) { absolute_file_path }
776
1077
 
777
- it { expect(subject.file?(path)).to be false }
1078
+ include_deferred 'should delete the file for a valid path'
778
1079
  end
779
1080
 
780
- describe 'with a qualified path to a file' do
1081
+ describe 'with a qualified path' do
781
1082
  let(:path) { qualified_file_path }
782
1083
 
783
- it { expect(subject.file?(path)).to be true }
1084
+ include_deferred 'should delete the file for a valid path'
784
1085
  end
785
1086
 
786
- describe 'with a relative path to a directory' do
787
- let(:path) { relative_directory_path }
1087
+ describe 'with a relative path' do
1088
+ let(:path) { relative_file_path }
788
1089
 
789
- it { expect(subject.file?(path)).to be false }
1090
+ include_deferred 'should delete the file for a valid path'
790
1091
  end
791
1092
 
792
- describe 'with a relative path to a file' do
793
- let(:path) { relative_file_path }
1093
+ wrap_deferred 'when initialized with root_path: value' do
1094
+ describe 'with a qualified path' do
1095
+ let(:path) { qualified_file_path }
794
1096
 
795
- it { expect(subject.file?(path)).to be true }
1097
+ include_deferred 'should delete the file for a valid path'
1098
+ end
1099
+
1100
+ describe 'with a relative path' do
1101
+ let(:path) { relative_file_path }
1102
+
1103
+ include_deferred 'should delete the file for a valid path'
1104
+ end
796
1105
  end
797
1106
  end
798
1107
  end
799
1108
 
800
- describe '#read_file', replace_file_contents: true do
801
- let(:invalid_absolute_path) do
802
- defined?(super()) ? super() : '/invalid-absolute-path'
803
- end
804
- let(:invalid_qualified_path) do
805
- defined?(super()) ? super() : '../invalid-qualified-path'
806
- end
807
- let(:invalid_relative_path) do
808
- defined?(super()) ? super() : 'invalid-relative-path'
1109
+ describe '#directory?' do
1110
+ define_method :check_directory do
1111
+ subject.directory?(path)
809
1112
  end
810
- let(:expected_contents) do
811
- path = self.path
1113
+ alias_method :call_method, :check_directory
812
1114
 
813
- if path.start_with?('.')
814
- path = File.expand_path(File.join(subject.root_path, path))
815
- end
816
-
817
- path = path.sub(%r{\A#{subject.root_path}/?}, '')
818
-
819
- "Contents of #{path}"
1115
+ it 'should define the method' do
1116
+ expect(subject).to respond_to(:directory?).with(1).argument
820
1117
  end
821
1118
 
822
- it { expect(subject).to respond_to(:read_file).with(1).argument }
823
-
824
1119
  it 'should define the aliased method' do
825
- expect(subject).to have_aliased_method(:read_file).as(:read)
826
- end
827
-
828
- describe 'with nil' do
829
- let(:error_message) do
830
- tools.assertions.error_message_for('presence', as: :file)
831
- end
832
-
833
- it 'should raise an exception' do
834
- expect { subject.read_file(nil) }
835
- .to raise_error ArgumentError, error_message
836
- end
837
- end
838
-
839
- describe 'with an Object' do
840
- let(:error_message) do
841
- 'file is not a String or IO stream'
842
- end
843
-
844
- it 'should raise an exception' do
845
- expect { subject.read_file(Object.new.freeze) }
846
- .to raise_error ArgumentError, error_message
847
- end
848
- end
849
-
850
- describe 'with an empty String' do
851
- let(:error_message) do
852
- tools.assertions.error_message_for('presence', as: :file)
853
- end
854
-
855
- it 'should raise an exception' do
856
- expect { subject.read_file('') }
857
- .to raise_error ArgumentError, error_message
858
- end
1120
+ expect(subject)
1121
+ .to have_aliased_method(:directory?)
1122
+ .as(:directory_exists?)
859
1123
  end
860
1124
 
861
- describe 'with an IO stream' do
862
- let(:stream) { StringIO.new('Greetings, programs!') }
863
-
864
- it { expect(subject.read_file(stream)).to be == stream.string }
865
- end
1125
+ include_deferred 'should validate the path'
866
1126
 
867
1127
  describe 'with an invalid absolute path' do
868
1128
  let(:path) { invalid_absolute_path }
869
- let(:error_class) do
870
- Cuprum::Cli::Dependencies::FileSystem::FileNotFoundError
871
- end
872
- let(:error_message) do
873
- "unable to read file #{path} - file not found"
874
- end
875
1129
 
876
- it 'should raise an exception' do
877
- expect { subject.read_file(path) }
878
- .to raise_error(error_class, error_message)
879
- end
1130
+ it { expect(subject.directory?(path)).to be false }
880
1131
  end
881
1132
 
882
1133
  describe 'with an invalid qualified path' do
883
1134
  let(:path) { invalid_qualified_path }
884
- let(:error_class) do
885
- Cuprum::Cli::Dependencies::FileSystem::FileNotFoundError
886
- end
887
- let(:error_message) do
888
- "unable to read file #{path} - file not found"
889
- end
890
1135
 
891
- it 'should raise an exception' do
892
- expect { subject.read_file(path) }
893
- .to raise_error(error_class, error_message)
894
- end
1136
+ it { expect(subject.directory?(path)).to be false }
895
1137
  end
896
1138
 
897
1139
  describe 'with an invalid relative path' do
898
1140
  let(:path) { invalid_relative_path }
899
- let(:error_class) do
900
- Cuprum::Cli::Dependencies::FileSystem::FileNotFoundError
901
- end
902
- let(:error_message) do
903
- "unable to read file #{path} - file not found"
904
- end
905
1141
 
906
- it 'should raise an exception' do
907
- expect { subject.read_file(path) }
908
- .to raise_error(error_class, error_message)
909
- end
1142
+ it { expect(subject.directory?(path)).to be false }
910
1143
  end
911
1144
 
912
1145
  wrap_deferred 'with valid file paths' do
913
1146
  describe 'with an absolute path to a directory' do
914
1147
  let(:path) { absolute_directory_path }
915
- let(:error_class) do
916
- Cuprum::Cli::Dependencies::FileSystem::FileIsADirectoryError
917
- end
918
- let(:error_message) do
919
- "unable to read file #{path} - file is a directory"
920
- end
921
1148
 
922
- it 'should raise an exception' do
923
- expect { subject.read_file(path) }
924
- .to raise_error(error_class, error_message)
925
- end
1149
+ it { expect(subject.directory?(path)).to be true }
926
1150
  end
927
1151
 
928
1152
  describe 'with an absolute path to a file' do
929
1153
  let(:path) { absolute_file_path }
930
1154
 
931
- it { expect(subject.read_file(path)).to be == expected_contents }
1155
+ it { expect(subject.directory?(path)).to be false }
932
1156
  end
933
1157
 
934
1158
  describe 'with a qualified path to a directory' do
935
1159
  let(:path) { qualified_directory_path }
936
- let(:error_class) do
937
- Cuprum::Cli::Dependencies::FileSystem::FileIsADirectoryError
938
- end
939
- let(:error_message) do
940
- "unable to read file #{path} - file is a directory"
941
- end
942
1160
 
943
- it 'should raise an exception' do
944
- expect { subject.read_file(path) }
945
- .to raise_error(error_class, error_message)
946
- end
1161
+ it { expect(subject.directory?(path)).to be true }
947
1162
  end
948
1163
 
949
1164
  describe 'with a qualified path to a file' do
950
1165
  let(:path) { qualified_file_path }
951
1166
 
952
- it { expect(subject.read_file(path)).to be == expected_contents }
1167
+ it { expect(subject.directory?(path)).to be false }
953
1168
  end
954
1169
 
955
1170
  describe 'with a relative path to a directory' do
956
1171
  let(:path) { relative_directory_path }
957
- let(:error_class) do
958
- Cuprum::Cli::Dependencies::FileSystem::FileIsADirectoryError
959
- end
960
- let(:error_message) do
961
- "unable to read file #{path} - file is a directory"
962
- end
963
1172
 
964
- it 'should raise an exception' do
965
- expect { subject.read_file(path) }
966
- .to raise_error(error_class, error_message)
967
- end
1173
+ it { expect(subject.directory?(path)).to be true }
968
1174
  end
969
1175
 
970
1176
  describe 'with a relative path to a file' do
971
1177
  let(:path) { relative_file_path }
972
1178
 
973
- it { expect(subject.read_file(path)).to be == expected_contents }
1179
+ it { expect(subject.directory?(path)).to be false }
974
1180
  end
975
1181
 
976
- wrap_deferred 'with valid file paths' do
1182
+ wrap_deferred 'when initialized with root_path: value' do
977
1183
  describe 'with a qualified path to a directory' do
978
1184
  let(:path) { qualified_directory_path }
979
- let(:error_class) do
980
- Cuprum::Cli::Dependencies::FileSystem::FileIsADirectoryError
981
- end
982
- let(:error_message) do
983
- "unable to read file #{path} - file is a directory"
984
- end
985
1185
 
986
- it 'should raise an exception' do
987
- expect { subject.read_file(path) }
988
- .to raise_error(error_class, error_message)
989
- end
1186
+ it { expect(subject.directory?(path)).to be true }
990
1187
  end
991
1188
 
992
1189
  describe 'with a qualified path to a file' do
993
1190
  let(:path) { qualified_file_path }
994
1191
 
995
- it { expect(subject.read_file(path)).to be == expected_contents }
1192
+ it { expect(subject.directory?(path)).to be false }
996
1193
  end
997
1194
 
998
1195
  describe 'with a relative path to a directory' do
999
1196
  let(:path) { relative_directory_path }
1000
- let(:error_class) do
1001
- Cuprum::Cli::Dependencies::FileSystem::FileIsADirectoryError
1002
- end
1003
- let(:error_message) do
1004
- "unable to read file #{path} - file is a directory"
1005
- end
1006
1197
 
1007
- it 'should raise an exception' do
1008
- expect { subject.read_file(path) }
1009
- .to raise_error(error_class, error_message)
1010
- end
1198
+ it { expect(subject.directory?(path)).to be true }
1011
1199
  end
1012
1200
 
1013
1201
  describe 'with a relative path to a file' do
1014
1202
  let(:path) { relative_file_path }
1015
1203
 
1016
- it { expect(subject.read_file(path)).to be == expected_contents }
1204
+ it { expect(subject.directory?(path)).to be false }
1017
1205
  end
1018
1206
  end
1019
1207
  end
1020
1208
  end
1021
1209
 
1022
- describe '#root_path' do
1023
- include_examples 'should define reader', :root_path, Dir.pwd
1210
+ describe '#each_file' do
1211
+ deferred_examples 'should return or yield the matching file names' do
1212
+ describe 'without a block' do
1213
+ it { expect(subject.each_file(pattern)).to be_a Enumerator }
1024
1214
 
1025
- wrap_deferred 'when initialized with root_path: value' do
1026
- it { expect(subject.root_path).to be == root_path }
1027
- end
1028
- end
1215
+ it 'should return the matching file names' do
1216
+ expect(subject.each_file(pattern).to_a).to be == expected_files
1217
+ end
1218
+ end
1029
1219
 
1030
- describe '#with_tempfile' do
1031
- let(:expected_tempfile) do
1032
- respond_to(:read)
1033
- .and(respond_to(:write)
1034
- .and(respond_to(:path)))
1220
+ describe 'with a block' do
1221
+ it { expect(subject.each_file(pattern) { nil }).to be nil }
1222
+
1223
+ it 'should yield the matching file names' do
1224
+ expect { |block| subject.each_file(pattern, &block) }.then \
1225
+ do |expectation|
1226
+ if expected_files.empty?
1227
+ expectation.not_to(yield_control)
1228
+ else
1229
+ expectation.to yield_successive_args(*expected_files)
1230
+ end
1231
+ end
1232
+ end
1233
+ end
1035
1234
  end
1036
1235
 
1236
+ let(:defined_files) { flattened_fixture_names }
1237
+
1037
1238
  it 'should define the method' do
1038
1239
  expect(subject)
1039
- .to respond_to(:with_tempfile)
1040
- .with(0).arguments
1240
+ .to respond_to(:each_file)
1241
+ .with(1).argument
1041
1242
  .and_a_block
1042
1243
  end
1043
1244
 
1044
- it 'should yield the file object' do
1045
- expect { |block| subject.with_tempfile(&block) }
1046
- .to yield_with_args(expected_tempfile)
1047
- end
1048
- end
1049
-
1050
- describe '#write_file', writeable_root_path: true do
1051
- let(:invalid_absolute_path) do
1052
- defined?(super()) ? super() : '/invalid-absolute-path'
1053
- end
1054
- let(:invalid_qualified_path) do
1055
- defined?(super()) ? super() : '../invalid-qualified-path'
1056
- end
1057
- let(:invalid_relative_path) do
1058
- defined?(super()) ? super() : 'invalid-relative-path'
1059
- end
1060
- let(:file_name) { "#{SecureRandom.uuid}.txt" }
1061
- let(:data) { "Greetings, programs!\n" }
1062
-
1063
- include_deferred 'with valid file paths'
1064
-
1065
- it { expect(subject).to respond_to(:write_file).with(2).arguments }
1066
-
1067
- it 'should define the aliased method' do
1068
- expect(subject).to have_aliased_method(:write_file).as(:write)
1069
- end
1245
+ wrap_deferred 'with valid file paths' do
1246
+ let(:expected_files) { defined_files }
1070
1247
 
1071
- describe 'with data: nil' do
1072
- let(:data) { nil }
1073
- let(:stream) { StringIO.new }
1248
+ describe 'with a pattern that does not match any files' do
1249
+ let(:pattern) { '*.xml' }
1250
+ let(:expected_files) { [] }
1074
1251
 
1075
- it 'should not write to the stream' do
1076
- expect { subject.write_file(stream, data) }
1077
- .not_to change(stream, :string)
1252
+ include_deferred 'should return or yield the matching file names'
1078
1253
  end
1079
- end
1080
-
1081
- describe 'with data: an Object' do
1082
- let(:data) { Object.new.freeze }
1083
- let(:stream) { StringIO.new }
1084
1254
 
1085
- it 'should write the object to the stream' do
1086
- expect { subject.write_file(stream, data) }
1087
- .to change(stream, :string)
1088
- .to be == data.inspect
1089
- end
1090
- end
1255
+ describe 'with a globbed pattern' do
1256
+ let(:pattern) { '*.txt' }
1257
+ let(:expected_files) do
1258
+ rxp = /\A\w+\.txt\z/
1091
1259
 
1092
- describe 'with data: an empty String' do
1093
- let(:data) { '' }
1094
- let(:stream) { StringIO.new }
1260
+ defined_files.grep(rxp)
1261
+ end
1095
1262
 
1096
- it 'should not write to the stream' do
1097
- expect { subject.write_file(stream, data) }
1098
- .not_to change(stream, :string)
1263
+ include_deferred 'should return or yield the matching file names'
1099
1264
  end
1100
- end
1101
1265
 
1102
- describe 'with file: nil' do
1103
- let(:error_message) do
1104
- tools.assertions.error_message_for('presence', as: :file)
1105
- end
1266
+ describe 'with a globbed pattern with fixed segments' do
1267
+ let(:pattern) { File.join('root_dir', '*.txt') }
1268
+ let(:expected_files) do
1269
+ rxp = %r{\Aroot_dir/\w+\.txt\z}
1106
1270
 
1107
- it 'should raise an exception' do
1108
- expect { subject.write_file(nil, data) }
1109
- .to raise_error ArgumentError, error_message
1110
- end
1111
- end
1271
+ defined_files.grep(rxp)
1272
+ end
1112
1273
 
1113
- describe 'with file: an Object' do
1114
- let(:error_message) do
1115
- 'file is not a String or IO stream'
1274
+ include_deferred 'should return or yield the matching file names'
1116
1275
  end
1117
1276
 
1118
- it 'should raise an exception' do
1119
- expect { subject.write_file(Object.new.freeze, data) }
1120
- .to raise_error ArgumentError, error_message
1121
- end
1122
- end
1277
+ describe 'with a globbed pattern without extension filter' do
1278
+ let(:pattern) { '*' }
1279
+ let(:expected_files) do
1280
+ rxp = /\A\w+(\.\w+)?\z/
1123
1281
 
1124
- describe 'with file: an empty String' do
1125
- let(:error_message) do
1126
- tools.assertions.error_message_for('presence', as: :file)
1127
- end
1282
+ defined_files.grep(rxp)
1283
+ end
1128
1284
 
1129
- it 'should raise an exception' do
1130
- expect { subject.write_file('', data) }
1131
- .to raise_error ArgumentError, error_message
1285
+ include_deferred 'should return or yield the matching file names'
1132
1286
  end
1133
- end
1134
1287
 
1135
- describe 'with file: an IO stream' do
1136
- let(:stream) { StringIO.new }
1288
+ describe 'with a recursive globbed pattern' do
1289
+ let(:pattern) { File.join('**', '*.txt') }
1290
+ let(:expected_files) do
1291
+ rxp = /\.txt\z/
1137
1292
 
1138
- it 'should write the data to the stream' do
1139
- expect { subject.write_file(stream, data) }
1140
- .to change(stream, :string)
1141
- .to be == data
1142
- end
1143
- end
1293
+ defined_files.grep(rxp)
1294
+ end
1144
1295
 
1145
- describe 'with an absolute path' do
1146
- let(:directory) do
1147
- File.join(subject.root_path, writeable_path)
1148
- end
1149
- let(:path) do
1150
- File.join(directory, "#{SecureRandom.uuid}.txt")
1296
+ include_deferred 'should return or yield the matching file names'
1151
1297
  end
1152
1298
 
1153
- include_deferred 'when created files are cleaned up'
1154
-
1155
- it 'should write the data to a new file' do
1156
- subject.write_file(path, data)
1157
-
1158
- expect(subject.read_file(path)).to be == data
1159
- end
1299
+ describe 'with a recursive globbed pattern with prefix' do
1300
+ let(:pattern) { File.join('root_dir', '**') }
1301
+ let(:expected_files) do
1302
+ rxp = %r{\Aroot_dir/}
1160
1303
 
1161
- context 'when the directory is not writeable' do
1162
- let(:directory) { invalid_absolute_path }
1163
- let(:error_class) do
1164
- Cuprum::Cli::Dependencies::FileSystem::DirectoryNotFoundError
1165
- end
1166
- let(:error_message) do
1167
- "unable to write file #{path} - directory not found"
1304
+ defined_files.grep(rxp)
1168
1305
  end
1169
1306
 
1170
- it 'should raise an exception' do
1171
- expect { subject.write_file(path, data) }
1172
- .to raise_error(error_class, error_message)
1173
- end
1307
+ include_deferred 'should return or yield the matching file names'
1174
1308
  end
1175
1309
 
1176
- context 'when the file already exists' do
1177
- before(:example) do
1178
- subject.write_file(path, "Existing contents...\n")
1179
- end
1310
+ describe 'with a pattern that includes the root path' do
1311
+ let(:pattern) { File.join(subject.root_path, '*.txt') }
1312
+ let(:expected_files) do
1313
+ rxp = /\A\w+\.txt\z/
1180
1314
 
1181
- it 'should replace the contents of the file' do
1182
- subject.write_file(path, data)
1315
+ defined_files
1316
+ .map do |path|
1317
+ path = File.expand_path(File.join(fixtures_path, path))
1183
1318
 
1184
- expect(subject.read_file(path)).to be == data
1185
- end
1186
- end
1319
+ next unless path.start_with?(subject.root_path)
1187
1320
 
1188
- context 'when the file is a directory' do
1189
- let(:path) { directory }
1190
- let(:error_class) do
1191
- Cuprum::Cli::Dependencies::FileSystem::FileIsADirectoryError
1192
- end
1193
- let(:error_message) do
1194
- "unable to write file #{path} - file is a directory"
1321
+ path[(1 + subject.root_path.size)...]
1322
+ end # rubocop:disable Style/MultilineBlockChain
1323
+ .compact
1324
+ .grep(rxp)
1325
+ .map { |path| File.join(subject.root_path, path) }
1195
1326
  end
1196
1327
 
1197
- it 'should raise an exception' do
1198
- expect { subject.write_file(path, data) }
1199
- .to raise_error(error_class, error_message)
1328
+ include_deferred 'should return or yield the matching file names'
1329
+
1330
+ wrap_deferred 'when initialized with root_path: value' do
1331
+ include_deferred 'should return or yield the matching file names'
1200
1332
  end
1201
1333
  end
1202
1334
 
1203
- context 'when the path includes a file' do
1204
- let(:directory) { File.join(super(), 'a_file') }
1205
- let(:error_class) do
1206
- Cuprum::Cli::Dependencies::FileSystem::DirectoryIsAFileError
1335
+ context 'when a matching file is added to the file system' do
1336
+ let(:pattern) { '*.txt' }
1337
+ let(:added_file_path) { 'added_file.txt' }
1338
+ let(:expanded_added_path) do
1339
+ File.expand_path(File.join(fixtures_path, 'added_file.txt'))
1207
1340
  end
1208
- let(:error_message) do
1209
- "unable to write file #{path} - directory is a file"
1341
+ let(:expected_files) do
1342
+ rxp = /\A\w+\.txt\z/
1343
+
1344
+ defined_files
1345
+ .grep(rxp)
1346
+ .push(added_file_path)
1347
+ .sort
1210
1348
  end
1211
1349
 
1212
1350
  before(:example) do
1213
- subject.write_file(directory, "Existing contents...\n")
1214
- end
1351
+ subject.each_file(pattern) { nil }
1215
1352
 
1216
- it 'should raise an exception' do
1217
- expect { subject.write_file(path, data) }
1218
- .to raise_error(error_class, error_message)
1353
+ subject.write_file(
1354
+ expanded_added_path,
1355
+ 'This file was added in the test.'
1356
+ )
1219
1357
  end
1220
- end
1221
1358
 
1222
- context 'when the path includes missing directories' do
1223
- let(:directory) do
1224
- File.join(super(), 'missing', 'directories')
1225
- end
1226
- let(:error_class) do
1227
- Cuprum::Cli::Dependencies::FileSystem::DirectoryNotFoundError
1228
- end
1229
- let(:error_message) do
1230
- "unable to write file #{path} - directory not found"
1231
- end
1359
+ after(:example) do
1360
+ next unless subject.file?(expanded_added_path)
1232
1361
 
1233
- it 'should raise an exception' do
1234
- expect { subject.write_file(path, data) }
1235
- .to raise_error(error_class, error_message)
1362
+ subject.delete_file(expanded_added_path)
1236
1363
  end
1364
+
1365
+ include_deferred 'should return or yield the matching file names'
1237
1366
  end
1238
1367
  end
1368
+ end
1239
1369
 
1240
- describe 'with a qualified path' do
1241
- let(:directory) do
1242
- File.join('.', writeable_path)
1243
- end
1244
- let(:path) do
1245
- File.join(directory, "#{SecureRandom.uuid}.txt")
1246
- end
1370
+ describe '#file?' do
1371
+ define_method :check_file do
1372
+ subject.file?(path)
1373
+ end
1374
+ alias_method :call_method, :check_file
1247
1375
 
1248
- include_deferred 'when created files are cleaned up'
1376
+ it 'should define the method' do
1377
+ expect(subject).to respond_to(:file?).with(1).argument
1378
+ end
1249
1379
 
1250
- it 'should write the data to a new file' do
1251
- subject.write_file(path, data)
1380
+ it 'should define the aliased method' do
1381
+ expect(subject)
1382
+ .to have_aliased_method(:file?)
1383
+ .as(:file_exists?)
1384
+ end
1252
1385
 
1253
- expect(subject.read_file(path)).to be == data
1254
- end
1386
+ include_deferred 'should validate the path'
1255
1387
 
1256
- context 'when the directory is not writeable' do
1257
- let(:directory) { invalid_absolute_path }
1258
- let(:error_class) do
1259
- Cuprum::Cli::Dependencies::FileSystem::DirectoryNotFoundError
1260
- end
1261
- let(:error_message) do
1262
- "unable to write file #{path} - directory not found"
1263
- end
1388
+ describe 'with an invalid absolute path' do
1389
+ let(:path) { invalid_absolute_path }
1264
1390
 
1265
- it 'should raise an exception' do
1266
- expect { subject.write_file(path, data) }
1267
- .to raise_error(error_class, error_message)
1268
- end
1391
+ it { expect(subject.file?(path)).to be false }
1392
+ end
1393
+
1394
+ describe 'with an invalid qualified path' do
1395
+ let(:path) { invalid_qualified_path }
1396
+
1397
+ it { expect(subject.file?(path)).to be false }
1398
+ end
1399
+
1400
+ describe 'with an invalid relative path' do
1401
+ let(:path) { invalid_relative_path }
1402
+
1403
+ it { expect(subject.file?(path)).to be false }
1404
+ end
1405
+
1406
+ wrap_deferred 'with valid file paths' do
1407
+ describe 'with an absolute path to a directory' do
1408
+ let(:path) { absolute_directory_path }
1409
+
1410
+ it { expect(subject.file?(path)).to be false }
1269
1411
  end
1270
1412
 
1271
- context 'when the file already exists' do
1272
- before(:example) do
1273
- subject.write_file(path, "Existing contents...\n")
1274
- end
1413
+ describe 'with an absolute path to a file' do
1414
+ let(:path) { absolute_file_path }
1275
1415
 
1276
- it 'should replace the contents of the file' do
1277
- subject.write_file(path, data)
1416
+ it { expect(subject.file?(path)).to be true }
1417
+ end
1278
1418
 
1279
- expect(subject.read_file(path)).to be == data
1280
- end
1419
+ describe 'with a qualified path to a directory' do
1420
+ let(:path) { qualified_directory_path }
1421
+
1422
+ it { expect(subject.file?(path)).to be false }
1281
1423
  end
1282
1424
 
1283
- context 'when the file is a directory' do
1284
- let(:path) { directory }
1285
- let(:error_class) do
1286
- Cuprum::Cli::Dependencies::FileSystem::FileIsADirectoryError
1287
- end
1288
- let(:error_message) do
1289
- "unable to write file #{path} - file is a directory"
1290
- end
1425
+ describe 'with a qualified path to a file' do
1426
+ let(:path) { qualified_file_path }
1291
1427
 
1292
- it 'should raise an exception' do
1293
- expect { subject.write_file(path, data) }
1294
- .to raise_error(error_class, error_message)
1295
- end
1428
+ it { expect(subject.file?(path)).to be true }
1296
1429
  end
1297
1430
 
1298
- context 'when the path includes a file' do
1299
- let(:directory) { File.join(super(), 'a_file') }
1300
- let(:error_class) do
1301
- Cuprum::Cli::Dependencies::FileSystem::DirectoryIsAFileError
1302
- end
1303
- let(:error_message) do
1304
- "unable to write file #{path} - directory is a file"
1305
- end
1431
+ describe 'with a relative path to a directory' do
1432
+ let(:path) { relative_directory_path }
1306
1433
 
1307
- before(:example) do
1308
- subject.write_file(directory, "Existing contents...\n")
1309
- end
1434
+ it { expect(subject.file?(path)).to be false }
1435
+ end
1310
1436
 
1311
- it 'should raise an exception' do
1312
- expect { subject.write_file(path, data) }
1313
- .to raise_error(error_class, error_message)
1314
- end
1437
+ describe 'with a relative path to a file' do
1438
+ let(:path) { relative_file_path }
1439
+
1440
+ it { expect(subject.file?(path)).to be true }
1315
1441
  end
1316
1442
 
1317
- context 'when the path includes missing directories' do
1318
- let(:directory) do
1319
- File.join(super(), 'missing', 'directories')
1443
+ wrap_deferred 'when initialized with root_path: value' do
1444
+ describe 'with a qualified path to a directory' do
1445
+ let(:path) { qualified_directory_path }
1446
+
1447
+ it { expect(subject.file?(path)).to be false }
1320
1448
  end
1321
- let(:error_class) do
1322
- Cuprum::Cli::Dependencies::FileSystem::DirectoryNotFoundError
1449
+
1450
+ describe 'with a qualified path to a file' do
1451
+ let(:path) { qualified_file_path }
1452
+
1453
+ it { expect(subject.file?(path)).to be true }
1323
1454
  end
1324
- let(:error_message) do
1325
- "unable to write file #{path} - directory not found"
1455
+
1456
+ describe 'with a relative path to a directory' do
1457
+ let(:path) { relative_directory_path }
1458
+
1459
+ it { expect(subject.file?(path)).to be false }
1326
1460
  end
1327
1461
 
1328
- it 'should raise an exception' do
1329
- expect { subject.write_file(path, data) }
1330
- .to raise_error(error_class, error_message)
1462
+ describe 'with a relative path to a file' do
1463
+ let(:path) { relative_file_path }
1464
+
1465
+ it { expect(subject.file?(path)).to be true }
1331
1466
  end
1332
1467
  end
1333
1468
  end
1469
+ end
1334
1470
 
1335
- describe 'with a relative path' do
1336
- let(:directory) do
1337
- writeable_path
1471
+ describe '#read_file' do
1472
+ deferred_examples 'should read the file' do
1473
+ it { expect(subject.read_file(file)).to be == expected_contents }
1474
+ end
1475
+
1476
+ deferred_examples 'should read the file for a valid path' do
1477
+ include_deferred 'should read the file'
1478
+
1479
+ include_deferred 'should handle a directory when a file is expected',
1480
+ :file
1481
+ end
1482
+
1483
+ let(:expected_contents) do
1484
+ path = file
1485
+
1486
+ unless path.start_with?('/')
1487
+ path = File.expand_path(File.join(subject.root_path, path))
1338
1488
  end
1339
- let(:path) do
1340
- File.join(directory, "#{SecureRandom.uuid}.txt")
1489
+
1490
+ path = path.sub(%r{\A#{File.expand_path(fixtures_path)}/?}, '')
1491
+
1492
+ read_fixture(path)
1493
+ end
1494
+ let(:file) { nil }
1495
+ let(:error_reason) { "unable to read file #{file}" }
1496
+
1497
+ define_method :read_file do
1498
+ subject.read_file(file)
1499
+ end
1500
+ alias_method :call_method, :read_file
1501
+
1502
+ define_method :safe_read_file do
1503
+ read_file
1504
+ rescue StandardError
1505
+ nil
1506
+ end
1507
+
1508
+ it { expect(subject).to respond_to(:read_file).with(1).argument }
1509
+
1510
+ it 'should define the aliased method' do
1511
+ expect(subject).to have_aliased_method(:read_file).as(:read)
1512
+ end
1513
+
1514
+ include_deferred 'should validate the file or path', :file
1515
+
1516
+ describe 'with an IO stream' do
1517
+ let(:stream) { StringIO.new('Greetings, programs!') }
1518
+
1519
+ it { expect(subject.read_file(stream)).to be == stream.string }
1520
+ end
1521
+
1522
+ include_deferred 'should handle a path with missing file', :file
1523
+
1524
+ wrap_deferred 'with valid file paths' do
1525
+ describe 'with an absolute path' do
1526
+ let(:file) { absolute_file_path }
1527
+
1528
+ include_deferred 'should read the file for a valid path'
1341
1529
  end
1342
1530
 
1343
- include_deferred 'when created files are cleaned up'
1531
+ describe 'with a qualified path' do
1532
+ let(:file) { qualified_file_path }
1344
1533
 
1345
- it 'should write the data to a new file' do
1346
- subject.write_file(path, data)
1534
+ include_deferred 'should read the file for a valid path'
1535
+ end
1347
1536
 
1348
- expect(subject.read_file(path)).to be == data
1537
+ describe 'with a relative path' do
1538
+ let(:file) { relative_file_path }
1539
+
1540
+ include_deferred 'should read the file for a valid path'
1349
1541
  end
1350
1542
 
1351
- context 'when the directory is not writeable' do
1352
- let(:directory) { invalid_absolute_path }
1353
- let(:error_class) do
1354
- Cuprum::Cli::Dependencies::FileSystem::DirectoryNotFoundError
1355
- end
1356
- let(:error_message) do
1357
- "unable to write file #{path} - directory not found"
1543
+ wrap_deferred 'when initialized with root_path: value' do
1544
+ describe 'with a qualified path' do
1545
+ let(:file) { qualified_file_path }
1546
+
1547
+ include_deferred 'should read the file for a valid path'
1358
1548
  end
1359
1549
 
1360
- it 'should raise an exception' do
1361
- expect { subject.write_file(path, data) }
1362
- .to raise_error(error_class, error_message)
1550
+ describe 'with a relative path' do
1551
+ let(:file) { relative_file_path }
1552
+
1553
+ include_deferred 'should read the file for a valid path'
1363
1554
  end
1364
1555
  end
1556
+ end
1557
+ end
1365
1558
 
1366
- context 'when the file already exists' do
1367
- before(:example) do
1368
- subject.write_file(path, "Existing contents...\n")
1369
- end
1559
+ describe '#root_path' do
1560
+ include_examples 'should define reader', :root_path, Dir.pwd
1370
1561
 
1371
- it 'should replace the contents of the file' do
1372
- subject.write_file(path, data)
1562
+ wrap_deferred 'when initialized with root_path: value' do
1563
+ it { expect(subject.root_path).to be == root_path }
1564
+ end
1565
+ end
1373
1566
 
1374
- expect(subject.read_file(path)).to be == data
1375
- end
1567
+ describe '#with_tempfile' do
1568
+ let(:expected_tempfile) do
1569
+ respond_to(:read)
1570
+ .and(respond_to(:write)
1571
+ .and(respond_to(:path)))
1572
+ end
1573
+
1574
+ it 'should define the method' do
1575
+ expect(subject)
1576
+ .to respond_to(:with_tempfile)
1577
+ .with(0).arguments
1578
+ .and_a_block
1579
+ end
1580
+
1581
+ it 'should yield the file object' do
1582
+ expect { |block| subject.with_tempfile(&block) }
1583
+ .to yield_with_args(expected_tempfile)
1584
+ end
1585
+ end
1586
+
1587
+ describe '#write_file' do
1588
+ deferred_examples 'should not write the file' do
1589
+ it 'should not write the file', :aggregate_failures do
1590
+ existing_file = subject.file?(file)
1591
+ existing_contents = subject.read_file(file) if existing_file
1592
+
1593
+ expect { safe_write_file }.not_to(change { subject.file?(file) })
1594
+
1595
+ next unless existing_file
1596
+
1597
+ expect(subject.read_file(file)).to eq(existing_contents)
1376
1598
  end
1599
+ end
1377
1600
 
1378
- context 'when the file is a directory' do
1379
- let(:path) { directory }
1380
- let(:error_class) do
1381
- Cuprum::Cli::Dependencies::FileSystem::FileIsADirectoryError
1382
- end
1383
- let(:error_message) do
1384
- "unable to write file #{path} - file is a directory"
1385
- end
1601
+ deferred_examples 'should create the file' do
1602
+ it 'should create the file' do
1603
+ expect { write_file }.to(
1604
+ change { subject.file?(file) }.to(be true)
1605
+ )
1606
+ end
1607
+ end
1386
1608
 
1387
- it 'should raise an exception' do
1388
- expect { subject.write_file(path, data) }
1389
- .to raise_error(error_class, error_message)
1390
- end
1609
+ deferred_examples 'should write the data to the file' do
1610
+ it 'should write the data to the file' do
1611
+ write_file
1612
+
1613
+ expect(subject.read_file(file)).to be == data
1391
1614
  end
1615
+ end
1392
1616
 
1393
- context 'when the path includes a file' do
1394
- let(:directory) { File.join(super(), 'a_file') }
1395
- let(:error_class) do
1396
- Cuprum::Cli::Dependencies::FileSystem::DirectoryIsAFileError
1397
- end
1398
- let(:error_message) do
1399
- "unable to write file #{path} - directory is a file"
1400
- end
1617
+ deferred_examples 'should write the file for a valid path' do
1618
+ include_deferred 'should create the file'
1401
1619
 
1402
- before(:example) do
1403
- subject.write_file(directory, "Existing contents...\n")
1404
- end
1620
+ include_deferred 'should write the data to the file'
1405
1621
 
1406
- it 'should raise an exception' do
1407
- expect { subject.write_file(path, data) }
1408
- .to raise_error(error_class, error_message)
1409
- end
1622
+ include_deferred 'should handle a path with missing directory',
1623
+ :file \
1624
+ do
1625
+ include_deferred 'should not write the file'
1410
1626
  end
1411
1627
 
1412
- context 'when the path includes missing directories' do
1413
- let(:directory) do
1414
- File.join(super(), 'missing', 'directories')
1415
- end
1416
- let(:error_class) do
1417
- Cuprum::Cli::Dependencies::FileSystem::DirectoryNotFoundError
1418
- end
1419
- let(:error_message) do
1420
- "unable to write file #{path} - directory not found"
1421
- end
1628
+ include_deferred 'should handle a directory when a file is expected',
1629
+ :file \
1630
+ do
1631
+ include_deferred 'should not write the file'
1632
+ end
1422
1633
 
1423
- it 'should raise an exception' do
1424
- expect { subject.write_file(path, data) }
1425
- .to raise_error(error_class, error_message)
1634
+ context 'when the file already exists' do
1635
+ let(:file) do
1636
+ if absolute_path?(super())
1637
+ absolute_file_path
1638
+ elsif qualified_path?(super())
1639
+ qualified_file_path
1640
+ else
1641
+ relative_file_path
1642
+ end
1426
1643
  end
1644
+
1645
+ include_deferred 'should write the data to the file'
1646
+ end
1647
+
1648
+ include_deferred 'should handle a path that includes a file',
1649
+ :file \
1650
+ do
1651
+ include_deferred 'should not write the file'
1652
+ end
1653
+
1654
+ include_deferred 'should handle a path with missing directory',
1655
+ :file \
1656
+ do
1657
+ include_deferred 'should not write the file'
1427
1658
  end
1428
1659
  end
1429
1660
 
1430
- wrap_deferred 'when initialized with root_path: value' do
1431
- include_deferred 'when created files are cleaned up'
1661
+ let(:file) { "#{SecureRandom.uuid}.txt" }
1662
+ let(:data) { "Greetings, programs!\n" }
1663
+ let(:error_reason) { "unable to write file #{file}" }
1432
1664
 
1433
- describe 'with a qualified path' do
1434
- let(:directory) do
1435
- File.join('..', '..', 'tmp', 'files')
1436
- end
1437
- let(:path) do
1438
- File.join(directory, "#{SecureRandom.uuid}.txt")
1439
- end
1440
- let(:expanded_path) do
1441
- File.expand_path(File.join(root_path, path))
1665
+ define_method :write_file do
1666
+ subject.write_file(file, data)
1667
+ end
1668
+ alias_method :call_method, :write_file
1669
+
1670
+ define_method :safe_write_file do
1671
+ write_file
1672
+ rescue StandardError
1673
+ nil
1674
+ end
1675
+
1676
+ it { expect(subject).to respond_to(:write_file).with(2).arguments }
1677
+
1678
+ it 'should define the aliased method' do
1679
+ expect(subject).to have_aliased_method(:write_file).as(:write)
1680
+ end
1681
+
1682
+ include_deferred 'should validate the file or path', :file
1683
+
1684
+ describe 'with data: nil' do
1685
+ let(:data) { nil }
1686
+ let(:stream) { StringIO.new }
1687
+
1688
+ it 'should not write to the stream' do
1689
+ expect { subject.write_file(stream, data) }
1690
+ .not_to change(stream, :string)
1691
+ end
1692
+ end
1693
+
1694
+ describe 'with data: an Object' do
1695
+ let(:data) { Object.new.freeze }
1696
+ let(:stream) { StringIO.new }
1697
+
1698
+ it 'should write the object to the stream' do
1699
+ expect { subject.write_file(stream, data) }
1700
+ .to change(stream, :string)
1701
+ .to be == data.inspect
1702
+ end
1703
+ end
1704
+
1705
+ describe 'with data: an empty String' do
1706
+ let(:data) { '' }
1707
+ let(:stream) { StringIO.new }
1708
+
1709
+ it 'should not write to the stream' do
1710
+ expect { subject.write_file(stream, data) }
1711
+ .not_to change(stream, :string)
1712
+ end
1713
+ end
1714
+
1715
+ describe 'with file: an IO stream' do
1716
+ let(:stream) { StringIO.new }
1717
+
1718
+ it 'should write the data to the stream' do
1719
+ expect { subject.write_file(stream, data) }
1720
+ .to change(stream, :string)
1721
+ .to be == data
1722
+ end
1723
+ end
1724
+
1725
+ wrap_deferred 'with valid file paths' do
1726
+ describe 'with an absolute path' do
1727
+ let(:file) do
1728
+ File.join(absolute_directory_path, "#{SecureRandom.uuid}.txt")
1442
1729
  end
1443
1730
 
1444
- it 'should write the data to a new file' do
1445
- subject.write_file(path, data)
1731
+ include_deferred 'should write the file for a valid path'
1732
+ end
1446
1733
 
1447
- expect(subject.read_file(path)).to be == data
1734
+ describe 'with a qualified path' do
1735
+ let(:file) do
1736
+ File.join(qualified_directory_path, "#{SecureRandom.uuid}.txt")
1448
1737
  end
1738
+
1739
+ include_deferred 'should write the file for a valid path'
1449
1740
  end
1450
1741
 
1451
1742
  describe 'with a relative path' do
1452
- let(:path) do
1453
- File.join('nested', "#{SecureRandom.uuid}.txt")
1743
+ let(:file) do
1744
+ File.join(relative_directory_path, "#{SecureRandom.uuid}.txt")
1454
1745
  end
1455
- let(:expanded_path) do
1456
- File.expand_path(File.join(root_path, path))
1746
+
1747
+ include_deferred 'should write the file for a valid path'
1748
+ end
1749
+
1750
+ wrap_deferred 'when initialized with root_path: value' do
1751
+ describe 'with a qualified path' do
1752
+ let(:file) do
1753
+ File.join(qualified_directory_path, "#{SecureRandom.uuid}.txt")
1754
+ end
1755
+
1756
+ include_deferred 'should write the file for a valid path'
1457
1757
  end
1458
1758
 
1459
- it 'should write the data to a new file' do
1460
- subject.write_file(path, data)
1759
+ describe 'with a relative path' do
1760
+ let(:file) do
1761
+ File.join(relative_directory_path, "#{SecureRandom.uuid}.txt")
1762
+ end
1461
1763
 
1462
- expect(subject.read_file(path)).to be == data
1764
+ include_deferred 'should write the file for a valid path'
1463
1765
  end
1464
1766
  end
1465
1767
  end