app_archetype 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
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