rfix 1.4.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (219) hide show
  1. checksums.yaml +4 -4
  2. data/exe/rfix +78 -34
  3. data/lib/rfix.rb +17 -28
  4. data/lib/rfix/branch.rb +3 -25
  5. data/lib/rfix/branch/base.rb +27 -0
  6. data/lib/rfix/branch/head.rb +15 -0
  7. data/lib/rfix/branch/main.rb +33 -0
  8. data/lib/rfix/branch/name.rb +21 -0
  9. data/lib/rfix/branch/reference.rb +15 -0
  10. data/lib/rfix/branch/upstream.rb +17 -0
  11. data/lib/rfix/cli/command.rb +19 -0
  12. data/lib/rfix/cli/command/base.rb +61 -0
  13. data/lib/rfix/cli/command/branch.rb +13 -0
  14. data/lib/rfix/cli/command/config.rb +22 -0
  15. data/lib/rfix/cli/command/extension.rb +25 -0
  16. data/lib/rfix/cli/command/help.rb +11 -0
  17. data/lib/rfix/cli/command/info.rb +11 -0
  18. data/lib/rfix/cli/command/lint.rb +17 -0
  19. data/lib/rfix/cli/command/local.rb +11 -0
  20. data/lib/rfix/cli/command/origin.rb +11 -0
  21. data/lib/rfix/cli/command/setup.rb +11 -0
  22. data/lib/rfix/error.rb +5 -1
  23. data/lib/rfix/extension/offense.rb +79 -0
  24. data/lib/rfix/extension/pastel.rb +11 -0
  25. data/lib/rfix/extension/string.rb +12 -0
  26. data/lib/rfix/extension/strings.rb +9 -0
  27. data/lib/rfix/file.rb +6 -41
  28. data/lib/rfix/file/base.rb +73 -0
  29. data/lib/rfix/file/deleted.rb +17 -0
  30. data/lib/rfix/file/ignored.rb +17 -0
  31. data/lib/rfix/file/tracked.rb +42 -0
  32. data/lib/rfix/file/untracked.rb +20 -0
  33. data/lib/rfix/formatter.rb +125 -86
  34. data/lib/rfix/highlighter.rb +118 -0
  35. data/lib/rfix/indicator.rb +19 -0
  36. data/lib/rfix/log.rb +12 -121
  37. data/lib/rfix/rake/gemfile.rb +111 -0
  38. data/lib/rfix/rake/paths.rb +25 -23
  39. data/lib/rfix/rake/support.rb +72 -57
  40. data/lib/rfix/repository.rb +114 -164
  41. data/lib/rfix/types.rb +52 -0
  42. data/lib/rfix/version.rb +1 -1
  43. data/rfix.gemspec +28 -38
  44. data/vendor/dry-cli/CHANGELOG.md +191 -0
  45. data/vendor/dry-cli/CODEOWNERS +1 -0
  46. data/vendor/dry-cli/CODE_OF_CONDUCT.md +13 -0
  47. data/vendor/dry-cli/CONTRIBUTING.md +29 -0
  48. data/vendor/dry-cli/Gemfile +14 -0
  49. data/vendor/dry-cli/Gemfile.devtools +18 -0
  50. data/vendor/dry-cli/LICENSE +20 -0
  51. data/vendor/dry-cli/README.md +29 -0
  52. data/vendor/dry-cli/Rakefile +13 -0
  53. data/vendor/dry-cli/bin/console +15 -0
  54. data/vendor/dry-cli/bin/setup +8 -0
  55. data/vendor/dry-cli/changelog.yml +97 -0
  56. data/vendor/dry-cli/docsite/source/arguments.html.md +57 -0
  57. data/vendor/dry-cli/docsite/source/callbacks.html.md +51 -0
  58. data/vendor/dry-cli/docsite/source/commands-with-subcommands-and-params.md +86 -0
  59. data/vendor/dry-cli/docsite/source/commands.html.md +41 -0
  60. data/vendor/dry-cli/docsite/source/index.html.md +302 -0
  61. data/vendor/dry-cli/docsite/source/options.html.md +51 -0
  62. data/vendor/dry-cli/docsite/source/subcommands.html.md +38 -0
  63. data/vendor/dry-cli/docsite/source/variadic-arguments.html.md +45 -0
  64. data/vendor/dry-cli/dry-cli.gemspec +36 -0
  65. data/vendor/dry-cli/lib/dry/cli.rb +224 -0
  66. data/vendor/dry-cli/lib/dry/cli/banner.rb +135 -0
  67. data/vendor/dry-cli/lib/dry/cli/command.rb +387 -0
  68. data/vendor/dry-cli/lib/dry/cli/command_registry.rb +253 -0
  69. data/vendor/dry-cli/lib/dry/cli/errors.rb +37 -0
  70. data/vendor/dry-cli/lib/dry/cli/inflector.rb +17 -0
  71. data/vendor/dry-cli/lib/dry/cli/inline.rb +75 -0
  72. data/vendor/dry-cli/lib/dry/cli/option.rb +131 -0
  73. data/vendor/dry-cli/lib/dry/cli/parser.rb +138 -0
  74. data/vendor/dry-cli/lib/dry/cli/program_name.rb +21 -0
  75. data/vendor/dry-cli/lib/dry/cli/registry.rb +338 -0
  76. data/vendor/dry-cli/lib/dry/cli/usage.rb +94 -0
  77. data/vendor/dry-cli/lib/dry/cli/version.rb +8 -0
  78. data/vendor/dry-cli/project.yml +13 -0
  79. data/vendor/dry-cli/spec/integration/commands_spec.rb +14 -0
  80. data/vendor/dry-cli/spec/integration/inherited_commands_spec.rb +24 -0
  81. data/vendor/dry-cli/spec/integration/inline_spec.rb +43 -0
  82. data/vendor/dry-cli/spec/integration/processes_errors_spec.rb +29 -0
  83. data/vendor/dry-cli/spec/integration/rendering_spec.rb +31 -0
  84. data/vendor/dry-cli/spec/integration/single_command_spec.rb +81 -0
  85. data/vendor/dry-cli/spec/integration/subcommands_spec.rb +60 -0
  86. data/vendor/dry-cli/spec/integration/third_party_gems_spec.rb +18 -0
  87. data/vendor/dry-cli/spec/spec_helper.rb +15 -0
  88. data/vendor/dry-cli/spec/support/coverage.rb +15 -0
  89. data/vendor/dry-cli/spec/support/files.rb +13 -0
  90. data/vendor/dry-cli/spec/support/fixtures/based +65 -0
  91. data/vendor/dry-cli/spec/support/fixtures/baz +9 -0
  92. data/vendor/dry-cli/spec/support/fixtures/baz_command.rb +19 -0
  93. data/vendor/dry-cli/spec/support/fixtures/foo +588 -0
  94. data/vendor/dry-cli/spec/support/fixtures/infinites +31 -0
  95. data/vendor/dry-cli/spec/support/fixtures/inline +20 -0
  96. data/vendor/dry-cli/spec/support/fixtures/registry.rb +15 -0
  97. data/vendor/dry-cli/spec/support/fixtures/shared_commands.rb +596 -0
  98. data/vendor/dry-cli/spec/support/fixtures/with_block.rb +86 -0
  99. data/vendor/dry-cli/spec/support/fixtures/with_registry.rb +90 -0
  100. data/vendor/dry-cli/spec/support/fixtures/with_zero_arity_block.rb +87 -0
  101. data/vendor/dry-cli/spec/support/helpers.rb +37 -0
  102. data/vendor/dry-cli/spec/support/path.rb +24 -0
  103. data/vendor/dry-cli/spec/support/rspec.rb +26 -0
  104. data/vendor/dry-cli/spec/support/rspec_options.rb +16 -0
  105. data/vendor/dry-cli/spec/support/shared_examples/commands.rb +300 -0
  106. data/vendor/dry-cli/spec/support/shared_examples/inherited_commands.rb +197 -0
  107. data/vendor/dry-cli/spec/support/shared_examples/rendering.rb +181 -0
  108. data/vendor/dry-cli/spec/support/shared_examples/subcommands.rb +226 -0
  109. data/vendor/dry-cli/spec/support/shared_examples/third_party_gems.rb +49 -0
  110. data/vendor/dry-cli/spec/support/warnings.rb +10 -0
  111. data/vendor/dry-cli/spec/unit/dry/cli/cli_spec.rb +123 -0
  112. data/vendor/dry-cli/spec/unit/dry/cli/inflector_spec.rb +26 -0
  113. data/vendor/dry-cli/spec/unit/dry/cli/registry_spec.rb +78 -0
  114. data/vendor/dry-cli/spec/unit/dry/cli/version_spec.rb +7 -0
  115. data/vendor/strings-ansi/CHANGELOG.md +24 -0
  116. data/vendor/strings-ansi/CODE_OF_CONDUCT.md +74 -0
  117. data/vendor/strings-ansi/Gemfile +11 -0
  118. data/{LICENSE.txt → vendor/strings-ansi/LICENSE.txt} +1 -1
  119. data/vendor/strings-ansi/README.md +155 -0
  120. data/vendor/strings-ansi/Rakefile +8 -0
  121. data/vendor/strings-ansi/appveyor.yml +32 -0
  122. data/vendor/strings-ansi/bin/console +14 -0
  123. data/vendor/strings-ansi/bin/setup +8 -0
  124. data/vendor/strings-ansi/lib/strings-ansi.rb +1 -0
  125. data/vendor/strings-ansi/lib/strings/ansi.rb +84 -0
  126. data/vendor/strings-ansi/lib/strings/ansi/extensions.rb +23 -0
  127. data/vendor/strings-ansi/lib/strings/ansi/version.rb +7 -0
  128. data/vendor/strings-ansi/spec/fixtures/ansi_codes.yaml +194 -0
  129. data/vendor/strings-ansi/spec/spec_helper.rb +51 -0
  130. data/vendor/strings-ansi/spec/unit/ansi_spec.rb +15 -0
  131. data/vendor/strings-ansi/spec/unit/extensions_spec.rb +19 -0
  132. data/vendor/strings-ansi/spec/unit/only_ansi_spec.rb +36 -0
  133. data/vendor/strings-ansi/spec/unit/sanitize_spec.rb +53 -0
  134. data/vendor/strings-ansi/strings-ansi.gemspec +34 -0
  135. data/vendor/strings-ansi/tasks/console.rake +11 -0
  136. data/vendor/strings-ansi/tasks/coverage.rake +11 -0
  137. data/vendor/strings-ansi/tasks/spec.rake +29 -0
  138. metadata +274 -188
  139. data/.github/workflows/main.yml +0 -26
  140. data/.gitignore +0 -43
  141. data/.rspec +0 -2
  142. data/.rubocop.yml +0 -87
  143. data/.travis.yml +0 -35
  144. data/Gemfile +0 -2
  145. data/Gemfile.base +0 -14
  146. data/Gemfile.base.lock +0 -172
  147. data/Gemfile.lock +0 -188
  148. data/Guardfile +0 -16
  149. data/Makefile +0 -12
  150. data/README.md +0 -85
  151. data/Rakefile +0 -31
  152. data/bin/bundle +0 -114
  153. data/bin/console +0 -29
  154. data/bin/guard +0 -29
  155. data/bin/rake +0 -29
  156. data/bin/rfix +0 -29
  157. data/bin/rspec +0 -29
  158. data/bin/setup +0 -29
  159. data/ci/Gemfile.rubocop-0.80 +0 -2
  160. data/ci/Gemfile.rubocop-0.80.lock +0 -170
  161. data/ci/Gemfile.rubocop-0.81 +0 -2
  162. data/ci/Gemfile.rubocop-0.81.lock +0 -170
  163. data/ci/Gemfile.rubocop-0.82 +0 -2
  164. data/ci/Gemfile.rubocop-0.82.lock +0 -170
  165. data/ci/Gemfile.rubocop-0.83 +0 -2
  166. data/ci/Gemfile.rubocop-0.83.lock +0 -168
  167. data/ci/Gemfile.rubocop-0.84 +0 -2
  168. data/ci/Gemfile.rubocop-0.84.lock +0 -171
  169. data/ci/Gemfile.rubocop-0.85 +0 -2
  170. data/ci/Gemfile.rubocop-0.85.1 +0 -2
  171. data/ci/Gemfile.rubocop-0.85.1.lock +0 -173
  172. data/ci/Gemfile.rubocop-0.85.lock +0 -173
  173. data/lib/rfix/box.rb +0 -112
  174. data/lib/rfix/branches/base.rb +0 -15
  175. data/lib/rfix/branches/head.rb +0 -13
  176. data/lib/rfix/branches/main.rb +0 -28
  177. data/lib/rfix/branches/name.rb +0 -23
  178. data/lib/rfix/branches/reference.rb +0 -21
  179. data/lib/rfix/branches/upstream.rb +0 -13
  180. data/lib/rfix/cmd.rb +0 -39
  181. data/lib/rfix/commands/branch.rb +0 -15
  182. data/lib/rfix/commands/extensions/options.rb +0 -8
  183. data/lib/rfix/commands/help.rb +0 -7
  184. data/lib/rfix/commands/helper/args.rb +0 -141
  185. data/lib/rfix/commands/helper/help.rb +0 -6
  186. data/lib/rfix/commands/helper/loader.rb +0 -6
  187. data/lib/rfix/commands/helper/option.rb +0 -0
  188. data/lib/rfix/commands/helper/params.rb +0 -0
  189. data/lib/rfix/commands/helper/rubocop.rb +0 -17
  190. data/lib/rfix/commands/info.rb +0 -30
  191. data/lib/rfix/commands/lint.rb +0 -22
  192. data/lib/rfix/commands/local.rb +0 -12
  193. data/lib/rfix/commands/origin.rb +0 -19
  194. data/lib/rfix/commands/setup.rb +0 -29
  195. data/lib/rfix/commands/welcome.rb +0 -24
  196. data/lib/rfix/deleted.rb +0 -13
  197. data/lib/rfix/extensions/extensions.rb +0 -18
  198. data/lib/rfix/extensions/offense.rb +0 -78
  199. data/lib/rfix/extensions/string.rb +0 -8
  200. data/lib/rfix/file_cache.rb +0 -59
  201. data/lib/rfix/git_helper.rb +0 -59
  202. data/lib/rfix/indentation.rb +0 -39
  203. data/lib/rfix/loader/bundler.rb +0 -37
  204. data/lib/rfix/loader/env.rb +0 -33
  205. data/lib/rfix/loader/spec.rb +0 -41
  206. data/lib/rfix/no_file.rb +0 -13
  207. data/lib/rfix/rfix.rb +0 -34
  208. data/lib/rfix/tracked.rb +0 -72
  209. data/lib/rfix/tracked_file.rb +0 -16
  210. data/lib/rfix/untracked.rb +0 -13
  211. data/resources/ps.png +0 -0
  212. data/tasks/bump.rake +0 -11
  213. data/tasks/bundle.rake +0 -17
  214. data/tasks/complex.rake +0 -54
  215. data/tasks/execute.rake +0 -38
  216. data/tasks/libgit2.rake +0 -33
  217. data/tasks/simple.rake +0 -62
  218. data/tasks/travis.rake +0 -74
  219. data/tasks/vendor.rake +0 -34
@@ -0,0 +1,387 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+ require "dry/cli/option"
5
+
6
+ module Dry
7
+ class CLI
8
+ # Base class for commands
9
+ #
10
+ # @since 0.1.0
11
+ class Command
12
+ # @since 0.1.0
13
+ # @api private
14
+ def self.inherited(base)
15
+ super
16
+ base.class_eval do
17
+ @_mutex = Mutex.new
18
+ @description = nil
19
+ @examples = []
20
+ @subcommands = []
21
+ @arguments = base.superclass_arguments || []
22
+ @options = base.superclass_options || []
23
+ end
24
+ base.extend ClassMethods
25
+ end
26
+
27
+ # @since 0.1.0
28
+ # @api private
29
+ module ClassMethods
30
+ # @since 0.1.0
31
+ # @api private
32
+ attr_reader :description
33
+
34
+ # @since 0.1.0
35
+ # @api private
36
+ attr_reader :examples
37
+
38
+ # @since 0.1.0
39
+ # @api private
40
+ attr_reader :arguments
41
+
42
+ # @since 0.1.0
43
+ # @api private
44
+ attr_reader :options
45
+
46
+ # @since x.x.x
47
+ # @api private
48
+ attr_accessor :subcommands
49
+
50
+ # @since x.x.x
51
+ # @api private
52
+ end
53
+
54
+ # Set the description of the command
55
+ #
56
+ # @param description [String] the description
57
+ #
58
+ # @since 0.1.0
59
+ #
60
+ # @example
61
+ # require "dry/cli"
62
+ #
63
+ # class Echo < Dry::CLI::Command
64
+ # desc "Prints given input"
65
+ #
66
+ # def call(*)
67
+ # # ...
68
+ # end
69
+ # end
70
+ def self.desc(description)
71
+ @description = description
72
+ end
73
+
74
+ # Describe the usage of the command
75
+ #
76
+ # @param examples [Array<String>] one or more examples
77
+ #
78
+ # @since 0.1.0
79
+ #
80
+ # @example
81
+ # require "dry/cli"
82
+ #
83
+ # class Server < Dry::CLI::Command
84
+ # example [
85
+ # " # Basic usage (it uses the bundled server engine)",
86
+ # "--server=webrick # Force `webrick` server engine",
87
+ # "--host=0.0.0.0 # Bind to a host",
88
+ # "--port=2306 # Bind to a port",
89
+ # "--no-code-reloading # Disable code reloading"
90
+ # ]
91
+ #
92
+ # def call(*)
93
+ # # ...
94
+ # end
95
+ # end
96
+ #
97
+ # # $ foo server --help
98
+ # # # ...
99
+ # #
100
+ # # Examples:
101
+ # # foo server # Basic usage (it uses the bundled server engine)
102
+ # # foo server --server=webrick # Force `webrick` server engine
103
+ # # foo server --host=0.0.0.0 # Bind to a host
104
+ # # foo server --port=2306 # Bind to a port
105
+ # # foo server --no-code-reloading # Disable code reloading
106
+ def self.example(*examples)
107
+ @examples += examples.flatten(1)
108
+ end
109
+
110
+ # Specify an argument
111
+ #
112
+ # @param name [Symbol] the argument name
113
+ # @param options [Hash] a set of options
114
+ #
115
+ # @since 0.1.0
116
+ #
117
+ # @example Optional argument
118
+ # require "dry/cli"
119
+ #
120
+ # class Hello < Dry::CLI::Command
121
+ # argument :name
122
+ #
123
+ # def call(name: nil, **)
124
+ # if name.nil?
125
+ # puts "Hello, stranger"
126
+ # else
127
+ # puts "Hello, #{name}"
128
+ # end
129
+ # end
130
+ # end
131
+ #
132
+ # # $ foo hello
133
+ # # Hello, stranger
134
+ #
135
+ # # $ foo hello Luca
136
+ # # Hello, Luca
137
+ #
138
+ # @example Required argument
139
+ # require "dry/cli"
140
+ #
141
+ # class Hello < Dry::CLI::Command
142
+ # argument :name, required: true
143
+ #
144
+ # def call(name:, **)
145
+ # puts "Hello, #{name}"
146
+ # end
147
+ # end
148
+ #
149
+ # # $ foo hello Luca
150
+ # # Hello, Luca
151
+ #
152
+ # # $ foo hello
153
+ # # ERROR: "foo hello" was called with no arguments
154
+ # # Usage: "foo hello NAME"
155
+ #
156
+ # @example Multiple arguments
157
+ # require "dry/cli"
158
+ #
159
+ # module Generate
160
+ # class Action < Dry::CLI::Command
161
+ # argument :app, required: true
162
+ # argument :action, required: true
163
+ #
164
+ # def call(app:, action:, **)
165
+ # puts "Generating action: #{action} for app: #{app}"
166
+ # end
167
+ # end
168
+ # end
169
+ #
170
+ # # $ foo generate action web home
171
+ # # Generating action: home for app: web
172
+ #
173
+ # # $ foo generate action
174
+ # # ERROR: "foo generate action" was called with no arguments
175
+ # # Usage: "foo generate action APP ACTION"
176
+ #
177
+ # @example Description
178
+ # require "dry/cli"
179
+ #
180
+ # class Hello < Dry::CLI::Command
181
+ # argument :name, desc: "The name of the person to greet"
182
+ #
183
+ # def call(name: nil, **)
184
+ # # ...
185
+ # end
186
+ # end
187
+ #
188
+ # # $ foo hello --help
189
+ # # Command:
190
+ # # foo hello
191
+ # #
192
+ # # Usage:
193
+ # # foo hello [NAME]
194
+ # #
195
+ # # Arguments:
196
+ # # NAME # The name of the person to greet
197
+ # #
198
+ # # Options:
199
+ # # --help, -h # Print this help
200
+ def self.argument(name, options = {})
201
+ @arguments << Argument.new(name, options)
202
+ end
203
+
204
+ # Command line option (aka optional argument)
205
+ #
206
+ # @param name [Symbol] the param name
207
+ # @param options [Hash] a set of options
208
+ #
209
+ # @since 0.1.0
210
+ #
211
+ # @example Basic usage
212
+ # require "dry/cli"
213
+ #
214
+ # class Console < Dry::CLI::Command
215
+ # param :engine
216
+ #
217
+ # def call(engine: nil, **)
218
+ # puts "starting console (engine: #{engine || :irb})"
219
+ # end
220
+ # end
221
+ #
222
+ # # $ foo console
223
+ # # starting console (engine: irb)
224
+ #
225
+ # # $ foo console --engine=pry
226
+ # # starting console (engine: pry)
227
+ #
228
+ # @example List values
229
+ # require "dry/cli"
230
+ #
231
+ # class Console < Dry::CLI::Command
232
+ # param :engine, values: %w(irb pry ripl)
233
+ #
234
+ # def call(engine: nil, **)
235
+ # puts "starting console (engine: #{engine || :irb})"
236
+ # end
237
+ # end
238
+ #
239
+ # # $ foo console
240
+ # # starting console (engine: irb)
241
+ #
242
+ # # $ foo console --engine=pry
243
+ # # starting console (engine: pry)
244
+ #
245
+ # # $ foo console --engine=foo
246
+ # # ERROR: Invalid param provided
247
+ #
248
+ # @example Description
249
+ # require "dry/cli"
250
+ #
251
+ # class Console < Dry::CLI::Command
252
+ # param :engine, desc: "Force a console engine"
253
+ #
254
+ # def call(engine: nil, **)
255
+ # # ...
256
+ # end
257
+ # end
258
+ #
259
+ # # $ foo console --help
260
+ # # # ...
261
+ # #
262
+ # # Options:
263
+ # # --engine=VALUE # Force a console engine: (irb/pry/ripl)
264
+ # # --help, -h # Print this help
265
+ #
266
+ # @example Boolean
267
+ # require "dry/cli"
268
+ #
269
+ # class Server < Dry::CLI::Command
270
+ # param :code_reloading, type: :boolean, default: true
271
+ #
272
+ # def call(code_reloading:, **)
273
+ # puts "staring server (code reloading: #{code_reloading})"
274
+ # end
275
+ # end
276
+ #
277
+ # # $ foo server
278
+ # # starting server (code reloading: true)
279
+ #
280
+ # # $ foo server --no-code-reloading
281
+ # # starting server (code reloading: false)
282
+ #
283
+ # # $ foo server --help
284
+ # # # ...
285
+ # #
286
+ # # Options:
287
+ # # --[no]-code-reloading
288
+ #
289
+ # @example Aliases
290
+ # require "dry/cli"
291
+ #
292
+ # class Server < Dry::CLI::Command
293
+ # param :port, aliases: ["-p"]
294
+ #
295
+ # def call(options)
296
+ # puts "staring server (port: #{options.fetch(:port, 2300)})"
297
+ # end
298
+ # end
299
+ #
300
+ # # $ foo server
301
+ # # starting server (port: 2300)
302
+ #
303
+ # # $ foo server --port=2306
304
+ # # starting server (port: 2306)
305
+ #
306
+ # # $ foo server -p 2306
307
+ # # starting server (port: 2306)
308
+ #
309
+ # # $ foo server --help
310
+ # # # ...
311
+ # #
312
+ # # Options:
313
+ # # --port=VALUE, -p VALUE
314
+ def self.option(name, options = {})
315
+ @options << Option.new(name, options)
316
+ end
317
+
318
+ # @since 0.1.0
319
+ # @api private
320
+ def self.params
321
+ @_mutex.synchronize do
322
+ (@arguments + @options).uniq
323
+ end
324
+ end
325
+
326
+ # @since 0.1.0
327
+ # @api private
328
+ def self.default_params
329
+ params.each_with_object({}) do |param, result|
330
+ result[param.name] = param.default unless param.default.nil?
331
+ end
332
+ end
333
+
334
+ # @since 0.1.0
335
+ # @api private
336
+ def self.required_arguments
337
+ arguments.select(&:required?)
338
+ end
339
+
340
+ # @since 0.1.0
341
+ # @api private
342
+ def self.optional_arguments
343
+ arguments.reject(&:required?)
344
+ end
345
+
346
+ # @since x.x.x
347
+ # @api private
348
+ def self.subcommands
349
+ subcommands
350
+ end
351
+
352
+ # @since 0.7.0
353
+ # @api private
354
+ def self.superclass_variable_dup(var)
355
+ if superclass.instance_variable_defined?(var)
356
+ superclass.instance_variable_get(var).dup
357
+ end
358
+ end
359
+
360
+ # @since 0.7.0
361
+ # @api private
362
+ def self.superclass_arguments
363
+ superclass_variable_dup(:@arguments)
364
+ end
365
+
366
+ # @since 0.7.0
367
+ # @api private
368
+ def self.superclass_options
369
+ superclass_variable_dup(:@options)
370
+ end
371
+
372
+ extend Forwardable
373
+
374
+ delegate %i[
375
+ description
376
+ examples
377
+ arguments
378
+ options
379
+ params
380
+ default_params
381
+ required_arguments
382
+ optional_arguments
383
+ subcommands
384
+ ] => "self.class"
385
+ end
386
+ end
387
+ end
@@ -0,0 +1,253 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module Dry
6
+ class CLI
7
+ # Command registry
8
+ #
9
+ # @since 0.1.0
10
+ # @api private
11
+ class CommandRegistry
12
+ # @since 0.1.0
13
+ # @api private
14
+ def initialize
15
+ @_mutex = Mutex.new
16
+ @root = Node.new
17
+ end
18
+
19
+ # @since 0.1.0
20
+ # @api private
21
+ def set(name, command, aliases)
22
+ @_mutex.synchronize do
23
+ node = @root
24
+ name.split(/[[:space:]]/).each do |token|
25
+ node = node.put(node, token)
26
+ end
27
+
28
+ node.aliases!(aliases)
29
+ if command
30
+ node.leaf!(command)
31
+ node.subcommands!(command)
32
+ end
33
+
34
+ nil
35
+ end
36
+ end
37
+
38
+ # @since 0.1.0
39
+ # @api private
40
+ #
41
+ def get(arguments)
42
+ @_mutex.synchronize do
43
+ node = @root
44
+ args = []
45
+ names = []
46
+ valid_leaf = nil
47
+ result = LookupResult.new(node, args, names, node.leaf?)
48
+
49
+ arguments.each_with_index do |token, i|
50
+ tmp = node.lookup(token)
51
+
52
+ if tmp.nil? && valid_leaf
53
+ result = valid_leaf
54
+ break
55
+ elsif tmp.nil?
56
+ result = LookupResult.new(node, args, names, false)
57
+ break
58
+ elsif tmp.leaf?
59
+ args = arguments[i + 1..-1]
60
+ names = arguments[0..i]
61
+ node = tmp
62
+ result = LookupResult.new(node, args, names, true)
63
+ valid_leaf = result
64
+ break unless tmp.children?
65
+ else
66
+ names = arguments[0..i]
67
+ node = tmp
68
+ result = LookupResult.new(node, args, names, node.leaf?)
69
+ end
70
+ end
71
+
72
+ result
73
+ end
74
+ end
75
+
76
+ # Node of the registry
77
+ #
78
+ # @since 0.1.0
79
+ # @api private
80
+ class Node
81
+ # @since 0.1.0
82
+ # @api private
83
+ attr_reader :parent
84
+
85
+ # @since 0.1.0
86
+ # @api private
87
+ attr_reader :children
88
+
89
+ # @since 0.1.0
90
+ # @api private
91
+ attr_reader :aliases
92
+
93
+ # @since 0.1.0
94
+ # @api private
95
+ attr_reader :command
96
+
97
+ # @since 0.1.0
98
+ # @api private
99
+ attr_reader :before_callbacks
100
+
101
+ # @since 0.1.0
102
+ # @api private
103
+ attr_reader :after_callbacks
104
+
105
+ # @since 0.1.0
106
+ # @api private
107
+ def initialize(parent = nil)
108
+ @parent = parent
109
+ @children = {}
110
+ @aliases = {}
111
+ @command = nil
112
+
113
+ @before_callbacks = Chain.new
114
+ @after_callbacks = Chain.new
115
+ end
116
+
117
+ # @since 0.1.0
118
+ # @api private
119
+ def put(parent, key)
120
+ children[key] ||= self.class.new(parent)
121
+ end
122
+
123
+ # @since 0.1.0
124
+ # @api private
125
+ def lookup(token)
126
+ children[token] || aliases[token]
127
+ end
128
+
129
+ # @since 0.1.0
130
+ # @api private
131
+ def leaf!(command)
132
+ @command = command
133
+ end
134
+
135
+ # @since x.x.x
136
+ # @api private
137
+ def subcommands!(command)
138
+ command_class = command.is_a?(Class) ? command : command.class
139
+ command_class.subcommands = children
140
+ end
141
+
142
+ # @since 0.1.0
143
+ # @api private
144
+ def alias!(key, child)
145
+ @aliases[key] = child
146
+ end
147
+
148
+ # @since 0.1.0
149
+ # @api private
150
+ def aliases!(aliases)
151
+ aliases.each do |a|
152
+ parent.alias!(a, self)
153
+ end
154
+ end
155
+
156
+ # @since 0.1.0
157
+ # @api private
158
+ def leaf?
159
+ !command.nil?
160
+ end
161
+
162
+ # @since x.x.x
163
+ # @api private
164
+ def children?
165
+ children.any?
166
+ end
167
+ end
168
+
169
+ # Result of a registry lookup
170
+ #
171
+ # @since 0.1.0
172
+ # @api private
173
+ class LookupResult
174
+ # @since 0.1.0
175
+ # @api private
176
+ attr_reader :names
177
+
178
+ # @since 0.1.0
179
+ # @api private
180
+ attr_reader :arguments
181
+
182
+ # @since 0.1.0
183
+ # @api private
184
+ def initialize(node, arguments, names, found)
185
+ @node = node
186
+ @arguments = arguments
187
+ @names = names
188
+ @found = found
189
+ end
190
+
191
+ # @since 0.1.0
192
+ # @api private
193
+ def found?
194
+ @found
195
+ end
196
+
197
+ # @since 0.1.0
198
+ # @api private
199
+ def children
200
+ @node.children
201
+ end
202
+
203
+ # @since 0.1.0
204
+ # @api private
205
+ def command
206
+ @node.command
207
+ end
208
+
209
+ # @since 0.2.0
210
+ # @api private
211
+ def before_callbacks
212
+ @node.before_callbacks
213
+ end
214
+
215
+ # @since 0.2.0
216
+ # @api private
217
+ def after_callbacks
218
+ @node.after_callbacks
219
+ end
220
+ end
221
+
222
+ # Callbacks chain
223
+ #
224
+ # @since 0.4.0
225
+ # @api private
226
+ class Chain
227
+ # @since 0.4.0
228
+ # @api private
229
+ attr_reader :chain
230
+
231
+ # @since 0.4.0
232
+ # @api private
233
+ def initialize
234
+ @chain = Set.new
235
+ end
236
+
237
+ # @since 0.4.0
238
+ # @api private
239
+ def append(&callback)
240
+ chain.add(callback)
241
+ end
242
+
243
+ # @since 0.4.0
244
+ # @api private
245
+ def run(context, *args)
246
+ chain.each do |callback|
247
+ context.instance_exec(*args, &callback)
248
+ end
249
+ end
250
+ end
251
+ end
252
+ end
253
+ end