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
@@ -3,15 +3,181 @@ require 'thor'
3
3
  require 'highline'
4
4
 
5
5
  require 'app_archetype'
6
- require 'app_archetype/cli/presenters'
7
- require 'app_archetype/cli/prompts'
8
6
 
9
7
  module AppArchetype
10
8
  # Command line interface helpers and actions
11
9
  class CLI < ::Thor
12
- package_name 'Archetype'
10
+ package_name 'AppArchetype'
13
11
 
14
- class <<self
12
+ desc 'version', 'Print app archetype version to STDOUT'
13
+
14
+ ##
15
+ # Runs version command
16
+ #
17
+ def version
18
+ cmd = AppArchetype::Commands::PrintVersion.new(options)
19
+ cmd.run
20
+ end
21
+
22
+ map %w[--version -v] => :version
23
+
24
+ desc 'list', 'Prints a list of known templates to STDOUT'
25
+
26
+ ##
27
+ # Runs list templates command
28
+ #
29
+ def list
30
+ cmd = AppArchetype::Commands::ListTemplates.new(
31
+ manager,
32
+ options
33
+ )
34
+ cmd.run
35
+ end
36
+
37
+ desc 'path', 'Prints template path to STDOUT'
38
+
39
+ ##
40
+ # Runs print path command
41
+ #
42
+ def path
43
+ cmd = AppArchetype::Commands::PrintPath.new(
44
+ template_dir,
45
+ options
46
+ )
47
+ cmd.run
48
+ end
49
+
50
+ desc 'open', 'Opens template manifest file'
51
+
52
+ method_option(
53
+ :name,
54
+ type: :string,
55
+ desc: 'Name of manifest'
56
+ )
57
+
58
+ ##
59
+ # Runs open manifest command
60
+ #
61
+ def open
62
+ cmd = AppArchetype::Commands::OpenManifest.new(
63
+ manager,
64
+ editor,
65
+ options
66
+ )
67
+ cmd.run
68
+ end
69
+
70
+ desc 'new', 'Creates a template in ARCHETYPE_TEMPLATE_DIR'
71
+
72
+ method_option(
73
+ :name,
74
+ type: :string,
75
+ desc: 'Name of template'
76
+ )
77
+
78
+ ##
79
+ # Runs new template commmand
80
+ #
81
+ def new
82
+ cmd = AppArchetype::Commands::NewTemplate.new(
83
+ template_dir,
84
+ options
85
+ )
86
+ cmd.run
87
+ end
88
+
89
+ desc 'delete', 'Deletes a template in ARCHETYPE_TEMPLATE_DIR'
90
+
91
+ method_option(
92
+ :name,
93
+ type: :string,
94
+ desc: 'Name of template'
95
+ )
96
+
97
+ ##
98
+ # Runs delete template command
99
+ #
100
+ def delete
101
+ cmd = AppArchetype::Commands::DeleteTemplate.new(
102
+ manager,
103
+ options
104
+ )
105
+ cmd.run
106
+ end
107
+
108
+ desc 'variables', 'Prints template variables'
109
+
110
+ method_option(
111
+ :name,
112
+ type: :string,
113
+ desc: 'Name of template'
114
+ )
115
+
116
+ ##
117
+ # Runs print template variables command
118
+ #
119
+ def variables
120
+ cmd = AppArchetype::Commands::PrintTemplateVariables.new(
121
+ manager,
122
+ options
123
+ )
124
+ cmd.run
125
+ end
126
+
127
+ desc 'find', 'Finds a template in collection'
128
+
129
+ method_option(
130
+ :name,
131
+ type: :string,
132
+ desc: 'Name of template'
133
+ )
134
+
135
+ ##
136
+ # Runs find templates command
137
+ #
138
+ def find
139
+ cmd = AppArchetype::Commands::FindTemplates.new(
140
+ manager,
141
+ options
142
+ )
143
+ cmd.run
144
+ end
145
+
146
+ desc 'render', 'Renders project template'
147
+
148
+ method_option(
149
+ :name,
150
+ type: :string,
151
+ desc: 'Name of template'
152
+ )
153
+
154
+ method_option(
155
+ :out,
156
+ type: :string,
157
+ desc: 'Destination path for rendered template',
158
+ default: FileUtils.pwd
159
+ )
160
+
161
+ method_option(
162
+ :overwrite,
163
+ type: :boolean,
164
+ default: false,
165
+ desc: 'Option to overwrite any existing files'
166
+ )
167
+
168
+ ##
169
+ # Runs render template command
170
+ #
171
+ def render
172
+ cmd = AppArchetype::Commands::RenderTemplate.new(
173
+ manager,
174
+ options.out,
175
+ options
176
+ )
177
+ cmd.run
178
+ end
179
+
180
+ no_commands do
15
181
  ##
16
182
  # Retrieves template dir from environment and raises error
17
183
  # when TEMPLATE_DIR environment variable is not set.
@@ -45,7 +211,7 @@ module AppArchetype
45
211
 
46
212
  `which #{@editor}`
47
213
  if $?.exitstatus != 0
48
- CLI.print_warning(
214
+ puts(
49
215
  "WARN: Configured editor #{@editor} is not installed correctly "\
50
216
  'please check your configuration'
51
217
  )
@@ -66,139 +232,5 @@ module AppArchetype
66
232
  @manager
67
233
  end
68
234
  end
69
-
70
- include AppArchetype::Logger
71
-
72
- def self.exit_on_failure?
73
- true
74
- end
75
-
76
- desc 'version', 'Prints archetype gem version'
77
- def version
78
- print_message(AppArchetype::VERSION)
79
- end
80
- map %w[--version -v] => :version
81
-
82
- desc 'list', 'Lists known templates in ARCHETYPE_TEMPLATE_DIR'
83
- def list
84
- print_message(
85
- Presenters.manifest_list(
86
- CLI.manager.manifests
87
- )
88
- )
89
- end
90
-
91
- desc 'path', 'Prints configured ARCHETYPE_TEMPLATE_DIR'
92
- def path
93
- print_message(
94
- CLI.template_dir
95
- )
96
- end
97
-
98
- desc 'open', 'Opens template manifest'
99
- def open(name)
100
- editor = CLI.editor
101
- manifest = CLI.manager.find_by_name(name)
102
-
103
- pid = Process.spawn("#{editor} #{manifest.path}")
104
- Process.waitpid(pid)
105
- end
106
-
107
- desc 'new', 'Creates a template in ARCHETYPE_TEMPLATE_DIR'
108
- def new(rel)
109
- raise 'template rel not provided' unless rel
110
-
111
- dest = File.join(CLI.template_dir, rel)
112
- FileUtils.mkdir_p(dest)
113
-
114
- name = File.basename(rel)
115
- AppArchetype::Generators.render_empty_template(name, dest)
116
-
117
- print_message("Template `#{name}` created at #{dest}")
118
- end
119
-
120
- desc 'delete', 'Deletes a template in ARCHETYPE_TEMPLATE_DIR'
121
- def delete(name)
122
- manifest = CLI.manager.find_by_name(name)
123
- raise 'Cannot find template' unless manifest
124
-
125
- proceed = Prompts.delete_template(manifest)
126
-
127
- return unless proceed
128
-
129
- FileUtils.rm_rf(manifest.parent_path)
130
- print_message("Template `#{manifest.name}` has been removed")
131
- end
132
-
133
- desc 'validate', 'Runs a schema validation on given template'
134
- def validate(name)
135
- manifest = CLI.manager.find_by_name(name)
136
- raise 'Cannot find template' unless manifest
137
-
138
- result = manifest.validate
139
-
140
- print_message("VALIDATION RESULTS FOR `#{name}`")
141
- if result.any?
142
- print_message(
143
- Presenters.validation_result(result)
144
- )
145
-
146
- raise "Manifest `#{name}` is not valid"
147
- end
148
-
149
- print_message("Manifest `#{name}` is valid") if result.empty?
150
- end
151
-
152
- desc 'variables', 'Prints template variables'
153
- def variables(search_term)
154
- result = CLI.manager.find_by_name(search_term)
155
- return print_message("Manifest `#{search_term}` not found") unless result
156
-
157
- print_message("VARIABLES FOR `#{search_term}`")
158
- print_message(
159
- Presenters.variable_list(result.variables.all)
160
- )
161
- end
162
-
163
- desc 'find', 'Finds a template in collection by name'
164
- def find(search_term)
165
- result = CLI.manager.find_by_name(search_term)
166
- return print_message("Manifest `#{search_term}` not found") unless result
167
-
168
- print_message("SEARCH RESULTS FOR `#{search_term}`")
169
- print_message(
170
- Presenters.manifest_list([result])
171
- )
172
- end
173
-
174
- desc 'render', 'Renders project template'
175
- method_option(
176
- :overwrite,
177
- type: :boolean,
178
- default: false,
179
- desc: 'Option to overwrite any existing files'
180
- )
181
- def render(manifest_name)
182
- manifest = CLI.manager.find_by_name(manifest_name)
183
-
184
- raise "Unable to find manifest `#{manifest_name}`" unless manifest
185
-
186
- template = manifest.template
187
- template.load
188
-
189
- manifest.variables.all.each do |var|
190
- value = Prompts.variable_prompt_for(var)
191
- var.set!(value)
192
- end
193
-
194
- plan = AppArchetype::Template::Plan.new(
195
- template,
196
- manifest.variables,
197
- destination_path: FileUtils.pwd,
198
- overwrite: options.overwrite
199
- )
200
- plan.devise
201
- plan.execute
202
- end
203
235
  end
204
236
  end
@@ -0,0 +1,58 @@
1
+ require 'tty-prompt'
2
+
3
+ module AppArchetype
4
+ module Commands
5
+ # Deletes template from collection
6
+ class DeleteTemplate
7
+ def initialize(manager, options = Hashie::Mash.new)
8
+ @manager = manager
9
+ @options = options
10
+ @prompt = TTY::Prompt.new
11
+ end
12
+
13
+ ##
14
+ # Deletes a manifest from template dir
15
+ #
16
+ # First it looks in options for the name of the
17
+ # manifest to delete. If this is not set then the user
18
+ # is presented a list of manifests to choose from.
19
+ #
20
+ # When the manifest specified does not exist
21
+ # a runtime error will be raised
22
+ #
23
+ # The user will then be prompted with a yes/no prompt
24
+ # to confirm they wish to delete the selected manifest.
25
+ #
26
+ # If the answer to this is no, the command will stop
27
+ #
28
+ # Otherwise, the template and manifest will be removed
29
+ # from the template dir
30
+ #
31
+ # A success message will be presented to confirm to the
32
+ # user that the operation was successful.
33
+ #
34
+ def run
35
+ name = @options.name
36
+ name ||= @prompt.select(
37
+ 'Please choose template for deletion',
38
+ @manager.manifest_names
39
+ )
40
+
41
+ manifest = @manager.find_by_name(name)
42
+
43
+ unless manifest
44
+ puts "✖ No template with name `#{name}` found."
45
+ return
46
+ end
47
+
48
+ ok_to_proceed = @prompt.yes?("Are you sure you want to delete #{name}?")
49
+
50
+ return unless ok_to_proceed
51
+
52
+ FileUtils.rm_rf(manifest.parent_path)
53
+
54
+ puts("✔ Template `#{name}` has been removed")
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,66 @@
1
+ require 'tty-prompt'
2
+
3
+ module AppArchetype
4
+ module Commands
5
+ # Finds template by name in collection
6
+ class FindTemplates
7
+ ##
8
+ # Output table header
9
+ #
10
+ RESULT_HEADER = %w[NAME VERSION].freeze
11
+
12
+ def initialize(manager, options = Hashie::Mash.new)
13
+ @manager = manager
14
+ @options = options
15
+ @prompt = TTY::Prompt.new
16
+ end
17
+
18
+ ##
19
+ # Finds a template by name from the collection
20
+ #
21
+ # First it looks for a name option, if this is not
22
+ # set then the user is prompted for the template name.
23
+ #
24
+ # Then the template manager runs a search to find a
25
+ # manifest with a fully or partially matching name
26
+ #
27
+ # If there are found results they will be rendered
28
+ # to STDOUT in a table.
29
+ #
30
+ # When there are no results found then a message
31
+ # confirming this will be printed to STDOUT.
32
+ #
33
+ def run
34
+ name = @options.name
35
+ name ||= @prompt.ask('Please enter a template name')
36
+
37
+ manifests = @manager.search_by_name(name)
38
+
39
+ if manifests.any?
40
+ puts manifest_list_table(manifests).render&.strip
41
+ else
42
+ puts "✖ No manifests with name `#{name}` found."
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ ##
49
+ # Builds a table of manifest information
50
+ #
51
+ # @param [Array] manifests
52
+ #
53
+ def manifest_list_table(manifests)
54
+ TTY::Table.new(
55
+ header: RESULT_HEADER,
56
+ rows: manifests.map do |manifest|
57
+ [
58
+ manifest.name,
59
+ manifest.version
60
+ ]
61
+ end
62
+ )
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,49 @@
1
+ require 'tty-prompt'
2
+ require 'tty-table'
3
+
4
+ module AppArchetype
5
+ module Commands
6
+ # Lists known templates for user
7
+ class ListTemplates
8
+ ##
9
+ # Output table header
10
+ #
11
+ RESULT_HEADER = %w[NAME VERSION].freeze
12
+
13
+ def initialize(manager, options = Hashie::Mash.new)
14
+ @manager = manager
15
+ @options = options
16
+ @prompt = TTY::Prompt.new
17
+ end
18
+
19
+ ##
20
+ # Lists all known and valid manifests
21
+ #
22
+ # This renders a manifest list table
23
+ #
24
+ # Note: any invalid manifests will be excluded
25
+ # from this list.
26
+ #
27
+ def run
28
+ puts(manifest_list_table.render.strip)
29
+ end
30
+
31
+ private
32
+
33
+ ##
34
+ # Builds a table of manifest information
35
+ #
36
+ def manifest_list_table
37
+ TTY::Table.new(
38
+ header: RESULT_HEADER,
39
+ rows: @manager.manifests.map do |manifest|
40
+ [
41
+ manifest.name,
42
+ manifest.version
43
+ ]
44
+ end
45
+ )
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,42 @@
1
+ require 'tty-prompt'
2
+
3
+ module AppArchetype
4
+ module Commands
5
+ # Creates new blank template for a user
6
+ class NewTemplate
7
+ def initialize(template_dir, options = Hashie::Mash.new)
8
+ @template_dir = template_dir
9
+ @options = options
10
+ @prompt = TTY::Prompt.new
11
+ end
12
+
13
+ ##
14
+ # Renders a new empty template and manifest
15
+ #
16
+ # First it looks to the name option for a relative
17
+ # path from the template dir
18
+ #
19
+ # If this is not provided then the user is prompted
20
+ # for input.
21
+ #
22
+ # Once we have a name, the destination folder is created
23
+ # and the generator renders an empty template there.
24
+ #
25
+ # If the operation is successful then a success message
26
+ # is printed to STDOUT confirming this.
27
+ #
28
+ def run
29
+ rel = @options.name
30
+ rel ||= @prompt.ask('Please enter a name for the new template')
31
+
32
+ dest = File.join(@template_dir, rel)
33
+ FileUtils.mkdir_p(dest)
34
+
35
+ name = File.basename(rel)
36
+ AppArchetype::Generators.render_empty_template(name, dest)
37
+
38
+ puts("✔ Template `#{name}` created at #{dest}")
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,48 @@
1
+ require 'tty-prompt'
2
+
3
+ module AppArchetype
4
+ module Commands
5
+ # Opens manifest in configured editor
6
+ class OpenManifest
7
+ def initialize(manager, editor, options = Hashie::Mash.new)
8
+ @manager = manager
9
+ @editor = editor
10
+ @options = options
11
+ @prompt = TTY::Prompt.new
12
+ end
13
+
14
+ ##
15
+ # Opens a manifest in the chosen editor program
16
+ #
17
+ # First it looks to the options for the manifest
18
+ # name. If this is not provided the user will be
19
+ # given a list to select from.
20
+ #
21
+ # The manager then attempts to find the manifest
22
+ # by name. If it cannot be found, a RuntimeError
23
+ # is raised and the command stops.
24
+ #
25
+ # Otherwise a new editor subprocess is started
26
+ # with the manifest path given as an arg.
27
+ #
28
+ # This process will wait until the conclusion of
29
+ # the editor process. It's recommended that the
30
+ # editor be a command line editor like `vi`
31
+ #
32
+ def run
33
+ name = @options.name
34
+ name ||= @prompt.select('Please choose manifest', @manager.manifest_names)
35
+
36
+ manifest = @manager.find_by_name(name)
37
+
38
+ unless manifest
39
+ puts "✖ No manifests with name `#{name}` found."
40
+ return
41
+ end
42
+
43
+ pid = Process.spawn("#{@editor} #{manifest.path}")
44
+ Process.waitpid(pid)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,20 @@
1
+ require 'tty-prompt'
2
+
3
+ module AppArchetype
4
+ module Commands
5
+ # Prints templates path to STDOUT
6
+ class PrintPath
7
+ def initialize(template_dir, options = Hashie::Mash.new)
8
+ @template_dir = template_dir
9
+ @options = options
10
+ end
11
+
12
+ ##
13
+ # Prints template directory to STDOUT
14
+ #
15
+ def run
16
+ puts(@template_dir)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,67 @@
1
+ require 'tty-prompt'
2
+ require 'tty-table'
3
+
4
+ module AppArchetype
5
+ module Commands
6
+ # Summarizes and prints variables from template manifest
7
+ class PrintTemplateVariables
8
+ ##
9
+ # Variable table header
10
+ #
11
+ VARIABLE_HEADER = %w[NAME DESCRIPTION DEFAULT].freeze
12
+
13
+ def initialize(manager, options = Hashie::Mash.new)
14
+ @manager = manager
15
+ @options = options
16
+ @prompt = TTY::Prompt.new
17
+ end
18
+
19
+ ##
20
+ # Prints manifest variables, descriptions and defaults
21
+ #
22
+ # First it looks to the options for the manifest name.
23
+ # If one is not provided then the user will be prompted
24
+ # to choose a manifest from the list of known manifests.
25
+ #
26
+ # If the manifest cannot be found a RuntimeError will be
27
+ # raised.
28
+ #
29
+ # Once the manifest is found, an info table is rendered
30
+ # with the variable names, descriptions and any defined
31
+ # defaults.
32
+ #
33
+ def run
34
+ name = @options.name
35
+ name ||= @prompt.select('Please choose manifest', @manager.manifest_names)
36
+
37
+ manifest = @manager.find_by_name(name)
38
+
39
+ if manifest
40
+ puts variable_table_for(manifest).render&.strip
41
+ else
42
+ puts "✖ No manifests with name `#{name}` found."
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ ##
49
+ # Builds a table of template variables
50
+ #
51
+ # @param [Manifest] manifest
52
+ #
53
+ def variable_table_for(manifest)
54
+ TTY::Table.new(
55
+ header: VARIABLE_HEADER,
56
+ rows: manifest.variables.all.map do |variable|
57
+ [
58
+ variable.name,
59
+ variable.description,
60
+ variable.default
61
+ ]
62
+ end
63
+ )
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,19 @@
1
+ require 'tty-prompt'
2
+
3
+ module AppArchetype
4
+ module Commands
5
+ # Prints gem version to STDOUT
6
+ class PrintVersion
7
+ def initialize(options = Hashie::Mash.new)
8
+ @options = options
9
+ end
10
+
11
+ ##
12
+ # Prints gem version
13
+ #
14
+ def run
15
+ puts(AppArchetype::VERSION)
16
+ end
17
+ end
18
+ end
19
+ end