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,178 @@
1
+ require 'tty-prompt'
2
+
3
+ module AppArchetype
4
+ module Commands
5
+ # Prompts user for variable values and renders template to disk
6
+ class RenderTemplate
7
+ def initialize(manager, destination_path, options = Hashie::Mash.new)
8
+ @manager = manager
9
+ @destination_path = destination_path
10
+ @options = options
11
+ @prompt = TTY::Prompt.new
12
+ end
13
+
14
+ ##
15
+ # Renders a template with instructions described
16
+ # in the manifest.
17
+ #
18
+ # First it looks to the options to determine the
19
+ # name of the manifest. If one is not provided
20
+ # then the user will be prompted to choose a manifest
21
+ # from the list.
22
+ #
23
+ # The manager will then attempt to find the manifest.
24
+ # if one is not found then a RuntimeError will be raised.
25
+ #
26
+ # Once the manifest is loaded the template will be loaded
27
+ # into memory.
28
+ #
29
+ # Then the variables specified in the manifest are
30
+ # resolved. This involves the command prompting
31
+ # for values.
32
+ #
33
+ # A plan can then be constructed with the template and
34
+ # variables, this plan is then devised and executed.
35
+ #
36
+ # When the render is successful a success message
37
+ # is sent to STDOUT to confirm the operation was
38
+ # successful.
39
+ #
40
+ def run
41
+ name = @options.name
42
+ name ||= @prompt.select('Please choose manifest', @manager.manifest_names)
43
+
44
+ manifest = @manager.find_by_name(name)
45
+
46
+ unless manifest
47
+ puts "✖ No template with name `#{name}` found."
48
+ return
49
+ end
50
+
51
+ template = manifest.template
52
+ template.load
53
+
54
+ resolve_variables(manifest)
55
+ render_template(
56
+ manifest,
57
+ template,
58
+ overwrite: @options.overwrite
59
+ )
60
+
61
+ puts("✔ Rendered #{name} to #{@destination_path}")
62
+ end
63
+
64
+ ##
65
+ # Prompts user for values for each variable
66
+ # specified in the given manifest. And then
67
+ # sets the value of those variables to the
68
+ # answers to the prompts.
69
+ #
70
+ # @param[AppArchetype::Template::Manifest] manifest
71
+ #
72
+ def resolve_variables(manifest)
73
+ manifest.variables.all.each do |var|
74
+ value = variable_prompt_for(var)
75
+ var.set!(value)
76
+ end
77
+ end
78
+
79
+ ##
80
+ # Builds plan to render template and executes
81
+ # it - essentially rendering the template to
82
+ # the output location
83
+ #
84
+ # @param [AppArchetype::Template::Manifest] manifest
85
+ # @param [AppArchetype::Template] template
86
+ # @param [Boolean] overwrite
87
+ #
88
+ def render_template(
89
+ manifest,
90
+ template,
91
+ overwrite: false
92
+ )
93
+ plan = AppArchetype::Template::Plan.new(
94
+ template,
95
+ manifest.variables,
96
+ destination_path: @destination_path,
97
+ overwrite: overwrite
98
+ )
99
+
100
+ plan.devise
101
+ plan.execute
102
+ end
103
+
104
+ ##
105
+ # Resolver for a given variable
106
+ #
107
+ # First, it will set the value if the value is set in
108
+ # the manifest.
109
+ #
110
+ # Otherwise it will call a function that prompts
111
+ # a user for input depending on type.
112
+ #
113
+ # By default it will call the string variable prompt
114
+ #
115
+ # @param [AppArchetype::Template::Variable] var
116
+ #
117
+ # @return [Object]
118
+ #
119
+ def variable_prompt_for(var)
120
+ return var.value if var.value?
121
+ return boolean_variable_prompt_for(var) if var.type == 'boolean'
122
+ return integer_variable_prompt_for(var) if var.type == 'integer'
123
+
124
+ string_variable_prompt_for(var)
125
+ end
126
+
127
+ ##
128
+ # Prompts and then asks for boolean input for
129
+ # a boolean variable
130
+ #
131
+ # @param [AppArchetype::Template::Variable] var
132
+ #
133
+ # @return [Boolean]
134
+ #
135
+ def boolean_variable_prompt_for(var)
136
+ puts "• #{var.name} (#{var.description})"
137
+
138
+ @prompt.yes?(
139
+ "Enter value for `#{var.name}` variable:",
140
+ default: var.default
141
+ )
142
+ end
143
+
144
+ ##
145
+ # Prompts and then asks for integer input for
146
+ # a integer variable
147
+ #
148
+ # @param [AppArchetype::Template::Variable] var
149
+ #
150
+ # @return [Integer]
151
+ #
152
+ def integer_variable_prompt_for(var)
153
+ puts "• #{var.name} (#{var.description})"
154
+
155
+ @prompt.ask("Enter value for `#{var.name}` variable:",
156
+ convert: :int,
157
+ default: var.default)
158
+ end
159
+
160
+ ##
161
+ # Prompts and then asks for string input for
162
+ # a string variable
163
+ #
164
+ # @param [AppArchetype::Template::Variable] var
165
+ #
166
+ # @return [String]
167
+ #
168
+ def string_variable_prompt_for(var)
169
+ puts "• #{var.name} (#{var.description})"
170
+
171
+ @prompt.ask(
172
+ "Enter value for `#{var.name}` variable:",
173
+ default: var.default
174
+ )
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,13 @@
1
+ require 'hashie'
2
+
3
+ ARCHETYPE_CLI_COMMANDS = File.join(__dir__, 'commands', '*.rb')
4
+
5
+ Dir[ARCHETYPE_CLI_COMMANDS].sort.each do |file|
6
+ require file
7
+ end
8
+
9
+ module AppArchetype
10
+ # Module for CLI command classes
11
+ module Commands
12
+ end
13
+ end
@@ -25,7 +25,8 @@ module AppArchetype
25
25
  'version' => AppArchetype::VERSION
26
26
  }
27
27
  },
28
- 'variables' => DEFAULT_VARS
28
+ 'variables' => DEFAULT_VARS,
29
+ 'next_steps' => []
29
30
  }
30
31
  end
31
32
 
@@ -48,7 +49,7 @@ module AppArchetype
48
49
  MD
49
50
  end
50
51
 
51
- class <<self
52
+ class << self
52
53
  ##
53
54
  # Render empty template renders a manifest and template folder at
54
55
  # the given path.
@@ -60,7 +61,7 @@ module AppArchetype
60
61
  # @param [String] path
61
62
  #
62
63
  def render_empty_template(name, path)
63
- template_path = File.join(path, name)
64
+ template_path = File.join(path, 'template')
64
65
  manifest_path = File.join(path, 'manifest.json')
65
66
  readme_path = File.join(path, 'README.md')
66
67
 
@@ -38,11 +38,14 @@ module AppArchetype
38
38
  },
39
39
  variables: {
40
40
  type: 'object'
41
+ },
42
+ next_steps: {
43
+ type: 'array'
41
44
  }
42
45
  }
43
46
  }.freeze
44
47
 
45
- class <<self
48
+ class << self
46
49
  ##
47
50
  # Creates a [AppArchetype::Template] from a manifest json so long as the
48
51
  # manifest is compatible with this version of AppArchetype.
@@ -130,6 +133,19 @@ module AppArchetype
130
133
  @data.metadata
131
134
  end
132
135
 
136
+ ##
137
+ # Next steps getter
138
+ #
139
+ # @return [String]
140
+ #
141
+ def next_steps
142
+ steps = @data.next_steps
143
+
144
+ return [] unless steps
145
+
146
+ steps
147
+ end
148
+
133
149
  ##
134
150
  # Parent path of the manifest (working directory)
135
151
  #
@@ -27,12 +27,10 @@ module AppArchetype
27
27
  Dir.glob(
28
28
  File.join(@template_dir, '**', 'manifest.json*')
29
29
  ).each do |manifest|
30
- begin
31
- @manifests << AppArchetype::Template::Manifest.new_from_file(manifest)
32
- rescue StandardError
33
- puts "WARN: `#{manifest}` is invalid, skipping"
34
- next
35
- end
30
+ @manifests << AppArchetype::Template::Manifest.new_from_file(manifest)
31
+ rescue StandardError
32
+ puts "WARN: `#{manifest}` is invalid, skipping"
33
+ next
36
34
  end
37
35
  end
38
36
 
@@ -109,5 +107,14 @@ module AppArchetype
109
107
 
110
108
  results.first
111
109
  end
110
+
111
+ ##
112
+ # Returns a list of manifest names from loaded templates
113
+ #
114
+ # @return [Array]
115
+ #
116
+ def manifest_names
117
+ @manifests.map(&:name)
118
+ end
112
119
  end
113
120
  end
@@ -2,5 +2,5 @@ module AppArchetype
2
2
  ##
3
3
  # AppArchetype version
4
4
  #
5
- VERSION = '1.2.8'.freeze
5
+ VERSION = '1.4.2'.freeze
6
6
  end
data/lib/app_archetype.rb CHANGED
@@ -7,41 +7,58 @@ require 'app_archetype/template_manager'
7
7
  require 'app_archetype/renderer'
8
8
  require 'app_archetype/generators'
9
9
 
10
- require 'app_archetype/cli/prompts'
11
-
12
10
  require 'app_archetype/version'
11
+ require_relative './app_archetype/commands'
13
12
 
14
13
  # AppArchetype is the namespace for app_archetype
15
14
  module AppArchetype
16
- def self.render(
17
- name,
18
- templates_dir,
19
- destination_path: Dir.pwd,
20
- overwrite: true,
21
- variables: []
15
+ ##
16
+ # Self contained template render method
17
+ #
18
+ # Takes collection directory and template name to
19
+ # load a new manager and find the desired template
20
+ #
21
+ # Then executes a render template command with the
22
+ # found template
23
+ #
24
+ # Returns the manifest rendered. Target can optionally
25
+ # be set to overwrite
26
+ #
27
+ # This method is to be used for self contained rendering#
28
+ # scripts.
29
+ #
30
+ # @param [String] collection_dir
31
+ # @param [String] template_name
32
+ # @param [String] destination_path
33
+ # @param [Boolean] overwrite
34
+ #
35
+ # @return [AppArchetype::Template::Manifest]
36
+ #
37
+ def self.render_template(
38
+ collection_dir: ENV.fetch('ARCHETYPE_TEMPLATE_DIR'),
39
+ template_name: '',
40
+ destination_path: '',
41
+ overwrite: false
22
42
  )
23
- manifest_file = File.join(templates_dir, name, 'manifest.json')
43
+ manager = AppArchetype::TemplateManager.new(collection_dir)
44
+ manager.load
24
45
 
25
- manifest = AppArchetype::Template::Manifest.new_from_file(manifest_file)
46
+ manifest = manager.find_by_name(template_name)
26
47
 
27
48
  template = manifest.template
28
49
  template.load
29
50
 
30
- variables.each { |var| manifest.variables.add(var) }
31
-
32
- manifest.variables.all.each do |var|
33
- value = AppArchetype::CLI::Prompts.variable_prompt_for(var)
34
- var.set!(value)
35
- end
36
-
37
- plan = AppArchetype::Template::Plan.new(
38
- template,
39
- manifest.variables,
40
- destination_path: destination_path,
51
+ options = Hashie::Mash.new(
52
+ name: template_name,
41
53
  overwrite: overwrite
42
54
  )
43
55
 
44
- plan.devise
45
- plan.execute
56
+ command = AppArchetype::Commands::RenderTemplate.new(
57
+ manager, destination_path, options
58
+ )
59
+
60
+ command.run
61
+
62
+ manifest
46
63
  end
47
64
  end
@@ -7,8 +7,9 @@ class String
7
7
  #
8
8
  # @return [String]
9
9
  #
10
- def snake_case
11
- helper.snake_case(self)
10
+ def snake_case(str = nil)
11
+ str ||= self
12
+ helper.snake_case(str)
12
13
  end
13
14
 
14
15
  ##
@@ -16,8 +17,9 @@ class String
16
17
  #
17
18
  # @return [String]
18
19
  #
19
- def dash_case
20
- helper.dash_case(self)
20
+ def dash_case(str = nil)
21
+ str ||= self
22
+ helper.dash_case(str)
21
23
  end
22
24
 
23
25
  ##
@@ -25,8 +27,9 @@ class String
25
27
  #
26
28
  # @return [String]
27
29
  #
28
- def camel_case
29
- helper.camel_case(self)
30
+ def camel_case(str = nil)
31
+ str ||= self
32
+ helper.camel_case(str)
30
33
  end
31
34
 
32
35
  ##
@@ -34,8 +37,9 @@ class String
34
37
  #
35
38
  # @return [String]
36
39
  #
37
- def pluralize
38
- helper.pluralize(self)
40
+ def pluralize(str = nil)
41
+ str ||= self
42
+ helper.pluralize(str)
39
43
  end
40
44
 
41
45
  ##
@@ -43,8 +47,9 @@ class String
43
47
  #
44
48
  # @return [String]
45
49
  #
46
- def singularize
47
- helper.singularize(self)
50
+ def singularize(str = nil)
51
+ str ||= self
52
+ helper.singularize(str)
48
53
  end
49
54
 
50
55
  ##
@@ -52,8 +57,9 @@ class String
52
57
  #
53
58
  # @return [String]
54
59
  #
55
- def randomize(size = 5)
56
- helper.randomize(self, size.to_s)
60
+ def randomize(size = 5, str = nil)
61
+ str ||= self
62
+ helper.randomize(str, size.to_s)
57
63
  end
58
64
 
59
65
  private
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/app_archetype'
4
+
5
+ puts 'CREATE NEW COMMAND'
6
+
7
+ manifest = AppArchetype.render_template(
8
+ collection_dir: File.join(__dir__, 'generators'),
9
+ template_name: 'command',
10
+ destination_path: File.expand_path(File.join(__dir__, '..'))
11
+ )
12
+
13
+ command_name = manifest.variables.get('command_name').value
14
+
15
+ next_steps = <<~TEXT
16
+ ✔ Command created
17
+
18
+ TODO:
19
+
20
+ Add the following to cli.rb within AppArchetype::CLI:
21
+
22
+ ```
23
+ desc '#{command_name.snake_case}', 'TODO: description'
24
+
25
+ def #{command_name.snake_case}
26
+ cmd = AppArchetype::Commands::#{command_name.camel_case}.new(options)
27
+ cmd.run
28
+ end
29
+ ```
30
+ TEXT
31
+
32
+ puts next_steps
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "command",
3
+ "version": "1.0.0",
4
+ "metadata": {
5
+ "app_archetype": {
6
+ "version": "1.2.7"
7
+ }
8
+ },
9
+ "variables": {
10
+ "command_name": {
11
+ "type": "string",
12
+ "description": "Name of command (i.e. create_new_thing)"
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,17 @@
1
+ require 'tty-prompt'
2
+
3
+ module AppArchetype
4
+ module Commands
5
+ class {{command_name.camel_case}}
6
+ def initialize(manager, options)
7
+ @manager = manager
8
+ @options = options
9
+ @prompt = TTY::Prompt.new
10
+ end
11
+
12
+ def run
13
+ puts('✔ {{command_name.snake_case}} complete')
14
+ end
15
+ end
16
+ end
17
+ end