app_archetype 1.2.8 → 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -1
  3. data/Gemfile.lock +102 -73
  4. data/README.md +166 -29
  5. data/app_archetype.gemspec +22 -19
  6. data/bin/app_archetype +20 -0
  7. data/bin/archetype +1 -1
  8. data/lib/app_archetype/cli.rb +171 -139
  9. data/lib/app_archetype/commands/delete_template.rb +58 -0
  10. data/lib/app_archetype/commands/find_templates.rb +66 -0
  11. data/lib/app_archetype/commands/list_templates.rb +49 -0
  12. data/lib/app_archetype/commands/new_template.rb +42 -0
  13. data/lib/app_archetype/commands/open_manifest.rb +48 -0
  14. data/lib/app_archetype/commands/print_path.rb +20 -0
  15. data/lib/app_archetype/commands/print_template_variables.rb +67 -0
  16. data/lib/app_archetype/commands/print_version.rb +19 -0
  17. data/lib/app_archetype/commands/render_template.rb +178 -0
  18. data/lib/app_archetype/commands.rb +13 -0
  19. data/lib/app_archetype/generators.rb +4 -3
  20. data/lib/app_archetype/template/manifest.rb +17 -1
  21. data/lib/app_archetype/template_manager.rb +13 -6
  22. data/lib/app_archetype/version.rb +1 -1
  23. data/lib/app_archetype.rb +40 -23
  24. data/lib/core_ext/string.rb +18 -12
  25. data/scripts/create_new_command +32 -0
  26. data/scripts/generators/command/manifest.json +15 -0
  27. data/scripts/generators/command/template/lib/app_archetype/commands/{{command_name.snake_case}}.rb.hbs +17 -0
  28. data/spec/app_archetype/cli/presenters_spec.rb +99 -99
  29. data/spec/app_archetype/cli/prompts_spec.rb +291 -291
  30. data/spec/app_archetype/cli_spec.rb +296 -65
  31. data/spec/app_archetype/commands/delete_template_spec.rb +132 -0
  32. data/spec/app_archetype/commands/find_templates_spec.rb +130 -0
  33. data/spec/app_archetype/commands/list_templates_spec.rb +55 -0
  34. data/spec/app_archetype/commands/new_template_spec.rb +84 -0
  35. data/spec/app_archetype/commands/open_manifest_spec.rb +113 -0
  36. data/spec/app_archetype/commands/print_path_spec.rb +22 -0
  37. data/spec/app_archetype/commands/print_template_variables_spec.rb +158 -0
  38. data/spec/app_archetype/commands/print_version_spec.rb +21 -0
  39. data/spec/app_archetype/commands/render_template_spec.rb +479 -0
  40. data/spec/app_archetype/generators_spec.rb +1 -1
  41. data/spec/app_archetype/template/manifest_spec.rb +31 -1
  42. data/spec/app_archetype/template_manager_spec.rb +32 -0
  43. data/spec/app_archetype_spec.rb +65 -0
  44. metadata +155 -80
  45. data/lib/app_archetype/cli/presenters.rb +0 -106
  46. data/lib/app_archetype/cli/prompts.rb +0 -152
@@ -0,0 +1,479 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe AppArchetype::Commands::RenderTemplate do
4
+ let(:manager) { double(AppArchetype::TemplateManager) }
5
+ let(:destination_path) { 'path/to/output' }
6
+ let(:options) { Hashie::Mash.new }
7
+ let(:prompt) { double(TTY::Prompt) }
8
+
9
+ subject { described_class.new(manager, destination_path, options) }
10
+
11
+ before do
12
+ subject.instance_variable_set(:@prompt, prompt)
13
+
14
+ allow(subject).to receive(:puts)
15
+ allow(prompt).to receive(:ask)
16
+ allow(prompt).to receive(:yes?)
17
+ allow(prompt).to receive(:select)
18
+ end
19
+
20
+ describe '#run' do
21
+ let(:manifest_names) { %s( manifest1 manifest2 ) }
22
+ let(:manifest_name) { 'some-manifest' }
23
+ let(:template) { double(AppArchetype::Template) }
24
+ let(:overwrite) { true }
25
+
26
+ context 'when a name is provided in options' do
27
+ let(:options) do
28
+ Hashie::Mash.new(
29
+ name: manifest_name,
30
+ overwrite: overwrite
31
+ )
32
+ end
33
+
34
+ let(:manifest) { double(AppArchetype::Template::Manifest) }
35
+
36
+ before do
37
+ allow(manager)
38
+ .to receive(:find_by_name)
39
+ .and_return(manifest)
40
+
41
+ allow(manifest).to receive(:template).and_return(template)
42
+ allow(template).to receive(:load)
43
+ allow(subject).to receive(:resolve_variables)
44
+ allow(subject).to receive(:render_template)
45
+ allow(subject).to receive(:puts)
46
+
47
+ subject.run
48
+ end
49
+
50
+ describe 'and template is found' do
51
+ it 'finds template by name' do
52
+ expect(manager)
53
+ .to have_received(:find_by_name)
54
+ .with(manifest_name)
55
+ end
56
+
57
+ it 'loads the template' do
58
+ expect(template)
59
+ .to have_received(:load)
60
+ end
61
+
62
+ it 'resolves variables' do
63
+ expect(subject)
64
+ .to have_received(:resolve_variables)
65
+ .with(manifest)
66
+ end
67
+
68
+ it 'renders template' do
69
+ expect(subject)
70
+ .to have_received(:render_template)
71
+ .with(
72
+ manifest,
73
+ template,
74
+ overwrite: overwrite
75
+ )
76
+ end
77
+
78
+ it 'prints success message' do
79
+ expect(subject)
80
+ .to have_received(:puts)
81
+ .with("✔ Rendered #{manifest_name} to #{destination_path}")
82
+ end
83
+ end
84
+
85
+ describe 'when template is not found' do
86
+ let(:manifest) { nil }
87
+
88
+ it 'attempts to find the template by name' do
89
+ expect(manager)
90
+ .to have_received(:find_by_name)
91
+ .with(manifest_name)
92
+ end
93
+
94
+ it 'prints no template found message' do
95
+ expect(subject)
96
+ .to have_received(:puts)
97
+ .with("✖ No template with name `#{manifest_name}` found.")
98
+ end
99
+
100
+ it 'does not resolve variables' do
101
+ expect(subject)
102
+ .not_to have_received(:resolve_variables)
103
+ end
104
+
105
+ it 'does not render template' do
106
+ expect(subject)
107
+ .not_to have_received(:render_template)
108
+ end
109
+ end
110
+ end
111
+
112
+ context 'when name is not provided in options' do
113
+ let(:options) do
114
+ Hashie::Mash.new(
115
+ overwrite: overwrite
116
+ )
117
+ end
118
+
119
+ let(:manifest) { double(AppArchetype::Template::Manifest) }
120
+
121
+ before do
122
+ allow(prompt).to receive(:select).and_return(manifest_name)
123
+ allow(manager)
124
+ .to receive(:manifest_names)
125
+ .and_return(manifest_names)
126
+
127
+ allow(manager)
128
+ .to receive(:find_by_name)
129
+ .and_return(manifest)
130
+
131
+ allow(manifest).to receive(:template).and_return(template)
132
+ allow(template).to receive(:load)
133
+ allow(subject).to receive(:resolve_variables)
134
+ allow(subject).to receive(:render_template)
135
+ allow(subject).to receive(:puts)
136
+
137
+ subject.run
138
+ end
139
+
140
+ it 'prompts user to select their manifest from a list' do
141
+ expect(prompt)
142
+ .to have_received(:select)
143
+ .with('Please choose manifest', manifest_names)
144
+ end
145
+
146
+ it 'loads the template' do
147
+ expect(template)
148
+ .to have_received(:load)
149
+ end
150
+
151
+ it 'resolves variables' do
152
+ expect(subject)
153
+ .to have_received(:resolve_variables)
154
+ .with(manifest)
155
+ end
156
+
157
+ it 'renders template' do
158
+ expect(subject)
159
+ .to have_received(:render_template)
160
+ .with(
161
+ manifest,
162
+ template,
163
+ overwrite: overwrite
164
+ )
165
+ end
166
+
167
+ it 'prints success message' do
168
+ expect(subject)
169
+ .to have_received(:puts)
170
+ .with("✔ Rendered #{manifest_name} to #{destination_path}")
171
+ end
172
+ end
173
+ end
174
+
175
+ describe '#resolve_variables' do
176
+ let(:manifest) { double(AppArchetype::Template::Manifest) }
177
+ let(:variable_manager) { double(AppArchetype::Template::VariableManager) }
178
+ let(:variable) { double(AppArchetype::Template::Variable) }
179
+ let(:variables) { [variable, variable] }
180
+ let(:prompt_response) { 'some-value' }
181
+
182
+ before do
183
+ allow(manifest).to receive(:variables).and_return(variable_manager)
184
+ allow(variable_manager).to receive(:all).and_return(variables)
185
+ allow(subject).to receive(:variable_prompt_for).and_return(prompt_response)
186
+ allow(variable).to receive(:set!)
187
+
188
+ subject.resolve_variables(manifest)
189
+ end
190
+
191
+ it 'executes the variable prompt for each variable' do
192
+ expect(subject)
193
+ .to have_received(:variable_prompt_for)
194
+ .with(variable)
195
+ .twice
196
+ end
197
+
198
+ it 'sets the variable with the prompt result' do
199
+ expect(variable)
200
+ .to have_received(:set!)
201
+ .with(prompt_response)
202
+ .twice
203
+ end
204
+ end
205
+
206
+ describe '#render_template' do
207
+ let(:manifest) { double(AppArchetype::Template::Manifest) }
208
+ let(:template) { double(AppArchetype::Template) }
209
+ let(:variables) { double(AppArchetype::Template::VariableManager) }
210
+ let(:plan) { double(AppArchetype::Template::Plan) }
211
+
212
+ before do
213
+ allow(manifest).to receive(:variables)
214
+ .and_return(variables)
215
+
216
+ allow(plan).to receive(:devise)
217
+ allow(plan).to receive(:execute)
218
+
219
+ allow(AppArchetype::Template::Plan)
220
+ .to receive(:new)
221
+ .and_return(plan)
222
+
223
+ subject.render_template(
224
+ manifest,
225
+ template
226
+ )
227
+ end
228
+
229
+ it 'constructs a plan' do
230
+ expect(AppArchetype::Template::Plan)
231
+ .to have_received(:new)
232
+ .with(template, variables, destination_path: destination_path, overwrite: false)
233
+ end
234
+
235
+ it 'devises the plan' do
236
+ expect(plan).to have_received(:devise)
237
+ end
238
+
239
+ it 'executes the plan' do
240
+ expect(plan).to have_received(:execute)
241
+ end
242
+ end
243
+
244
+ describe '#variable_prompt_for' do
245
+ let(:bool_prompt_resp) { true }
246
+ let(:int_prompt_resp) { 3 }
247
+ let(:str_prompt_resp) { 'resp' }
248
+
249
+ before do
250
+ allow(subject)
251
+ .to receive(:boolean_variable_prompt_for)
252
+ .and_return(bool_prompt_resp)
253
+
254
+ allow(subject)
255
+ .to receive(:integer_variable_prompt_for)
256
+ .and_return(int_prompt_resp)
257
+
258
+ allow(subject)
259
+ .to receive(:string_variable_prompt_for)
260
+ .and_return(str_prompt_resp)
261
+ end
262
+
263
+ context 'when variable is set' do
264
+ let(:set_variable) do
265
+ AppArchetype::Template::Variable.new(
266
+ 'string',
267
+ {
268
+ name: 'string',
269
+ description: 'a string',
270
+ value: 'already-set',
271
+ type: 'string'
272
+ }
273
+ )
274
+ end
275
+
276
+ before do
277
+ @resp = subject.variable_prompt_for(set_variable)
278
+ end
279
+
280
+ it 'returns set value' do
281
+ expect(@resp).to eq 'already-set'
282
+ end
283
+ end
284
+
285
+ context 'when variable is a boolean type' do
286
+ let(:variable) do
287
+ AppArchetype::Template::Variable.new(
288
+ 'boolean',
289
+ {
290
+ name: 'bool',
291
+ description: 'a boolean',
292
+ default: false,
293
+ type: 'boolean'
294
+ }
295
+ )
296
+ end
297
+
298
+ before do
299
+ @resp = subject.variable_prompt_for(variable)
300
+ end
301
+
302
+ it 'returns a boolean prompt for the variable' do
303
+ expect(subject).to have_received(:boolean_variable_prompt_for).with(variable)
304
+ end
305
+
306
+ it 'returns prompt response' do
307
+ expect(@resp).to eq bool_prompt_resp
308
+ end
309
+ end
310
+
311
+ context 'when variable is integer type' do
312
+ let(:variable) do
313
+ AppArchetype::Template::Variable.new(
314
+ 'integer',
315
+ {
316
+ name: 'int',
317
+ description: 'a integer',
318
+ default: 2,
319
+ type: 'integer'
320
+ }
321
+ )
322
+ end
323
+
324
+ before do
325
+ @resp = subject.variable_prompt_for(variable)
326
+ end
327
+
328
+ it 'returns a integer prompt for the variable' do
329
+ expect(subject).to have_received(:integer_variable_prompt_for).with(variable)
330
+ end
331
+
332
+ it 'returns prompt response' do
333
+ expect(@resp).to eq int_prompt_resp
334
+ end
335
+ end
336
+
337
+ context 'when variable is string type' do
338
+ let(:variable) do
339
+ AppArchetype::Template::Variable.new(
340
+ 'string',
341
+ {
342
+ name: 'string',
343
+ description: 'a string',
344
+ default: false,
345
+ type: 'string'
346
+ }
347
+ )
348
+ end
349
+
350
+ before do
351
+ @resp = subject.variable_prompt_for(variable)
352
+ end
353
+
354
+ it 'returns a string prompt for the variable' do
355
+ expect(subject).to have_received(:string_variable_prompt_for).with(variable)
356
+ end
357
+
358
+ it 'returns prompt response' do
359
+ expect(@resp).to eq str_prompt_resp
360
+ end
361
+ end
362
+ end
363
+
364
+ describe '#boolean_variable_prompt_for' do
365
+ let(:variable_name) { 'some-boolean' }
366
+ let(:variable_description) { 'A test boolean' }
367
+ let(:variable_default) { true }
368
+
369
+ let(:variable_spec) do
370
+ {
371
+ name: variable_name,
372
+ description: variable_description,
373
+ default: variable_default
374
+ }
375
+ end
376
+
377
+ let(:variable) do
378
+ AppArchetype::Template::Variable.new(
379
+ variable_name,
380
+ variable_spec
381
+ )
382
+ end
383
+
384
+ before do
385
+ subject.boolean_variable_prompt_for(variable)
386
+ end
387
+
388
+ it 'prints boolean prompt message' do
389
+ expect(subject).to have_received(:puts)
390
+ .with("• #{variable_name} (#{variable_description})")
391
+ end
392
+
393
+ it 'asks for boolean value for variable' do
394
+ expect(prompt).to have_received(:yes?)
395
+ .with(
396
+ "Enter value for `#{variable_name}` variable:",
397
+ default: variable_default
398
+ )
399
+ end
400
+ end
401
+
402
+ describe '#integer_variable_prompt_for' do
403
+ let(:variable_name) { 'some-integer' }
404
+ let(:variable_description) { 'A test integer' }
405
+ let(:variable_default) { 1 }
406
+
407
+ let(:variable_spec) do
408
+ {
409
+ name: variable_name,
410
+ description: variable_description,
411
+ default: variable_default
412
+ }
413
+ end
414
+
415
+ let(:variable) do
416
+ AppArchetype::Template::Variable.new(
417
+ variable_name,
418
+ variable_spec
419
+ )
420
+ end
421
+
422
+ before do
423
+ subject.integer_variable_prompt_for(variable)
424
+ end
425
+
426
+ it 'prints integer prompt message' do
427
+ expect(subject).to have_received(:puts)
428
+ .with("• #{variable_name} (#{variable_description})")
429
+ end
430
+
431
+ it 'asks for integer value for variable' do
432
+ expect(prompt)
433
+ .to have_received(:ask)
434
+ .with(
435
+ "Enter value for `#{variable_name}` variable:",
436
+ convert: :int,
437
+ default: variable_default
438
+ )
439
+ end
440
+ end
441
+
442
+ describe '#string_variable_prompt_for' do
443
+ let(:variable_name) { 'some-string' }
444
+ let(:variable_description) { 'A test string' }
445
+ let(:variable_default) { 'default string' }
446
+
447
+ let(:variable_spec) do
448
+ {
449
+ name: variable_name,
450
+ description: variable_description,
451
+ default: variable_default
452
+ }
453
+ end
454
+
455
+ let(:variable) do
456
+ AppArchetype::Template::Variable.new(
457
+ variable_name,
458
+ variable_spec
459
+ )
460
+ end
461
+
462
+ before do
463
+ subject.string_variable_prompt_for(variable)
464
+ end
465
+
466
+ it 'prints string prompt message' do
467
+ expect(subject).to have_received(:puts)
468
+ .with("• #{variable_name} (#{variable_description})")
469
+ end
470
+
471
+ it 'asks for string value for message' do
472
+ expect(prompt).to have_received(:ask)
473
+ .with(
474
+ "Enter value for `#{variable_name}` variable:",
475
+ default: variable_default
476
+ )
477
+ end
478
+ end
479
+ end
@@ -101,7 +101,7 @@ RSpec.describe AppArchetype::Generators do
101
101
  it 'makes template dir' do
102
102
  expect(FileUtils)
103
103
  .to have_received(:mkdir_p)
104
- .with(File.join(templates_path, name))
104
+ .with(File.join(templates_path, 'template'))
105
105
  end
106
106
 
107
107
  it 'renders blank manifest' do
@@ -27,12 +27,21 @@ RSpec.describe AppArchetype::Template::Manifest do
27
27
  }
28
28
  }
29
29
  end
30
+
31
+ let(:next_steps) do
32
+ [
33
+ 'do something',
34
+ 'do something else'
35
+ ]
36
+ end
37
+
30
38
  let(:content) do
31
39
  {
32
40
  'name' => manifest_name,
33
41
  'version' => version,
34
42
  'metadata' => app_archetype_meta,
35
- 'variables' => vars
43
+ 'variables' => vars,
44
+ 'next_steps' => next_steps
36
45
  }.to_json
37
46
  end
38
47
 
@@ -67,6 +76,27 @@ RSpec.describe AppArchetype::Template::Manifest do
67
76
  expect(@parsed.variables)
68
77
  .to be_a AppArchetype::Template::VariableManager
69
78
  end
79
+
80
+ it 'has next steps' do
81
+ expect(@parsed.next_steps)
82
+ .to eq next_steps
83
+ end
84
+
85
+ context 'when next steps is not defined' do
86
+ let(:content) do
87
+ {
88
+ 'name' => manifest_name,
89
+ 'version' => version,
90
+ 'metadata' => app_archetype_meta,
91
+ 'variables' => vars
92
+ }.to_json
93
+ end
94
+
95
+ it 'returns empty array for next steps' do
96
+ expect(@parsed.next_steps)
97
+ .to eq []
98
+ end
99
+ end
70
100
  end
71
101
 
72
102
  context 'when manifest is from a later version of app archetype' do
@@ -218,4 +218,36 @@ RSpec.describe AppArchetype::TemplateManager do
218
218
  end
219
219
  end
220
220
  end
221
+
222
+ describe '#manifest_names' do
223
+ let(:manifest) do
224
+ AppArchetype::Template::Manifest.new(
225
+ 'path/to/manifest.json',
226
+ 'name' => 'manifest'
227
+ )
228
+ end
229
+
230
+ let(:manifests) do
231
+ [
232
+ manifest,
233
+ manifest,
234
+ manifest
235
+ ]
236
+ end
237
+
238
+ before do
239
+ subject.instance_variable_set(:@manifests, manifests)
240
+ @result = subject.manifest_names
241
+ end
242
+
243
+ it 'returns array of manifest names' do
244
+ expect(@result).to eq(
245
+ %w[
246
+ manifest
247
+ manifest
248
+ manifest
249
+ ]
250
+ )
251
+ end
252
+ end
221
253
  end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe AppArchetype do
4
+ let(:collection_dir) { 'path/to/collection' }
5
+ let(:template_name) { 'template' }
6
+ let(:destination_path) { 'path/to/destination' }
7
+
8
+ describe '.render_template' do
9
+ let(:manager) { double(AppArchetype::TemplateManager) }
10
+ let(:manifest) { double(AppArchetype::Template::Manifest) }
11
+ let(:template) { double(AppArchetype::Template) }
12
+ let(:command) { double(AppArchetype::Commands::RenderTemplate) }
13
+ let(:options) do
14
+ Hashie::Mash.new(
15
+ name: template_name,
16
+ overwrite: false
17
+ )
18
+ end
19
+
20
+ before do
21
+ allow(AppArchetype::TemplateManager)
22
+ .to receive(:new)
23
+ .and_return(manager)
24
+
25
+ allow(manager).to receive(:load)
26
+ allow(manager).to receive(:find_by_name).and_return(manifest)
27
+ allow(manifest).to receive(:template).and_return(template)
28
+ allow(template).to receive(:load)
29
+
30
+ allow(AppArchetype::Commands::RenderTemplate)
31
+ .to receive(:new)
32
+ .and_return(command)
33
+
34
+ allow(command).to receive(:run)
35
+
36
+ @manifest = described_class.render_template(
37
+ collection_dir: collection_dir,
38
+ template_name: template_name,
39
+ destination_path: destination_path
40
+ )
41
+ end
42
+
43
+ it 'loads manager' do
44
+ expect(manager).to have_received(:load)
45
+ end
46
+
47
+ it 'finds template by name' do
48
+ expect(manager).to have_received(:find_by_name)
49
+ end
50
+
51
+ it 'loads template' do
52
+ expect(template).to have_received(:load)
53
+ end
54
+
55
+ it 'runs render command' do
56
+ expect(AppArchetype::Commands::RenderTemplate)
57
+ .to have_received(:new)
58
+ .with(manager, destination_path, options)
59
+ end
60
+
61
+ it 'returns manifest' do
62
+ expect(@manifest).to eq manifest
63
+ end
64
+ end
65
+ end