app_archetype 1.2.8 → 1.3.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +36 -9
  3. data/README.md +150 -28
  4. data/app_archetype.gemspec +3 -0
  5. data/bin/app_archetype +20 -0
  6. data/bin/archetype +1 -1
  7. data/lib/app_archetype/cli.rb +171 -139
  8. data/lib/app_archetype/commands/delete_template.rb +58 -0
  9. data/lib/app_archetype/commands/find_templates.rb +66 -0
  10. data/lib/app_archetype/commands/list_templates.rb +49 -0
  11. data/lib/app_archetype/commands/new_template.rb +42 -0
  12. data/lib/app_archetype/commands/open_manifest.rb +48 -0
  13. data/lib/app_archetype/commands/print_path.rb +20 -0
  14. data/lib/app_archetype/commands/print_template_variables.rb +67 -0
  15. data/lib/app_archetype/commands/print_version.rb +19 -0
  16. data/lib/app_archetype/commands/render_template.rb +178 -0
  17. data/lib/app_archetype/commands.rb +13 -0
  18. data/lib/app_archetype/generators.rb +1 -1
  19. data/lib/app_archetype/template_manager.rb +9 -0
  20. data/lib/app_archetype/version.rb +1 -1
  21. data/lib/app_archetype.rb +40 -23
  22. data/scripts/create_new_command +32 -0
  23. data/scripts/generators/command/manifest.json +15 -0
  24. data/scripts/generators/command/template/lib/app_archetype/commands/{{command_name.snake_case}}.rb.hbs +17 -0
  25. data/spec/app_archetype/cli/presenters_spec.rb +99 -99
  26. data/spec/app_archetype/cli/prompts_spec.rb +291 -291
  27. data/spec/app_archetype/cli_spec.rb +427 -65
  28. data/spec/app_archetype/commands/delete_template_spec.rb +132 -0
  29. data/spec/app_archetype/commands/find_templates_spec.rb +130 -0
  30. data/spec/app_archetype/commands/list_templates_spec.rb +55 -0
  31. data/spec/app_archetype/commands/new_template_spec.rb +84 -0
  32. data/spec/app_archetype/commands/open_manifest_spec.rb +113 -0
  33. data/spec/app_archetype/commands/print_path_spec.rb +22 -0
  34. data/spec/app_archetype/commands/print_template_variables_spec.rb +158 -0
  35. data/spec/app_archetype/commands/print_version_spec.rb +21 -0
  36. data/spec/app_archetype/commands/render_template_spec.rb +479 -0
  37. data/spec/app_archetype/generators_spec.rb +1 -1
  38. data/spec/app_archetype/template_manager_spec.rb +32 -0
  39. data/spec/app_archetype_spec.rb +65 -0
  40. metadata +79 -4
  41. data/lib/app_archetype/cli/presenters.rb +0 -106
  42. 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