rfix 1.4.1 → 2.0.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 (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