app_archetype 1.2.3

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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/.doxie.json +25 -0
  3. data/.github/workflows/build.yml +25 -0
  4. data/.gitignore +22 -0
  5. data/.rubocop.yml +35 -0
  6. data/.ruby-version +1 -0
  7. data/CONTRIBUTING.md +51 -0
  8. data/Gemfile +3 -0
  9. data/Gemfile.lock +172 -0
  10. data/LICENSE +21 -0
  11. data/README.md +138 -0
  12. data/Rakefile +19 -0
  13. data/app_archetype.gemspec +39 -0
  14. data/bin/archetype +20 -0
  15. data/lib/app_archetype.rb +14 -0
  16. data/lib/app_archetype/cli.rb +204 -0
  17. data/lib/app_archetype/cli/presenters.rb +106 -0
  18. data/lib/app_archetype/cli/prompts.rb +152 -0
  19. data/lib/app_archetype/generators.rb +95 -0
  20. data/lib/app_archetype/logger.rb +69 -0
  21. data/lib/app_archetype/renderer.rb +116 -0
  22. data/lib/app_archetype/template.rb +12 -0
  23. data/lib/app_archetype/template/helpers.rb +216 -0
  24. data/lib/app_archetype/template/manifest.rb +193 -0
  25. data/lib/app_archetype/template/plan.rb +172 -0
  26. data/lib/app_archetype/template/source.rb +39 -0
  27. data/lib/app_archetype/template/variable.rb +237 -0
  28. data/lib/app_archetype/template/variable_manager.rb +75 -0
  29. data/lib/app_archetype/template_manager.rb +113 -0
  30. data/lib/app_archetype/version.rb +6 -0
  31. data/lib/core_ext/string.rb +67 -0
  32. data/spec/app_archetype/cli/presenters_spec.rb +99 -0
  33. data/spec/app_archetype/cli/prompts_spec.rb +292 -0
  34. data/spec/app_archetype/cli_spec.rb +132 -0
  35. data/spec/app_archetype/generators_spec.rb +119 -0
  36. data/spec/app_archetype/logger_spec.rb +86 -0
  37. data/spec/app_archetype/renderer_spec.rb +291 -0
  38. data/spec/app_archetype/template/helpers_spec.rb +251 -0
  39. data/spec/app_archetype/template/manifest_spec.rb +245 -0
  40. data/spec/app_archetype/template/plan_spec.rb +191 -0
  41. data/spec/app_archetype/template/source_spec.rb +60 -0
  42. data/spec/app_archetype/template/variable_manager_spec.rb +103 -0
  43. data/spec/app_archetype/template/variable_spec.rb +245 -0
  44. data/spec/app_archetype/template_manager_spec.rb +221 -0
  45. data/spec/core_ext/string_spec.rb +143 -0
  46. data/spec/spec_helper.rb +29 -0
  47. metadata +370 -0
@@ -0,0 +1,191 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe AppArchetype::Template::Plan do
4
+ let(:template) { AppArchetype::Template::Source.new('path/to/template') }
5
+ let(:destination) { 'path/to/destination' }
6
+ let(:variables) do
7
+ OpenStruct.new(
8
+ foo: 'bar'
9
+ )
10
+ end
11
+
12
+ subject do
13
+ described_class.new(
14
+ template,
15
+ variables,
16
+ destination_path: destination
17
+ )
18
+ end
19
+
20
+ describe '#devise' do
21
+ let(:dest_exist) { true }
22
+
23
+ before do
24
+ allow(subject).to receive(:destination_exist?).and_return(dest_exist)
25
+ allow(template).to receive(:files).and_return(['path/to/file'])
26
+ end
27
+
28
+ it 'creates file objects' do
29
+ subject.devise
30
+ expect(subject.files).to all be_a(AppArchetype::Template::OutputFile)
31
+ end
32
+
33
+ context 'destination path does not exist' do
34
+ let(:dest_exist) { false }
35
+
36
+ it 'raises error' do
37
+ expect do
38
+ subject.devise
39
+ end.to raise_error('destination path does not exist')
40
+ end
41
+ end
42
+ end
43
+
44
+ describe '#execute' do
45
+ let(:renderer) { double(AppArchetype::Renderer) }
46
+
47
+ before do
48
+ allow(renderer).to receive(:render)
49
+ allow(AppArchetype::Renderer)
50
+ .to receive(:new)
51
+ .and_return(renderer)
52
+
53
+ subject.execute
54
+ end
55
+
56
+ it 'creates a renderer' do
57
+ expect(AppArchetype::Renderer)
58
+ .to have_received(:new).with(subject, false)
59
+ end
60
+
61
+ it 'renders plan' do
62
+ expect(renderer).to have_received(:render)
63
+ end
64
+ end
65
+
66
+ describe '#destination_exist?' do
67
+ let(:dirname) { 'some-directory' }
68
+ let(:exist) { true }
69
+
70
+ before do
71
+ allow(File).to receive(:exist?).and_return(exist)
72
+ allow(File).to receive(:dirname).and_return(dirname)
73
+ end
74
+
75
+ it 'returns true' do
76
+ expect(subject.destination_exist?).to be true
77
+ end
78
+
79
+ context 'when destination does not exist' do
80
+ let(:exist) { false }
81
+
82
+ it 'returns false' do
83
+ expect(subject.destination_exist?).to be false
84
+ end
85
+ end
86
+ end
87
+
88
+ describe '#render_dest_file_path' do
89
+ it 'creates a destination file path from source path' do
90
+ expect(
91
+ subject.render_dest_file_path('path/to/template/file')
92
+ ).to eq 'path/to/destination/file'
93
+ end
94
+ end
95
+
96
+ describe '#render_path' do
97
+ it 'renders path with variables' do
98
+ expect(subject.render_path('{{foo}}')).to eq 'bar'
99
+ end
100
+ end
101
+ end
102
+
103
+ RSpec.describe AppArchetype::Template::OutputFile do
104
+ let(:source_file) { 'path/to/source' }
105
+ let(:dest_file) { 'path/to/dest' }
106
+
107
+ subject { described_class.new(source_file, dest_file) }
108
+
109
+ describe '#source_directory?' do
110
+ before do
111
+ allow(File).to receive(:directory?)
112
+ subject.source_directory?
113
+ end
114
+
115
+ it 'delegates source file directory check to File' do
116
+ expect(File).to have_received(:directory?).with(source_file)
117
+ end
118
+ end
119
+
120
+ describe '#source_erb?' do
121
+ let(:ext) { '.erb' }
122
+
123
+ before do
124
+ allow(File).to receive(:extname).and_return(ext)
125
+ @result = subject.source_erb?
126
+ end
127
+
128
+ it 'delegates source file template check to File' do
129
+ expect(File).to have_received(:extname).with(source_file)
130
+ end
131
+
132
+ it 'returns true if file is erb' do
133
+ expect(@result).to be true
134
+ end
135
+
136
+ context 'when not erb' do
137
+ let(:ext) { 'doc' }
138
+
139
+ it 'returns false' do
140
+ expect(@result).to be false
141
+ end
142
+ end
143
+ end
144
+
145
+ describe '#source_hbs?' do
146
+ let(:ext) { '.hbs' }
147
+
148
+ before do
149
+ allow(File).to receive(:extname).and_return(ext)
150
+ @result = subject.source_hbs?
151
+ end
152
+
153
+ it 'delegates source file template check to File' do
154
+ expect(File).to have_received(:extname).with(source_file)
155
+ end
156
+
157
+ it 'returns true if file is hbs' do
158
+ expect(@result).to be true
159
+ end
160
+
161
+ context 'when not hbs' do
162
+ let(:ext) { 'doc' }
163
+
164
+ it 'returns false' do
165
+ expect(@result).to be false
166
+ end
167
+ end
168
+ end
169
+
170
+ describe '#source_file?' do
171
+ before do
172
+ allow(File).to receive(:file?)
173
+ subject.source_file?
174
+ end
175
+
176
+ it 'delegates source file check to File' do
177
+ expect(File).to have_received(:file?).with(source_file)
178
+ end
179
+ end
180
+
181
+ describe '#exist?' do
182
+ before do
183
+ allow(File).to receive(:exist?)
184
+ subject.exist?
185
+ end
186
+
187
+ it 'delegates exist check to File' do
188
+ expect(File).to have_received(:exist?).with(dest_file)
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe AppArchetype::Template::Source do
4
+ let(:path) { 'path/to/source' }
5
+ subject { described_class.new(path) }
6
+
7
+ describe '#load' do
8
+ let(:glob_files) { %w[file1 file2] }
9
+ let(:exist) { true }
10
+
11
+ before do
12
+ allow(Dir).to receive(:glob).and_return(glob_files)
13
+ allow(subject).to receive(:exist?).and_return(exist)
14
+ end
15
+
16
+ context 'when source exists' do
17
+ before do
18
+ subject.load
19
+ end
20
+
21
+ it 'loads expected file paths into source_files' do
22
+ expect(subject.files).to eq glob_files
23
+ end
24
+ end
25
+
26
+ context 'when source does not exist' do
27
+ let(:exist) { false }
28
+
29
+ it 'raises template source does not exist error' do
30
+ expect do
31
+ subject.load
32
+ end.to raise_error('template source does not exist')
33
+ end
34
+ end
35
+ end
36
+
37
+ describe '#exist?' do
38
+ let(:path) { 'path/to/source' }
39
+ let(:exist) { true }
40
+
41
+ before { allow(File).to receive(:exist?).and_return(exist) }
42
+
43
+ it 'checks if file exists' do
44
+ expect(File).to receive(:exist?).with(path)
45
+ subject.exist?
46
+ end
47
+
48
+ it 'returns true' do
49
+ expect(subject.exist?).to be true
50
+ end
51
+
52
+ context 'when file does not exist' do
53
+ let(:exist) { false }
54
+
55
+ it 'returns false' do
56
+ expect(subject.exist?).to be false
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe AppArchetype::Template::VariableManager do
4
+ let(:var_name) { 'example_string' }
5
+ let(:var_type) { 'string' }
6
+ let(:var_desc) { 'This is an example string variable' }
7
+ let(:var_default) { 'default value' }
8
+ let(:var_value) { 'set value' }
9
+
10
+ let(:variables_spec) do
11
+ {
12
+ var_name => {
13
+ 'type' => var_type,
14
+ 'description' => var_desc,
15
+ 'default' => var_default
16
+ },
17
+ "#{var_name}_with_value" => {
18
+ 'type' => var_type,
19
+ 'description' => var_desc,
20
+ 'default' => var_default,
21
+ 'value' => var_value
22
+ },
23
+ 'example_random_string' => {
24
+ 'type' => 'string',
25
+ 'description' => 'Example call to helper to generate 25 char string',
26
+ 'value' => '#random_string,25'
27
+ }
28
+ }
29
+ end
30
+
31
+ subject { described_class.new(variables_spec) }
32
+
33
+ describe '#all' do
34
+ it 'returns array of template variables' do
35
+ expect(subject.all).to all(be_a AppArchetype::Template::Variable)
36
+ end
37
+
38
+ it 'has expected variables' do
39
+ expect(subject.all.map(&:name))
40
+ .to eq(
41
+ %w[
42
+ example_string
43
+ example_string_with_value
44
+ example_random_string
45
+ ]
46
+ )
47
+ end
48
+ end
49
+
50
+ describe '#get' do
51
+ before { @var = subject.get(var_name) }
52
+
53
+ it 'returns variable' do
54
+ expect(@var.name).to eq var_name
55
+ expect(@var.type).to eq var_type
56
+ expect(@var.description).to eq var_desc
57
+ expect(@var.default).to eq var_default
58
+ end
59
+ end
60
+
61
+ describe '#to_h' do
62
+ let(:instance_helpers) { double }
63
+ let(:random_result) { 'random-string-25-chars-long' }
64
+
65
+ before do
66
+ allow_any_instance_of(AppArchetype::Template::Variable)
67
+ .to receive(:helpers)
68
+ .and_return(instance_helpers)
69
+
70
+ allow(instance_helpers)
71
+ .to receive(:random_string)
72
+ .and_return(random_result)
73
+ end
74
+
75
+ it 'returns hash representation of variables (key => value)' do
76
+ expect(subject.to_h).to eq(
77
+ var_name => var_default,
78
+ "#{var_name}_with_value" => var_value,
79
+ 'example_random_string' => random_result
80
+ )
81
+ end
82
+ end
83
+
84
+ describe '#method_missing' do
85
+ context 'when variable is defined without value' do
86
+ it 'returns default variable value' do
87
+ expect(subject.example_string).to eq var_default
88
+ end
89
+ end
90
+
91
+ context 'when variable is defined with a value' do
92
+ it 'returns set variable value' do
93
+ expect(subject.example_string_with_value).to eq var_value
94
+ end
95
+ end
96
+
97
+ context 'when variable is not defined' do
98
+ it 'raises NoMethodError' do
99
+ expect { subject.foo }.to raise_error(NoMethodError)
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,245 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe AppArchetype::Template::Variable do
4
+ let(:description) { 'explanation of variable' }
5
+ let(:type) { 'string' }
6
+ let(:default) { 'default-value' }
7
+ let(:value) { nil }
8
+
9
+ let(:name) { 'var' }
10
+ let(:spec) do
11
+ {
12
+ 'description' => description,
13
+ 'type' => type,
14
+ 'default' => default,
15
+ 'value' => value
16
+ }
17
+ end
18
+
19
+ subject { described_class.new(name, spec) }
20
+
21
+ describe '::STRING_VALIDATOR' do
22
+ it 'returns true when given string input' do
23
+ expect(
24
+ described_class::STRING_VALIDATOR.call('this is string')
25
+ ).to be true
26
+ end
27
+
28
+ it 'returns false when given non string input' do
29
+ expect(
30
+ described_class::STRING_VALIDATOR.call(1)
31
+ ).to be false
32
+ end
33
+ end
34
+
35
+ describe '::BOOLEAN_VALIDATOR' do
36
+ it 'returns true when given boolean input' do
37
+ expect(
38
+ described_class::BOOLEAN_VALIDATOR.call(true)
39
+ ).to be true
40
+ end
41
+
42
+ it 'returns true when given string representation of boolean input' do
43
+ expect(
44
+ described_class::BOOLEAN_VALIDATOR.call('true')
45
+ ).to be true
46
+ end
47
+
48
+ it 'returns false when given non boolean input' do
49
+ expect(
50
+ described_class::BOOLEAN_VALIDATOR.call('not bool')
51
+ ).to be false
52
+ end
53
+ end
54
+
55
+ describe '::INTEGER_VALIDATOR' do
56
+ it 'returns true when given integer input' do
57
+ expect(
58
+ described_class::INTEGER_VALIDATOR.call(1)
59
+ ).to be true
60
+ end
61
+
62
+ it 'returns true when given string representation of integer' do
63
+ expect(
64
+ described_class::INTEGER_VALIDATOR.call('1')
65
+ ).to be true
66
+ end
67
+
68
+ it 'returns false when given non integer input' do
69
+ expect(
70
+ described_class::INTEGER_VALIDATOR.call('one')
71
+ ).to be false
72
+ end
73
+ end
74
+
75
+ describe '#set!' do
76
+ context 'setting with a valid value' do
77
+ before { subject.set!('a value') }
78
+
79
+ it 'sets the value of the variable' do
80
+ expect(subject.value).to eq 'a value'
81
+ end
82
+ end
83
+
84
+ context 'setting with an invalid value' do
85
+ let(:type) { 'integer' }
86
+
87
+ it 'raises invalid value runtime error' do
88
+ expect do
89
+ subject.set!('a string')
90
+ end.to raise_error('invalid value')
91
+ end
92
+ end
93
+ end
94
+
95
+ describe '#default' do
96
+ context 'when default is in specification' do
97
+ it 'returns specified default value' do
98
+ expect(subject.default).to eq default
99
+ end
100
+ end
101
+
102
+ context 'when default is not specified' do
103
+ let(:spec) do
104
+ {
105
+ 'type' => 'integer'
106
+ }
107
+ end
108
+
109
+ it 'returns archetype default' do
110
+ expect(subject.default).to eq 0
111
+ end
112
+ end
113
+ end
114
+
115
+ describe '#description' do
116
+ context 'when description is in specification' do
117
+ it 'returns specified description value' do
118
+ expect(subject.description).to eq description
119
+ end
120
+ end
121
+
122
+ context 'when default is not specified' do
123
+ let(:spec) do
124
+ {
125
+ 'type' => 'integer'
126
+ }
127
+ end
128
+
129
+ it 'returns empty string' do
130
+ expect(subject.description).to eq ''
131
+ end
132
+ end
133
+ end
134
+
135
+ describe '#type' do
136
+ context 'when type is in specification' do
137
+ it 'returns specified type value' do
138
+ expect(subject.type).to eq type
139
+ end
140
+ end
141
+
142
+ context 'when default is not specified' do
143
+ let(:spec) do
144
+ {}
145
+ end
146
+
147
+ it 'returns string by default' do
148
+ expect(subject.type).to eq 'string'
149
+ end
150
+ end
151
+ end
152
+
153
+ describe '#value' do
154
+ context 'when value is not specified' do
155
+ it 'returns default value' do
156
+ expect(subject.value).to eq default
157
+ end
158
+ end
159
+
160
+ context 'when value is a function call' do
161
+ let(:helper) { double }
162
+ let(:value) { '#upcase,foo' }
163
+
164
+ it 'returns result of function call' do
165
+ expect(subject.value).to eq 'FOO'
166
+ end
167
+ end
168
+
169
+ context 'when value has been set' do
170
+ let(:value) { 'some-value' }
171
+
172
+ it 'returns variable value' do
173
+ expect(subject.value).to eq value
174
+ end
175
+ end
176
+ end
177
+
178
+ describe '#value?' do
179
+ context 'when value is not set' do
180
+ let(:value) { nil }
181
+
182
+ it 'returns false' do
183
+ expect(subject.value?).to be false
184
+ end
185
+ end
186
+
187
+ let(:value) { 'some-value' }
188
+
189
+ context 'when value is set' do
190
+ it 'returns true' do
191
+ expect(subject.value?).to be true
192
+ end
193
+ end
194
+ end
195
+
196
+ describe '#validator' do
197
+ context 'when variable type is specified' do
198
+ let(:type) { 'integer' }
199
+
200
+ it 'returns validator for specified type' do
201
+ expect(subject.validator)
202
+ .to eq AppArchetype::Template::Variable::INTEGER_VALIDATOR
203
+ end
204
+ end
205
+
206
+ context 'when variable type is not specified' do
207
+ let(:type) { nil }
208
+
209
+ it 'returns string validator' do
210
+ expect(subject.validator)
211
+ .to eq AppArchetype::Template::Variable::STRING_VALIDATOR
212
+ end
213
+ end
214
+ end
215
+
216
+ describe '#valid?' do
217
+ context 'when validator returns true' do
218
+ let(:result) { true }
219
+ let(:validator) { double(call: result) }
220
+
221
+ before do
222
+ allow(subject).to receive(:validator)
223
+ .and_return(validator)
224
+ end
225
+
226
+ it 'returns true' do
227
+ expect(subject.valid?('input')).to be true
228
+ end
229
+ end
230
+
231
+ context 'when validator returns false' do
232
+ let(:result) { false }
233
+ let(:validator) { double(call: result) }
234
+
235
+ before do
236
+ allow(subject).to receive(:validator)
237
+ .and_return(validator)
238
+ end
239
+
240
+ it 'returns false' do
241
+ expect(subject.valid?('input')).to be false
242
+ end
243
+ end
244
+ end
245
+ end