pdk 2.3.0 → 2.4.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 (153) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1329 -1321
  3. data/LICENSE +201 -201
  4. data/README.md +163 -163
  5. data/exe/pdk +10 -10
  6. data/lib/pdk/analytics/client/google_analytics.rb +143 -143
  7. data/lib/pdk/analytics/client/noop.rb +25 -25
  8. data/lib/pdk/analytics/util.rb +19 -19
  9. data/lib/pdk/analytics.rb +30 -30
  10. data/lib/pdk/answer_file.rb +12 -12
  11. data/lib/pdk/bolt.rb +19 -19
  12. data/lib/pdk/cli/build.rb +82 -82
  13. data/lib/pdk/cli/bundle.rb +48 -48
  14. data/lib/pdk/cli/config/get.rb +26 -26
  15. data/lib/pdk/cli/config.rb +22 -22
  16. data/lib/pdk/cli/console.rb +148 -148
  17. data/lib/pdk/cli/convert.rb +52 -52
  18. data/lib/pdk/cli/env.rb +52 -52
  19. data/lib/pdk/cli/errors.rb +25 -25
  20. data/lib/pdk/cli/exec/command.rb +293 -293
  21. data/lib/pdk/cli/exec/interactive_command.rb +114 -114
  22. data/lib/pdk/cli/exec.rb +84 -84
  23. data/lib/pdk/cli/exec_group.rb +104 -104
  24. data/lib/pdk/cli/get/config.rb +24 -24
  25. data/lib/pdk/cli/get.rb +20 -20
  26. data/lib/pdk/cli/module/build.rb +12 -12
  27. data/lib/pdk/cli/module/generate.rb +47 -47
  28. data/lib/pdk/cli/module.rb +14 -14
  29. data/lib/pdk/cli/new/class.rb +32 -32
  30. data/lib/pdk/cli/new/defined_type.rb +32 -32
  31. data/lib/pdk/cli/new/fact.rb +29 -29
  32. data/lib/pdk/cli/new/function.rb +29 -29
  33. data/lib/pdk/cli/new/module.rb +53 -53
  34. data/lib/pdk/cli/new/provider.rb +29 -29
  35. data/lib/pdk/cli/new/task.rb +34 -34
  36. data/lib/pdk/cli/new/test.rb +52 -52
  37. data/lib/pdk/cli/new/transport.rb +27 -27
  38. data/lib/pdk/cli/new.rb +21 -21
  39. data/lib/pdk/cli/release/prep.rb +39 -39
  40. data/lib/pdk/cli/release/publish.rb +50 -50
  41. data/lib/pdk/cli/release.rb +194 -194
  42. data/lib/pdk/cli/remove/config.rb +80 -80
  43. data/lib/pdk/cli/remove.rb +20 -20
  44. data/lib/pdk/cli/set/config.rb +119 -119
  45. data/lib/pdk/cli/set.rb +20 -20
  46. data/lib/pdk/cli/test/unit.rb +90 -90
  47. data/lib/pdk/cli/test.rb +11 -11
  48. data/lib/pdk/cli/update.rb +64 -64
  49. data/lib/pdk/cli/util/command_redirector.rb +27 -27
  50. data/lib/pdk/cli/util/interview.rb +72 -72
  51. data/lib/pdk/cli/util/option_normalizer.rb +55 -55
  52. data/lib/pdk/cli/util/option_validator.rb +68 -68
  53. data/lib/pdk/cli/util/spinner.rb +13 -13
  54. data/lib/pdk/cli/util/update_manager_printer.rb +82 -82
  55. data/lib/pdk/cli/util.rb +305 -305
  56. data/lib/pdk/cli/validate.rb +116 -116
  57. data/lib/pdk/cli.rb +175 -175
  58. data/lib/pdk/config/analytics_schema.json +26 -26
  59. data/lib/pdk/config/errors.rb +5 -5
  60. data/lib/pdk/config/ini_file.rb +183 -183
  61. data/lib/pdk/config/ini_file_setting.rb +39 -39
  62. data/lib/pdk/config/json.rb +34 -34
  63. data/lib/pdk/config/json_schema_namespace.rb +142 -142
  64. data/lib/pdk/config/json_schema_setting.rb +53 -53
  65. data/lib/pdk/config/json_with_schema.rb +49 -49
  66. data/lib/pdk/config/namespace.rb +354 -354
  67. data/lib/pdk/config/setting.rb +135 -135
  68. data/lib/pdk/config/validator.rb +31 -31
  69. data/lib/pdk/config/yaml.rb +46 -46
  70. data/lib/pdk/config/yaml_with_schema.rb +59 -59
  71. data/lib/pdk/config.rb +390 -390
  72. data/lib/pdk/context/control_repo.rb +60 -60
  73. data/lib/pdk/context/module.rb +28 -28
  74. data/lib/pdk/context/none.rb +22 -22
  75. data/lib/pdk/context.rb +99 -99
  76. data/lib/pdk/control_repo.rb +90 -90
  77. data/lib/pdk/generate/defined_type.rb +43 -43
  78. data/lib/pdk/generate/fact.rb +25 -25
  79. data/lib/pdk/generate/function.rb +48 -48
  80. data/lib/pdk/generate/module.rb +352 -352
  81. data/lib/pdk/generate/provider.rb +28 -28
  82. data/lib/pdk/generate/puppet_class.rb +43 -43
  83. data/lib/pdk/generate/puppet_object.rb +232 -232
  84. data/lib/pdk/generate/task.rb +68 -68
  85. data/lib/pdk/generate/transport.rb +33 -33
  86. data/lib/pdk/generate.rb +24 -24
  87. data/lib/pdk/i18n.rb +4 -4
  88. data/lib/pdk/logger.rb +45 -45
  89. data/lib/pdk/module/build.rb +322 -322
  90. data/lib/pdk/module/convert.rb +296 -296
  91. data/lib/pdk/module/metadata.rb +202 -202
  92. data/lib/pdk/module/release.rb +260 -260
  93. data/lib/pdk/module/update.rb +131 -131
  94. data/lib/pdk/module/update_manager.rb +227 -227
  95. data/lib/pdk/module.rb +30 -30
  96. data/lib/pdk/report/event.rb +370 -370
  97. data/lib/pdk/report.rb +121 -121
  98. data/lib/pdk/template/fetcher/git.rb +85 -85
  99. data/lib/pdk/template/fetcher/local.rb +28 -28
  100. data/lib/pdk/template/fetcher.rb +98 -98
  101. data/lib/pdk/template/renderer/v1/legacy_template_dir.rb +116 -116
  102. data/lib/pdk/template/renderer/v1/renderer.rb +132 -132
  103. data/lib/pdk/template/renderer/v1/template_file.rb +102 -102
  104. data/lib/pdk/template/renderer/v1.rb +25 -25
  105. data/lib/pdk/template/renderer.rb +96 -96
  106. data/lib/pdk/template/template_dir.rb +67 -67
  107. data/lib/pdk/template.rb +59 -59
  108. data/lib/pdk/tests/unit.rb +252 -252
  109. data/lib/pdk/util/bundler.rb +259 -259
  110. data/lib/pdk/util/changelog_generator.rb +137 -137
  111. data/lib/pdk/util/env.rb +47 -47
  112. data/lib/pdk/util/filesystem.rb +138 -138
  113. data/lib/pdk/util/git.rb +179 -179
  114. data/lib/pdk/util/json_finder.rb +85 -85
  115. data/lib/pdk/util/puppet_strings.rb +125 -125
  116. data/lib/pdk/util/puppet_version.rb +266 -266
  117. data/lib/pdk/util/ruby_version.rb +179 -179
  118. data/lib/pdk/util/template_uri.rb +295 -295
  119. data/lib/pdk/util/vendored_file.rb +93 -93
  120. data/lib/pdk/util/version.rb +43 -43
  121. data/lib/pdk/util/windows/api_types.rb +82 -82
  122. data/lib/pdk/util/windows/file.rb +36 -36
  123. data/lib/pdk/util/windows/process.rb +79 -79
  124. data/lib/pdk/util/windows/string.rb +16 -16
  125. data/lib/pdk/util/windows.rb +15 -15
  126. data/lib/pdk/util.rb +278 -277
  127. data/lib/pdk/validate/control_repo/control_repo_validator_group.rb +23 -23
  128. data/lib/pdk/validate/control_repo/environment_conf_validator.rb +98 -98
  129. data/lib/pdk/validate/external_command_validator.rb +208 -208
  130. data/lib/pdk/validate/internal_ruby_validator.rb +100 -100
  131. data/lib/pdk/validate/invokable_validator.rb +228 -228
  132. data/lib/pdk/validate/metadata/metadata_json_lint_validator.rb +86 -86
  133. data/lib/pdk/validate/metadata/metadata_syntax_validator.rb +78 -78
  134. data/lib/pdk/validate/metadata/metadata_validator_group.rb +20 -20
  135. data/lib/pdk/validate/puppet/puppet_epp_validator.rb +133 -133
  136. data/lib/pdk/validate/puppet/puppet_lint_validator.rb +66 -66
  137. data/lib/pdk/validate/puppet/puppet_syntax_validator.rb +137 -137
  138. data/lib/pdk/validate/puppet/puppet_validator_group.rb +21 -21
  139. data/lib/pdk/validate/ruby/ruby_rubocop_validator.rb +80 -80
  140. data/lib/pdk/validate/ruby/ruby_validator_group.rb +19 -19
  141. data/lib/pdk/validate/tasks/tasks_metadata_lint_validator.rb +88 -88
  142. data/lib/pdk/validate/tasks/tasks_name_validator.rb +50 -50
  143. data/lib/pdk/validate/tasks/tasks_validator_group.rb +20 -20
  144. data/lib/pdk/validate/validator.rb +118 -118
  145. data/lib/pdk/validate/validator_group.rb +104 -104
  146. data/lib/pdk/validate/yaml/yaml_syntax_validator.rb +95 -95
  147. data/lib/pdk/validate/yaml/yaml_validator_group.rb +19 -19
  148. data/lib/pdk/validate.rb +94 -94
  149. data/lib/pdk/version.rb +4 -4
  150. data/lib/pdk.rb +76 -76
  151. data/locales/config.yaml +21 -21
  152. data/locales/pdk.pot +2094 -2094
  153. metadata +5 -6
@@ -1,28 +1,28 @@
1
- require 'pdk'
2
-
3
- module PDK
4
- module Generate
5
- class Provider < PuppetObject
6
- def friendly_name
7
- 'Resource API Provider'.freeze
8
- end
9
-
10
- def template_files
11
- files = {
12
- 'provider_spec.erb' => File.join('spec', 'unit', 'puppet', 'provider', object_name, object_name) + '_spec.rb',
13
- 'provider_type_spec.erb' => File.join('spec', 'unit', 'puppet', 'type', object_name) + '_spec.rb',
14
- }
15
- return files if spec_only?
16
- files.merge(
17
- 'provider.erb' => File.join('lib', 'puppet', 'provider', object_name, object_name) + '.rb',
18
- 'provider_type.erb' => File.join('lib', 'puppet', 'type', object_name) + '.rb',
19
- )
20
- end
21
-
22
- def template_data
23
- { name: object_name,
24
- provider_class: class_name_from_object_name(object_name) }
25
- end
26
- end
27
- end
28
- end
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Generate
5
+ class Provider < PuppetObject
6
+ def friendly_name
7
+ 'Resource API Provider'.freeze
8
+ end
9
+
10
+ def template_files
11
+ files = {
12
+ 'provider_spec.erb' => File.join('spec', 'unit', 'puppet', 'provider', object_name, object_name) + '_spec.rb',
13
+ 'provider_type_spec.erb' => File.join('spec', 'unit', 'puppet', 'type', object_name) + '_spec.rb',
14
+ }
15
+ return files if spec_only?
16
+ files.merge(
17
+ 'provider.erb' => File.join('lib', 'puppet', 'provider', object_name, object_name) + '.rb',
18
+ 'provider_type.erb' => File.join('lib', 'puppet', 'type', object_name) + '.rb',
19
+ )
20
+ end
21
+
22
+ def template_data
23
+ { name: object_name,
24
+ provider_class: class_name_from_object_name(object_name) }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,43 +1,43 @@
1
- require 'pdk'
2
-
3
- module PDK
4
- module Generate
5
- class PuppetClass < PuppetObject
6
- PUPPET_STRINGS_TYPE = 'puppet_classes'.freeze
7
-
8
- def initialize(*_args)
9
- super
10
- object_name_parts = @object_name.split('::')
11
-
12
- @object_name = if object_name_parts.first == module_name
13
- object_name
14
- else
15
- [module_name, object_name].join('::')
16
- end
17
- end
18
-
19
- def friendly_name
20
- 'Puppet Class'.freeze
21
- end
22
-
23
- def template_files
24
- # Calculate the class tests name
25
- class_name_parts = object_name.split('::')
26
- # Drop the module name if the object name contains multiple parts
27
- class_name_parts.delete_at(0) if class_name_parts.length > 1
28
- files = { 'class_spec.erb' => File.join('spec', 'classes', *class_name_parts) + '_spec.rb' }
29
- return files if spec_only?
30
-
31
- class_name_parts = object_name.split('::')[1..-1]
32
- class_name_parts << 'init' if class_name_parts.empty?
33
- files['class.erb'] = File.join('manifests', *class_name_parts) + '.pp'
34
-
35
- files
36
- end
37
-
38
- def template_data
39
- { name: object_name }
40
- end
41
- end
42
- end
43
- end
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Generate
5
+ class PuppetClass < PuppetObject
6
+ PUPPET_STRINGS_TYPE = 'puppet_classes'.freeze
7
+
8
+ def initialize(*_args)
9
+ super
10
+ object_name_parts = @object_name.split('::')
11
+
12
+ @object_name = if object_name_parts.first == module_name
13
+ object_name
14
+ else
15
+ [module_name, object_name].join('::')
16
+ end
17
+ end
18
+
19
+ def friendly_name
20
+ 'Puppet Class'.freeze
21
+ end
22
+
23
+ def template_files
24
+ # Calculate the class tests name
25
+ class_name_parts = object_name.split('::')
26
+ # Drop the module name if the object name contains multiple parts
27
+ class_name_parts.delete_at(0) if class_name_parts.length > 1
28
+ files = { 'class_spec.erb' => File.join('spec', 'classes', *class_name_parts) + '_spec.rb' }
29
+ return files if spec_only?
30
+
31
+ class_name_parts = object_name.split('::')[1..-1]
32
+ class_name_parts << 'init' if class_name_parts.empty?
33
+ files['class.erb'] = File.join('manifests', *class_name_parts) + '.pp'
34
+
35
+ files
36
+ end
37
+
38
+ def template_data
39
+ { name: object_name }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,232 +1,232 @@
1
- require 'pdk'
2
-
3
- module PDK
4
- module Generate
5
- class PuppetObject
6
- attr_reader :context
7
- attr_reader :object_name
8
- attr_reader :options
9
-
10
- # Initialises the PDK::Generate::PuppetObject object.
11
- #
12
- # In general, this object should never be instantiated directly. Instead,
13
- # one of the subclasses should be used e.g. PDK::Generate::Klass.
14
- #
15
- # New subclasses generally only need to inherit this class, set the
16
- # OBJECT_TYPE constant and implement the {#template_data},
17
- # {#target_object_path} and {#target_spec_path} methods.
18
- #
19
- # @param module_dir [String] The path to the root of module that the
20
- # will contain the object.
21
- # @param object_name [String] The name of the object.
22
- # @param options [Hash{Symbol => Object}]
23
- def initialize(context, object_name, options)
24
- raise ArgumentError, _('Expected PDK::Context::AbstractContext but got \'%{klass}\' for context') % { klass: context.class } unless context.is_a?(PDK::Context::AbstractContext)
25
- @context = context
26
- @options = options
27
- @object_name = object_name
28
- end
29
-
30
- # Whether the generator should only return test (spec) files
31
- # @return [Boolean]
32
- def spec_only?
33
- @options[:spec_only]
34
- end
35
-
36
- # Subclass and implement {#friendly_name} to provide a nice name to show users in CLI
37
- # @abstract
38
- # @return String
39
- def friendly_name
40
- raise NotImplementedError
41
- end
42
-
43
- # Subclass and implement {#template_files} to provide the template files to
44
- # render. Implementations of this method should return a Hash!{String => String}.
45
- # @abstract
46
- # @return Hash{String => String} Hash key is the source template file and the Hash value is
47
- # the relative destination path
48
- def template_files
49
- raise NotImplementedError
50
- end
51
-
52
- # Subclass and implement {#template_data} to provide data to the templates during rendering.
53
- # @abstract
54
- # @return Hash{Symbol => Object}
55
- def template_data
56
- raise NotImplementedError
57
- end
58
-
59
- # Raises an error if any pre-conditions are not met
60
- #
61
- # @return [void]
62
- # @abstract
63
- def check_preconditions
64
- raise ArgumentError, _('Expected a module context but got %{context_name}') % { context_name: context.display_name } unless context.is_a?(PDK::Context::Module)
65
- end
66
-
67
- # Check the preconditions of this template group, behaving as a predicate rather than raising an exception.
68
- #
69
- # @return [Boolean] true if the generator is safe to run, otherwise false.
70
- def can_run?
71
- check_preconditions
72
- true
73
- rescue StandardError
74
- false
75
- end
76
-
77
- # Creates an instance of an update manager
78
- # @api private
79
- def update_manager_instance
80
- require 'pdk/module/update_manager'
81
- PDK::Module::UpdateManager.new
82
- end
83
-
84
- # Stages and then executes the changes for the templates to be rendereed.
85
- # This is the main entry point for the class.
86
- #
87
- # @see #stage_changes
88
- # @return [PDK::Module::UpdateManager] The update manager which implemented the changes
89
- # @api public
90
- def run(update_manager = update_manager_instance)
91
- stage_changes(update_manager).sync_changes!
92
- update_manager
93
- end
94
-
95
- # Check that the templates can be rendered. Find an appropriate template
96
- # and stages the target files from the template. This is the main entry
97
- # point for the class.
98
- #
99
- # @raise [PDK::CLI::ExitWithError] if the target files already exist.
100
- # @raise [PDK::CLI::FatalError] (see #render_file)
101
- # @return [PDK::Module::UpdateManager] The update manager with the staged changes
102
- # @api public
103
- def stage_changes(update_manager)
104
- check_preconditions
105
-
106
- with_templates do |template_dir|
107
- template_files.each do |source_file, relative_dest_path|
108
- new_content = template_dir.render_single_item(source_file, template_data)
109
- next if new_content.nil?
110
-
111
- stage_change(relative_dest_path, new_content, update_manager)
112
- end
113
- end
114
- non_template_files.each { |relative_dest_path, content| stage_change(relative_dest_path, content, update_manager) }
115
-
116
- update_manager
117
- end
118
-
119
- # Stages a single file into the Update Manager.
120
- # @return [void]
121
- # @api private
122
- def stage_change(relative_dest_path, content, update_manager)
123
- absolute_file_path = File.join(context.root_path, relative_dest_path)
124
- if PDK::Util::Filesystem.exist?(absolute_file_path)
125
- raise PDK::CLI::ExitWithError, _("Unable to generate %{object_type}; '%{file}' already exists.") % {
126
- file: absolute_file_path,
127
- object_type: spec_only? ? 'unit test' : friendly_name,
128
- }
129
- end
130
- update_manager.add_file(absolute_file_path, content)
131
- end
132
-
133
- # A subclass may wish to stage files into the Update Manager, but the content is not templated. Subclasses
134
- # can override this method to stage arbitrary files
135
- #
136
- # @api private
137
- # @return [Hash{String => String}] A Hash with the relative file path as the key and the new file content as the value.
138
- # @abstract
139
- def non_template_files
140
- {}
141
- end
142
-
143
- # Search the possible template directories in order of preference to find
144
- # a template that can be used to render a new object of the specified
145
- # type.
146
- #
147
- # @yieldparam template_paths [Hash{Symbol => String}] :object contains
148
- # the path on disk to the template file for the object, :spec contains
149
- # the path on disk to the template file for the tests for the object
150
- # (if it exists).
151
- # @yieldparam config_hash [Hash{Object => Object}] the contents of the
152
- # :global key in the config_defaults.yml file.
153
- #
154
- # @raise [PDK::CLI::FatalError] if no suitable template could be found.
155
- #
156
- # @api private
157
- def with_templates
158
- require 'pdk/logger'
159
- require 'pdk/util/template_uri'
160
-
161
- templates.each do |template|
162
- if template[:uri].nil?
163
- PDK.logger.debug(_('No %{dir_type} template found; trying next template directory.') % { dir_type: template[:type] })
164
- next
165
- end
166
-
167
- PDK::Template.with(PDK::Util::TemplateURI.new(template[:uri]), context) do |template_dir|
168
- if template_files.any? { |source_file, _| template_dir.has_single_item?(source_file) }
169
- yield template_dir
170
- # TODO: refactor to a search-and-execute form instead
171
- return # work is done # rubocop:disable Lint/NonLocalExitFromIterator
172
- elsif template[:allow_fallback]
173
- PDK.logger.debug(_('Unable to find a %{type} template in %{url}; trying next template directory.') % { type: friendly_name, url: template[:uri] })
174
- else
175
- raise PDK::CLI::FatalError, _('Unable to find the %{type} template in %{url}.') % { type: friendly_name, url: template[:uri] }
176
- end
177
- end
178
- end
179
- rescue ArgumentError => e
180
- raise PDK::CLI::ExitWithError, e
181
- end
182
-
183
- # Provides the possible template directory locations in the order in
184
- # which they should be searched for a valid template.
185
- #
186
- # If a template-url has been specified on in the options hash (e.g. from
187
- # a CLI parameter), then this template directory will be checked first
188
- # and we do not fall back to the next possible template directory.
189
- #
190
- # If we have not been provided a specific template directory to use, we
191
- # try the template specified in the module metadata (as set during
192
- # PDK::Generate::Module) and fall back to the default template if
193
- # necessary.
194
- #
195
- # @return [Array<Hash{Symbol => Object}>] an array of hashes. Each hash
196
- # contains 3 keys: :type contains a String that describes the template
197
- # directory, :url contains a String with the URL to the template
198
- # directory, and :allow_fallback contains a Boolean that specifies if
199
- # the lookup process should proceed to the next template directory if
200
- # the template file is not in this template directory.
201
- #
202
- # @api private
203
- def templates
204
- require 'pdk/util/template_uri'
205
-
206
- @templates ||= PDK::Util::TemplateURI.templates(@options)
207
- end
208
-
209
- # Retrieves the name of the module (without the forge username) from the
210
- # module metadata.
211
- #
212
- # @return [String] The name of the module.
213
- #
214
- # @api private
215
- def module_name
216
- return nil unless context.is_a?(PDK::Context::Module)
217
-
218
- require 'pdk/util'
219
- @module_name ||= PDK::Util.module_metadata(context.root_path)['name'].rpartition('-').last
220
- rescue ArgumentError => e
221
- raise PDK::CLI::FatalError, e
222
- end
223
-
224
- private
225
-
226
- # Transform an object name into a ruby class name
227
- def class_name_from_object_name(object_name)
228
- object_name.to_s.split('_').map(&:capitalize).join
229
- end
230
- end
231
- end
232
- end
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Generate
5
+ class PuppetObject
6
+ attr_reader :context
7
+ attr_reader :object_name
8
+ attr_reader :options
9
+
10
+ # Initialises the PDK::Generate::PuppetObject object.
11
+ #
12
+ # In general, this object should never be instantiated directly. Instead,
13
+ # one of the subclasses should be used e.g. PDK::Generate::Klass.
14
+ #
15
+ # New subclasses generally only need to inherit this class, set the
16
+ # OBJECT_TYPE constant and implement the {#template_data},
17
+ # {#target_object_path} and {#target_spec_path} methods.
18
+ #
19
+ # @param module_dir [String] The path to the root of module that the
20
+ # will contain the object.
21
+ # @param object_name [String] The name of the object.
22
+ # @param options [Hash{Symbol => Object}]
23
+ def initialize(context, object_name, options)
24
+ raise ArgumentError, _('Expected PDK::Context::AbstractContext but got \'%{klass}\' for context') % { klass: context.class } unless context.is_a?(PDK::Context::AbstractContext)
25
+ @context = context
26
+ @options = options
27
+ @object_name = object_name
28
+ end
29
+
30
+ # Whether the generator should only return test (spec) files
31
+ # @return [Boolean]
32
+ def spec_only?
33
+ @options[:spec_only]
34
+ end
35
+
36
+ # Subclass and implement {#friendly_name} to provide a nice name to show users in CLI
37
+ # @abstract
38
+ # @return String
39
+ def friendly_name
40
+ raise NotImplementedError
41
+ end
42
+
43
+ # Subclass and implement {#template_files} to provide the template files to
44
+ # render. Implementations of this method should return a Hash!{String => String}.
45
+ # @abstract
46
+ # @return Hash{String => String} Hash key is the source template file and the Hash value is
47
+ # the relative destination path
48
+ def template_files
49
+ raise NotImplementedError
50
+ end
51
+
52
+ # Subclass and implement {#template_data} to provide data to the templates during rendering.
53
+ # @abstract
54
+ # @return Hash{Symbol => Object}
55
+ def template_data
56
+ raise NotImplementedError
57
+ end
58
+
59
+ # Raises an error if any pre-conditions are not met
60
+ #
61
+ # @return [void]
62
+ # @abstract
63
+ def check_preconditions
64
+ raise ArgumentError, _('Expected a module context but got %{context_name}') % { context_name: context.display_name } unless context.is_a?(PDK::Context::Module)
65
+ end
66
+
67
+ # Check the preconditions of this template group, behaving as a predicate rather than raising an exception.
68
+ #
69
+ # @return [Boolean] true if the generator is safe to run, otherwise false.
70
+ def can_run?
71
+ check_preconditions
72
+ true
73
+ rescue StandardError
74
+ false
75
+ end
76
+
77
+ # Creates an instance of an update manager
78
+ # @api private
79
+ def update_manager_instance
80
+ require 'pdk/module/update_manager'
81
+ PDK::Module::UpdateManager.new
82
+ end
83
+
84
+ # Stages and then executes the changes for the templates to be rendereed.
85
+ # This is the main entry point for the class.
86
+ #
87
+ # @see #stage_changes
88
+ # @return [PDK::Module::UpdateManager] The update manager which implemented the changes
89
+ # @api public
90
+ def run(update_manager = update_manager_instance)
91
+ stage_changes(update_manager).sync_changes!
92
+ update_manager
93
+ end
94
+
95
+ # Check that the templates can be rendered. Find an appropriate template
96
+ # and stages the target files from the template. This is the main entry
97
+ # point for the class.
98
+ #
99
+ # @raise [PDK::CLI::ExitWithError] if the target files already exist.
100
+ # @raise [PDK::CLI::FatalError] (see #render_file)
101
+ # @return [PDK::Module::UpdateManager] The update manager with the staged changes
102
+ # @api public
103
+ def stage_changes(update_manager)
104
+ check_preconditions
105
+
106
+ with_templates do |template_dir|
107
+ template_files.each do |source_file, relative_dest_path|
108
+ new_content = template_dir.render_single_item(source_file, template_data)
109
+ next if new_content.nil?
110
+
111
+ stage_change(relative_dest_path, new_content, update_manager)
112
+ end
113
+ end
114
+ non_template_files.each { |relative_dest_path, content| stage_change(relative_dest_path, content, update_manager) }
115
+
116
+ update_manager
117
+ end
118
+
119
+ # Stages a single file into the Update Manager.
120
+ # @return [void]
121
+ # @api private
122
+ def stage_change(relative_dest_path, content, update_manager)
123
+ absolute_file_path = File.join(context.root_path, relative_dest_path)
124
+ if PDK::Util::Filesystem.exist?(absolute_file_path)
125
+ raise PDK::CLI::ExitWithError, _("Unable to generate %{object_type}; '%{file}' already exists.") % {
126
+ file: absolute_file_path,
127
+ object_type: spec_only? ? 'unit test' : friendly_name,
128
+ }
129
+ end
130
+ update_manager.add_file(absolute_file_path, content)
131
+ end
132
+
133
+ # A subclass may wish to stage files into the Update Manager, but the content is not templated. Subclasses
134
+ # can override this method to stage arbitrary files
135
+ #
136
+ # @api private
137
+ # @return [Hash{String => String}] A Hash with the relative file path as the key and the new file content as the value.
138
+ # @abstract
139
+ def non_template_files
140
+ {}
141
+ end
142
+
143
+ # Search the possible template directories in order of preference to find
144
+ # a template that can be used to render a new object of the specified
145
+ # type.
146
+ #
147
+ # @yieldparam template_paths [Hash{Symbol => String}] :object contains
148
+ # the path on disk to the template file for the object, :spec contains
149
+ # the path on disk to the template file for the tests for the object
150
+ # (if it exists).
151
+ # @yieldparam config_hash [Hash{Object => Object}] the contents of the
152
+ # :global key in the config_defaults.yml file.
153
+ #
154
+ # @raise [PDK::CLI::FatalError] if no suitable template could be found.
155
+ #
156
+ # @api private
157
+ def with_templates
158
+ require 'pdk/logger'
159
+ require 'pdk/util/template_uri'
160
+
161
+ templates.each do |template|
162
+ if template[:uri].nil?
163
+ PDK.logger.debug(_('No %{dir_type} template found; trying next template directory.') % { dir_type: template[:type] })
164
+ next
165
+ end
166
+
167
+ PDK::Template.with(PDK::Util::TemplateURI.new(template[:uri]), context) do |template_dir|
168
+ if template_files.any? { |source_file, _| template_dir.has_single_item?(source_file) }
169
+ yield template_dir
170
+ # TODO: refactor to a search-and-execute form instead
171
+ return # work is done # rubocop:disable Lint/NonLocalExitFromIterator
172
+ elsif template[:allow_fallback]
173
+ PDK.logger.debug(_('Unable to find a %{type} template in %{url}; trying next template directory.') % { type: friendly_name, url: template[:uri] })
174
+ else
175
+ raise PDK::CLI::FatalError, _('Unable to find the %{type} template in %{url}.') % { type: friendly_name, url: template[:uri] }
176
+ end
177
+ end
178
+ end
179
+ rescue ArgumentError => e
180
+ raise PDK::CLI::ExitWithError, e
181
+ end
182
+
183
+ # Provides the possible template directory locations in the order in
184
+ # which they should be searched for a valid template.
185
+ #
186
+ # If a template-url has been specified on in the options hash (e.g. from
187
+ # a CLI parameter), then this template directory will be checked first
188
+ # and we do not fall back to the next possible template directory.
189
+ #
190
+ # If we have not been provided a specific template directory to use, we
191
+ # try the template specified in the module metadata (as set during
192
+ # PDK::Generate::Module) and fall back to the default template if
193
+ # necessary.
194
+ #
195
+ # @return [Array<Hash{Symbol => Object}>] an array of hashes. Each hash
196
+ # contains 3 keys: :type contains a String that describes the template
197
+ # directory, :url contains a String with the URL to the template
198
+ # directory, and :allow_fallback contains a Boolean that specifies if
199
+ # the lookup process should proceed to the next template directory if
200
+ # the template file is not in this template directory.
201
+ #
202
+ # @api private
203
+ def templates
204
+ require 'pdk/util/template_uri'
205
+
206
+ @templates ||= PDK::Util::TemplateURI.templates(@options)
207
+ end
208
+
209
+ # Retrieves the name of the module (without the forge username) from the
210
+ # module metadata.
211
+ #
212
+ # @return [String] The name of the module.
213
+ #
214
+ # @api private
215
+ def module_name
216
+ return nil unless context.is_a?(PDK::Context::Module)
217
+
218
+ require 'pdk/util'
219
+ @module_name ||= PDK::Util.module_metadata(context.root_path)['name'].rpartition('-').last
220
+ rescue ArgumentError => e
221
+ raise PDK::CLI::FatalError, e
222
+ end
223
+
224
+ private
225
+
226
+ # Transform an object name into a ruby class name
227
+ def class_name_from_object_name(object_name)
228
+ object_name.to_s.split('_').map(&:capitalize).join
229
+ end
230
+ end
231
+ end
232
+ end