dpl 1.10.17.travis.6637.6 → 2.0.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +74 -0
  3. data/CONTRIBUTING.md +392 -0
  4. data/Gemfile +17 -3
  5. data/Gemfile.lock +373 -0
  6. data/LICENSE +16 -19
  7. data/NOTES.md +275 -0
  8. data/README.md +1977 -707
  9. data/Rakefile +2 -2
  10. data/bin/dpl +7 -3
  11. data/lib/dpl.rb +20 -0
  12. data/lib/dpl/assets/atlas/install +19 -0
  13. data/lib/dpl/assets/dpl/README.erb.md +133 -0
  14. data/lib/dpl/assets/dpl/git_ssh +2 -0
  15. data/lib/dpl/assets/git/detect_private_key +8 -0
  16. data/lib/dpl/assets/hephy/filter_log +3 -0
  17. data/lib/dpl/assets/pypi/install +4 -0
  18. data/lib/dpl/assets/scalingo/install +6 -0
  19. data/lib/dpl/cli.rb +36 -48
  20. data/lib/dpl/ctx.rb +2 -0
  21. data/lib/dpl/ctx/bash.rb +543 -0
  22. data/lib/dpl/ctx/test.rb +242 -0
  23. data/lib/dpl/helper/assets.rb +36 -0
  24. data/lib/dpl/helper/cmd.rb +167 -0
  25. data/lib/dpl/helper/config_file.rb +47 -0
  26. data/lib/dpl/helper/env.rb +39 -0
  27. data/lib/dpl/helper/interpolate.rb +126 -0
  28. data/lib/dpl/helper/memoize.rb +20 -0
  29. data/lib/dpl/helper/squiggle.rb +22 -0
  30. data/lib/dpl/helper/zip.rb +69 -0
  31. data/lib/dpl/provider.rb +562 -234
  32. data/lib/dpl/provider/dsl.rb +369 -0
  33. data/lib/dpl/provider/examples.rb +128 -0
  34. data/lib/dpl/provider/status.rb +59 -0
  35. data/lib/dpl/providers.rb +40 -0
  36. data/lib/dpl/providers/anynines.rb +65 -0
  37. data/lib/dpl/providers/atlas.rb +49 -0
  38. data/lib/dpl/providers/azure_web_apps.rb +59 -0
  39. data/lib/dpl/providers/bintray.rb +313 -0
  40. data/lib/dpl/providers/bluemixcloudfoundry.rb +92 -0
  41. data/lib/dpl/providers/boxfuse.rb +48 -0
  42. data/lib/dpl/providers/cargo.rb +19 -0
  43. data/lib/dpl/providers/chef_supermarket.rb +128 -0
  44. data/lib/dpl/providers/cloud66.rb +40 -0
  45. data/lib/dpl/providers/cloudfiles.rb +56 -0
  46. data/lib/dpl/providers/cloudfoundry.rb +81 -0
  47. data/lib/dpl/providers/codedeploy.rb +179 -0
  48. data/lib/dpl/providers/datica.rb +60 -0
  49. data/lib/dpl/providers/elasticbeanstalk.rb +195 -0
  50. data/lib/dpl/providers/engineyard.rb +107 -0
  51. data/lib/dpl/providers/firebase.rb +41 -0
  52. data/lib/dpl/providers/gae.rb +74 -0
  53. data/lib/dpl/providers/gcs.rb +105 -0
  54. data/lib/dpl/providers/hackage.rb +47 -0
  55. data/lib/dpl/providers/hephy.rb +101 -0
  56. data/lib/dpl/providers/heroku.rb +111 -0
  57. data/lib/dpl/providers/heroku/api.rb +119 -0
  58. data/lib/dpl/providers/heroku/git.rb +50 -0
  59. data/lib/dpl/providers/lambda.rb +202 -0
  60. data/lib/dpl/providers/launchpad.rb +74 -0
  61. data/lib/dpl/providers/netlify.rb +30 -0
  62. data/lib/dpl/providers/npm.rb +88 -0
  63. data/lib/dpl/providers/openshift.rb +46 -0
  64. data/lib/dpl/providers/opsworks.rb +142 -0
  65. data/lib/dpl/providers/packagecloud.rb +190 -0
  66. data/lib/dpl/providers/pages.rb +17 -0
  67. data/lib/dpl/providers/pages/api.rb +102 -0
  68. data/lib/dpl/providers/pages/git.rb +251 -0
  69. data/lib/dpl/providers/puppetforge.rb +44 -0
  70. data/lib/dpl/providers/pypi.rb +120 -0
  71. data/lib/dpl/providers/releases.rb +214 -0
  72. data/lib/dpl/providers/rubygems.rb +89 -0
  73. data/lib/dpl/providers/s3.rb +243 -0
  74. data/lib/dpl/providers/scalingo.rb +63 -0
  75. data/lib/dpl/providers/script.rb +28 -0
  76. data/lib/dpl/providers/snap.rb +59 -0
  77. data/lib/dpl/providers/surge.rb +55 -0
  78. data/lib/dpl/providers/testfairy.rb +93 -0
  79. data/lib/dpl/providers/transifex.rb +66 -0
  80. data/lib/dpl/support/aws_sdk_patch.rb +23 -0
  81. data/lib/dpl/support/gems.rb +69 -0
  82. data/lib/dpl/support/gstore_patch.rb +6 -0
  83. data/lib/dpl/support/version.rb +83 -0
  84. data/lib/dpl/version.rb +2 -2
  85. metadata +98 -169
  86. data/.coveralls.yml +0 -1
  87. data/.github/CONTRIBUTING.md +0 -173
  88. data/.github/stale.yml +0 -53
  89. data/.gitignore +0 -13
  90. data/.rspec +0 -2
  91. data/.travis.yml +0 -56
  92. data/dpl-anynines.gemspec +0 -3
  93. data/dpl-atlas.gemspec +0 -3
  94. data/dpl-azure_webapps.gemspec +0 -3
  95. data/dpl-bintray.gemspec +0 -3
  96. data/dpl-bitballoon.gemspec +0 -3
  97. data/dpl-bluemix_cloud_foundry.gemspec +0 -3
  98. data/dpl-boxfuse.gemspec +0 -3
  99. data/dpl-cargo.gemspec +0 -3
  100. data/dpl-catalyze.gemspec +0 -3
  101. data/dpl-chef_supermarket.gemspec +0 -20
  102. data/dpl-cloud66.gemspec +0 -3
  103. data/dpl-cloud_files.gemspec +0 -3
  104. data/dpl-cloud_foundry.gemspec +0 -3
  105. data/dpl-code_deploy.gemspec +0 -3
  106. data/dpl-deis.gemspec +0 -3
  107. data/dpl-elastic_beanstalk.gemspec +0 -3
  108. data/dpl-engine_yard.gemspec +0 -3
  109. data/dpl-firebase.gemspec +0 -3
  110. data/dpl-gae.gemspec +0 -3
  111. data/dpl-gcs.gemspec +0 -3
  112. data/dpl-hackage.gemspec +0 -3
  113. data/dpl-hephy.gemspec +0 -3
  114. data/dpl-heroku.gemspec +0 -3
  115. data/dpl-lambda.gemspec +0 -3
  116. data/dpl-launchpad.gemspec +0 -3
  117. data/dpl-npm.gemspec +0 -3
  118. data/dpl-openshift.gemspec +0 -3
  119. data/dpl-ops_works.gemspec +0 -3
  120. data/dpl-packagecloud.gemspec +0 -3
  121. data/dpl-pages.gemspec +0 -3
  122. data/dpl-puppet_forge.gemspec +0 -3
  123. data/dpl-pypi.gemspec +0 -3
  124. data/dpl-releases.gemspec +0 -8
  125. data/dpl-rubygems.gemspec +0 -3
  126. data/dpl-s3.gemspec +0 -3
  127. data/dpl-scalingo.gemspec +0 -3
  128. data/dpl-script.gemspec +0 -3
  129. data/dpl-snap.gemspec +0 -3
  130. data/dpl-surge.gemspec +0 -3
  131. data/dpl-testfairy.gemspec +0 -3
  132. data/dpl-transifex.gemspec +0 -3
  133. data/dpl.gemspec +0 -3
  134. data/gemspec_helper.rb +0 -51
  135. data/lib/dpl/error.rb +0 -3
  136. data/notes/engine_yard.md +0 -1
  137. data/notes/heroku.md +0 -3
  138. data/spec/cli_spec.rb +0 -36
  139. data/spec/provider_spec.rb +0 -191
  140. data/spec/spec_helper.rb +0 -20
@@ -0,0 +1,369 @@
1
+ require 'dpl/provider/status'
2
+
3
+ module Dpl
4
+ class Provider < Cl::Cmd
5
+ # DSL available on the provider's class body.
6
+ #
7
+ # Use this to declare various features, requirements, and attributes that
8
+ # apply to your provider.
9
+ module Dsl
10
+ include Squiggle
11
+
12
+ # Declare the full name of the provider. Required if the proper provider
13
+ # name does not match the provider's class name.
14
+ #
15
+ # @param name [String] The provider's full name
16
+ # @return The previously declared full name if no argument is given
17
+ def full_name(name = nil)
18
+ name ? @full_name = name : @full_name || self.name.split('::').last
19
+ end
20
+
21
+ # Summary of the provider's functionality.
22
+ def summary(summary = nil)
23
+ summary ? super : @summary || "#{full_name} deployment provider"
24
+ end
25
+
26
+ # Summary of the provider's functionality.
27
+ def description(str = nil)
28
+ str << status.msg if str && status && status.announce?
29
+ super
30
+ end
31
+
32
+ # Set or read the provider's maturity status with an optional message
33
+ def status(status = nil, msg = nil)
34
+ status ? @status = Status.new(self, status, msg) : @status
35
+ end
36
+
37
+ # @!method env
38
+ # Declare an environment variable prefix to accept env vars as options
39
+ #
40
+ # This method is defined in `Env::ClassMethods`.
41
+ #
42
+ # Declares an environment variable prefix that imports environment
43
+ # variables into `opts` if they match declared options.
44
+ #
45
+ # For example, with the following declaration on the class body:
46
+ #
47
+ # ```ruby
48
+ # env :aws
49
+ # opt '--access_key_id ID'
50
+ # ```
51
+ #
52
+ # if the environment variable `AWS_ACCESS_KEY_ID` is set then the option
53
+ # `opts[:access_key_id]` will default to the value given on that
54
+ # variable (i.e. it could still be overwritten by the user by passing
55
+ # the `--access_key_id` option).
56
+
57
+ # @!method opt
58
+ # Declare command line options that the provider supports.
59
+ #
60
+ # This method is inherited from the base class `Cl::Cmd` which is defined
61
+ # in the Rubygem `Cl`. See the gem's documentation for details on how
62
+ # to declare command line options.
63
+ #
64
+ # @see https://github.com/svenfuchs/cl
65
+
66
+ def path(path)
67
+ ENV['PATH'] = "#{File.expand_path(path)}:#{ENV['PATH']}"
68
+ end
69
+
70
+ def move(*paths)
71
+ paths.any? ? @move = paths : @move ||= []
72
+ end
73
+
74
+ def node_js(*requirements)
75
+ runtimes(:node_js, requirements)
76
+ end
77
+
78
+ def python(*requirements)
79
+ runtimes(:python, requirements)
80
+ end
81
+
82
+ def runtimes(name = nil, requirements = nil)
83
+ return @runtimes ||= [] unless name
84
+ runtimes << [name, requirements]
85
+ end
86
+
87
+ # Declare APT packages the provider depends on. These will be installed
88
+ # during the `before_install` stage using `apt-get install`, unless the
89
+ # given cmd is already available according to `which [cmd]`.
90
+ #
91
+ # @param package [String] Package name (required).
92
+ # @param cmd [String] Executable command installed by that package (optional, defaults to the package name).
93
+ #
94
+ # @return Previously declared apt packages if no arguments were given.
95
+ def apt(package = nil, cmd = nil)
96
+ return apt << [package, cmd].compact if package
97
+ @apt ||= self == Provider ? [] : superclass.apt.dup
98
+ end
99
+
100
+ # Whether or not the provider depends on any apt packages.
101
+ def apt?
102
+ apt.any?
103
+ end
104
+
105
+ # Declare additional paths to Ruby gem source code that this provider
106
+ # requires.
107
+ #
108
+ # These gems will be installed, and files required at runtime, during the
109
+ # `before_init` stage (not at install time, and/or load time), unless they
110
+ # are already installed.
111
+ #
112
+ # @param name [String] Ruby gem name (required)
113
+ # @param version [String] Ruby gem version (required)
114
+ # @param opts [Hash] options
115
+ # @option opts [Array<String>, String] :require A single path or a list of paths to source files to require from this Ruby gem. If not given the name of the gem will be assumed to be the path to be required.
116
+ #
117
+ # @return Previously declared gems if no arguments were given
118
+ def gem(name = nil, version = nil, opts = {})
119
+ return gem << [name, version, opts] if name
120
+ @gem ||= self == Provider ? [] : superclass.gem.dup
121
+ end
122
+
123
+ def gem?
124
+ gem.any?
125
+ end
126
+
127
+ # Declare NPM packages the provider depends on. These will be installed
128
+ # during the `before_install` stage using `npm install -g`, unless the
129
+ # given cmd is already available according to `which [cmd]`.
130
+ #
131
+ # @param package [String] Package name (required).
132
+ # @param cmd [String] Executable command installed by that package (optional, defaults to the package name).
133
+ #
134
+ # @return Previously declared NPM packages if no arguments are given.
135
+ def npm(package = nil, cmd = nil)
136
+ return npm << [package, cmd].compact if package
137
+ @npm ||= self == Provider ? [] : superclass.npm.dup
138
+ end
139
+
140
+ # Whether or not the provider depends on any NPM packages.
141
+ def npm?
142
+ npm.any?
143
+ end
144
+
145
+ # Declare Python packages the provider depends on. These will be installed
146
+ # during the `before_install` stage using `pip install --user`. A previously
147
+ # installed package is uninstalled before that, but only if `version` was
148
+ # given.
149
+ #
150
+ # @param package [String] Package name (required).
151
+ # @param cmd [String] Executable command installed by that package (optional, defaults to the package name).
152
+ # @param version [String] Package version (optional).
153
+ #
154
+ # @return Previously declared Python packages if no arguments are given.
155
+ def pip(package = nil, cmd = nil, version = nil)
156
+ return pip << [package, cmd, version].compact if package
157
+ @pip ||= self == Provider ? [] : superclass.pip.dup
158
+ end
159
+
160
+ # Whether or not the provider depends on any Python packages.
161
+ def pip?
162
+ pip.any?
163
+ end
164
+
165
+ # Declare shell commands used by the provider.
166
+ #
167
+ # This exists so shell commands used can be separated from the
168
+ # implementation that runs them. This is useful in order to easily get an
169
+ # overview of all shell commands used by a provider on one hand, and in
170
+ # order to keep the implementation code focussed on the logic and
171
+ # functionality it provides, rather than the details of (potentially long
172
+ # winded) shell commands.
173
+ #
174
+ # For example, a shell command declared on the class body like so:
175
+ #
176
+ # ```ruby
177
+ # cmds git_push: 'git push -f %{target}'
178
+ # ```
179
+ #
180
+ # can be used in the deploy stage like so:
181
+ #
182
+ # ```ruby
183
+ # def deploy
184
+ # shell :git_push
185
+ # end
186
+ # ```
187
+ #
188
+ # The variable `%{target}` will be interpolated by calling the method
189
+ # `target` on the provider instance, so it will expect that method to
190
+ # exist.
191
+ #
192
+ # @param cmds [Hash] Commands to declare.
193
+ # @return Previously declared cmds if no argument is given.
194
+ #
195
+ # @see Dpl::Ctx::Bash#shell Ctx::Bash#shell for more details on how to call shell
196
+ # commands.
197
+ def cmds(cmds = nil)
198
+ return self.cmds.update(cmds) if cmds
199
+ @cmds ||= self == Provider ? {} : superclass.cmds.dup
200
+ end
201
+
202
+ # Declare error messages that are raised if a shell command fails.
203
+ #
204
+ # This exists so error messages can be separated from the implementation
205
+ # that uses them. This is useful in order to easily get an overview of
206
+ # all error messages used by a provider on one hand, and in order to keep
207
+ # the implementation code focussed on the logic and functionality it
208
+ # provides, rather than the details of (potentially long winded) error
209
+ # message strings.
210
+ #
211
+ # The method `shell` will raise an error if the given shell command fails
212
+ # (returns a non-zero exit code) unless it is called with the option
213
+ # `assert: false`. The error message declared using `errs` will be used
214
+ # to raise with the eror.
215
+ #
216
+ # For example, an error message declared on the class body like so:
217
+ #
218
+ # ```ruby
219
+ # errs git_push: 'Failed to push to %{target}'
220
+ # ```
221
+ #
222
+ # will be included to the raised error if the given command has failed:
223
+ #
224
+ # ```ruby
225
+ # def deploy
226
+ # shell :git_push
227
+ # end
228
+ # ```
229
+ #
230
+ # The variable `%{target}` will be interpolated by calling the method
231
+ # `target` on the provider instance, so it will expect that method to
232
+ # exist.
233
+ #
234
+ # @param errs [Hash] Error messages to declare.
235
+ # @return Previously declared errs if no argument is given.
236
+ #
237
+ # See Dpl::Ctx::Bash#shell for more details on how to call shell
238
+ # commands.
239
+ def errs(errs = nil)
240
+ return self.errs.update(errs) if errs
241
+ @errs ||= self == Provider ? {} : superclass.errs.dup
242
+ end
243
+
244
+ # Declare other messages, such as info level log output, warnings, or
245
+ # custom strings, such as commit messages or descriptions.
246
+ #
247
+ # This exists so various messages can be separated from the
248
+ # implementation that uses them. This is useful in order to easily get an
249
+ # overview of all error messages used by a provider on one hand, and in
250
+ # order to keep the implementation code focussed on the logic and
251
+ # functionality it provides, rather than the details of (potentially long
252
+ # winded) message strings.
253
+ #
254
+ # For example, a message declared on the class body like so:
255
+ #
256
+ # ```ruby
257
+ # msgs login: 'Logging in to the service %{full_name}'
258
+ # ```
259
+ #
260
+ # could be used by the implementation like so:
261
+ #
262
+ # ```ruby
263
+ # def login
264
+ # info :login
265
+ # end
266
+ # ```
267
+ #
268
+ # The variable `%{full_name}` will be interpolated by calling the method
269
+ # `full_name` on the provider instance, so it will expect that method to
270
+ # exist.
271
+ #
272
+ # It is possible to use msgs in order to declare and use custom messages,
273
+ # e.g. for the commit message on a commit a provider needs to create, or
274
+ # a description that needs to be included to an API call.
275
+ #
276
+ # For example, a message declared on the class body like so:
277
+ #
278
+ # ```ruby
279
+ # cmds git_commit: 'git commit -am "%{commit_msg}"'
280
+ # msgs commit_msg: 'Commit build artifacts on build %{build_number}'
281
+ # ```
282
+ #
283
+ # could be used by the implementation like so:
284
+ #
285
+ # ```ruby
286
+ # def create_commit
287
+ # shell :git_commit
288
+ # end
289
+ #
290
+ # def commit_msg
291
+ # interpolate(msg(:commit_msg))
292
+ # end
293
+ # ```
294
+ #
295
+ # Note that in cases where builtin methods such as `shell`, `info`,
296
+ # `warn` etc. are not used the method `interpolate` needs to be used in
297
+ # order to interpolate variables used in a message (if any).
298
+ #
299
+ # @param msgs [Hash] Messages to declare.
300
+ # @return Previously declared msgs if no argument is given.
301
+ def msgs(msgs = nil)
302
+ return self.msgs.update(msgs) if msgs
303
+ @msgs ||= self == Provider ? {} : superclass.msgs.dup
304
+ end
305
+
306
+ def strs(strs = nil)
307
+ return self.strs.update(strs) if strs
308
+ @strs ||= self == Provider ? {} : superclass.strs.dup
309
+ end
310
+
311
+ # Declare artifacts, such as executables during the `install` stage that
312
+ # need to be kept during `cleanup`.
313
+ #
314
+ # @param paths [String] Paths to artifacts to keep during `cleanup`
315
+ # @return Previously declared artifacts to keep if no argument is given.
316
+ def keep(*paths)
317
+ return keep.concat(paths) if paths.any?
318
+ @keep ||= self == Provider ? [] : superclass.keep.dup
319
+ end
320
+
321
+ # Declare features that the provider needs.
322
+ #
323
+ # Known features currently are:
324
+ #
325
+ # * `ssh_key`: Generates a temporary, per-build SSH key, and calls the
326
+ # methods `add_key` and `remove_key` if the provider defines them.
327
+ # This gives providers the opportunity to install this key on their
328
+ # service, and remove it after the deployment has finished.
329
+ # * `git`: Populates the git config.user and config.email attributes,
330
+ # unless present.
331
+ # * `git_http_user_agent`: Changes the environment variable
332
+ # `GIT_HTTP_USER_AGENT` to the one generated by `user_agent`. This
333
+ # gives providers the opportunity to identify and track coming from
334
+ # Travis CI and/or dpl.
335
+ #
336
+ # @param features [Symbol] Features to activate for this provider
337
+ # @return Previously declared features needed if no argument is given.
338
+ def needs(*features)
339
+ return needs.concat(features) if features.any?
340
+ @needs ||= self == Provider ? [] : superclass.needs.dup
341
+ end
342
+
343
+ # Whether or not the provider has declared any features it needs.
344
+ def needs?(feature)
345
+ needs.include?(feature)
346
+ end
347
+
348
+ # Generates a useragent string that identifies the current dpl version,
349
+ # and whether it runs int he context of Travis CI. Can include arbitrary
350
+ # extra strings or key value pairs (passed as String or Hash arguments).
351
+ # @param strs [String(s) or Hash(es)] Additional strings or key value pairs to include to the useragent string.
352
+ # @return [String] The useragent string
353
+ def user_agent(*strs)
354
+ strs.unshift "dpl/#{Dpl::VERSION}"
355
+ strs.unshift 'travis/0.1.0' if ENV['TRAVIS']
356
+ strs = strs.flat_map { |e| Hash === e ? e.map { |k, v| "#{k}/#{v}" } : e }
357
+ strs.join(' ').gsub(/\s+/, ' ').strip
358
+ end
359
+
360
+ def ruby_version
361
+ Gem::Version.new(RUBY_VERSION)
362
+ end
363
+
364
+ def ruby_pre?(version)
365
+ ruby_version < Gem::Version.new(version)
366
+ end
367
+ end
368
+ end
369
+ end
@@ -0,0 +1,128 @@
1
+ module Dpl
2
+ class Examples < Struct.new(:const)
3
+ def cmds
4
+ examples.map(&:cmd).join("\n")
5
+ end
6
+
7
+ def full_config
8
+ full.config
9
+ end
10
+
11
+ def configs
12
+ examples.map(&:config)
13
+ end
14
+
15
+ def examples
16
+ [requireds, required, many].flatten.compact.uniq
17
+ end
18
+
19
+ def requireds
20
+ requireds_opts.map { |opts| example(opts) }
21
+ end
22
+
23
+ def required
24
+ opts = required_opts
25
+ example(opts)
26
+ end
27
+
28
+ def many
29
+ opts = const.opts.opts
30
+ opts = order(opts)
31
+ opts = without_required(opts)
32
+ opts = with_required(opts)
33
+ opts = filter(opts)
34
+ opts = opts[0, 5]
35
+ example(opts)
36
+ end
37
+
38
+ def full
39
+ opts = const.opts.opts
40
+ opts = filter(opts)
41
+ example(opts)
42
+ end
43
+
44
+ def filter(opts)
45
+ opts = opts.reject(&:internal?)
46
+ opts.reject { |opt| opt.name == :help }
47
+ end
48
+
49
+ def order(opts)
50
+ cmmn = const.superclass.opts.opts
51
+ opts - cmmn + cmmn
52
+ end
53
+
54
+ def with_required(opts)
55
+ requireds = requireds_opts.first || []
56
+ opts = requireds + required_opts + opts
57
+ opts.uniq
58
+ end
59
+
60
+ def without_required(opts)
61
+ opts = opts - const.required.flatten.map { |key| const.opts[key] }
62
+ opts - required_opts.map(&:opts)
63
+ end
64
+
65
+ def example(opts)
66
+ return unless opts.any?
67
+ opts = required_opts.concat(opts).uniq.compact
68
+ Example.new(const, opts)
69
+ end
70
+
71
+ def requireds_opts
72
+ opts = const.required.flatten(1)
73
+ opts.map { |keys| Array(keys).map { |key| const.opts[key] } }
74
+ end
75
+
76
+ def required_opts
77
+ const.opts.select(&:required?)
78
+ end
79
+ end
80
+
81
+ class Example < Struct.new(:const, :opts)
82
+ def config
83
+ config = opts_for(opts)
84
+ config = config.merge(strategy: strategy) # hmm.
85
+ compact(config)
86
+ end
87
+
88
+ def strategy
89
+ const.registry_key.to_s.split(':').last if const.registry_key.to_s.include?(':')
90
+ end
91
+
92
+ def cmd
93
+ "dpl #{name} #{strs_for(opts)}"
94
+ end
95
+
96
+ def ==(other)
97
+ const == other.const && opts == other.opts
98
+ end
99
+
100
+ def name
101
+ const.registry_key.to_s.split(':').join(' ')
102
+ end
103
+
104
+ def opts_for(opts)
105
+ opts.map { |opt| [opt.name, value_for(opt)] }.to_h
106
+ end
107
+
108
+ def strs_for(opts)
109
+ opts.map { |opt| str_for(opt) }.join(' ')
110
+ end
111
+
112
+ def str_for(opt)
113
+ "--#{opt.name} #{value_for(opt)}".strip
114
+ end
115
+
116
+ def value_for(opt)
117
+ return if opt.type == :flag
118
+ return 1 if opt.type == :integer
119
+ return opt.enum.first if opt.enum?
120
+ str = opt.strs.detect { |str| str =~ /^--#{opt.name} (.*)$/ } && $1
121
+ str ? str.downcase : 'str'
122
+ end
123
+
124
+ def compact(hash)
125
+ hash.reject { |_, value| value.nil? }
126
+ end
127
+ end
128
+ end