dry-cli 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/dry/cli/usage.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/cli/program_name'
3
+ require "dry/cli/program_name"
4
4
 
5
5
  module Dry
6
6
  class CLI
@@ -11,12 +11,13 @@ module Dry
11
11
  module Usage
12
12
  # @since 0.1.0
13
13
  # @api private
14
- SUBCOMMAND_BANNER = ' [SUBCOMMAND]'
14
+ SUBCOMMAND_BANNER = " [SUBCOMMAND]"
15
+ ROOT_COMMAND_WITH_SUBCOMMANDS_BANNER = " [ARGUMENT|SUBCOMMAND]"
15
16
 
16
17
  # @since 0.1.0
17
18
  # @api private
18
19
  def self.call(result)
19
- header = 'Commands:'
20
+ header = "Commands:"
20
21
  max_length, commands = commands_and_arguments(result)
21
22
 
22
23
  commands.map do |banner, node|
@@ -30,7 +31,9 @@ module Dry
30
31
  def self.commands_and_arguments(result)
31
32
  max_length = 0
32
33
  ret = commands(result).each_with_object({}) do |(name, node), memo|
33
- args = if node.leaf?
34
+ args = if node.command && node.leaf? && node.children?
35
+ ROOT_COMMAND_WITH_SUBCOMMANDS_BANNER
36
+ elsif node.leaf?
34
37
  arguments(node.command)
35
38
  else
36
39
  SUBCOMMAND_BANNER
@@ -52,11 +55,11 @@ module Dry
52
55
  required_arguments = command.required_arguments
53
56
  optional_arguments = command.optional_arguments
54
57
 
55
- required = required_arguments.map { |arg| arg.name.upcase }.join(' ') if required_arguments.any? # rubocop:disable Metrics/LineLength
56
- optional = optional_arguments.map { |arg| "[#{arg.name.upcase}]" }.join(' ') if optional_arguments.any? # rubocop:disable Metrics/LineLength
58
+ required = required_arguments.map { |arg| arg.name.upcase }.join(" ") if required_arguments.any? # rubocop:disable Metrics/LineLength
59
+ optional = optional_arguments.map { |arg| "[#{arg.name.upcase}]" }.join(" ") if optional_arguments.any? # rubocop:disable Metrics/LineLength
57
60
  result = [required, optional].compact
58
61
 
59
- " #{result.join(' ')}" unless result.empty?
62
+ " #{result.join(" ")}" unless result.empty?
60
63
  end
61
64
 
62
65
  # @since 0.1.0
@@ -70,7 +73,7 @@ module Dry
70
73
  # @since 0.1.0
71
74
  # @api private
72
75
  def self.justify(string, padding, usage)
73
- return string.chomp(' ') if usage.nil?
76
+ return string.chomp(" ") if usage.nil?
74
77
 
75
78
  string.ljust(padding + padding / 2)
76
79
  end
@@ -3,6 +3,6 @@
3
3
  module Dry
4
4
  class CLI
5
5
  # @since 0.1.0
6
- VERSION = '0.6.0'
6
+ VERSION = "0.7.0"
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-06 00:00:00.000000000 Z
11
+ date: 2021-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: concurrent-ruby
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: bundler
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -72,6 +58,20 @@ dependencies:
72
58
  - - "~>"
73
59
  - !ruby/object:Gem::Version
74
60
  version: '3.7'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rubocop
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.82'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.82'
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: simplecov
77
77
  requirement: !ruby/object:Gem::Requirement
@@ -109,7 +109,6 @@ files:
109
109
  - lib/dry/cli/program_name.rb
110
110
  - lib/dry/cli/registry.rb
111
111
  - lib/dry/cli/usage.rb
112
- - lib/dry/cli/utils/files.rb
113
112
  - lib/dry/cli/version.rb
114
113
  homepage: https://dry-rb.org/gems/dry-cli
115
114
  licenses:
@@ -127,14 +126,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
127
126
  requirements:
128
127
  - - ">="
129
128
  - !ruby/object:Gem::Version
130
- version: 2.3.0
129
+ version: 2.4.0
131
130
  required_rubygems_version: !ruby/object:Gem::Requirement
132
131
  requirements:
133
132
  - - ">="
134
133
  - !ruby/object:Gem::Version
135
134
  version: '0'
136
135
  requirements: []
137
- rubygems_version: 3.0.3
136
+ rubygems_version: 3.1.6
138
137
  signing_key:
139
138
  specification_version: 4
140
139
  summary: Common framework to build command line interfaces with Ruby
@@ -1,444 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'pathname'
4
- require 'fileutils'
5
- require 'backports/2.4.0/string/match' if RUBY_VERSION < '2.4'
6
-
7
- module Dry
8
- class CLI
9
- module Utils
10
- # Files utilities
11
- #
12
- # @since 0.3.1
13
- module Files # rubocop:disable Metrics/ModuleLength
14
- # Creates an empty file for the given path.
15
- # All the intermediate directories are created.
16
- # If the path already exists, it doesn't change the contents
17
- #
18
- # @param path [String,Pathname] the path to file
19
- #
20
- # @since 0.3.1
21
- def self.touch(path)
22
- mkdir_p(path)
23
- FileUtils.touch(path)
24
- end
25
-
26
- # Creates a new file or rewrites the contents
27
- # of an existing file for the given path and content
28
- # All the intermediate directories are created.
29
- #
30
- # @param path [String,Pathname] the path to file
31
- # @param content [String, Array<String>] the content to write
32
- #
33
- # @since 0.3.1
34
- def self.write(path, *content)
35
- mkdir_p(path)
36
- open(path, ::File::CREAT | ::File::WRONLY | ::File::TRUNC, *content) # rubocop:disable LineLength, Security/Open - this isn't a call to `::Kernel.open`, but to `self.open`
37
- end
38
-
39
- # Copies source into destination.
40
- # All the intermediate directories are created.
41
- # If the destination already exists, it overrides the contents.
42
- #
43
- # @param source [String,Pathname] the path to the source file
44
- # @param destination [String,Pathname] the path to the destination file
45
- #
46
- # @since 0.3.1
47
- def self.cp(source, destination)
48
- mkdir_p(destination)
49
- FileUtils.cp(source, destination)
50
- end
51
-
52
- # Creates a directory for the given path.
53
- # It assumes that all the tokens in `path` are meant to be a directory.
54
- # All the intermediate directories are created.
55
- #
56
- # @param path [String,Pathname] the path to directory
57
- #
58
- # @since 0.3.1
59
- #
60
- # @see .mkdir_p
61
- #
62
- # @example
63
- # require "dry/cli/utils/files"
64
- #
65
- # Dry::CLI::Utils::Files.mkdir("path/to/directory")
66
- # # => creates the `path/to/directory` directory
67
- #
68
- # # WRONG this isn't probably what you want, check `.mkdir_p`
69
- # Dry::CLI::Utils::Files.mkdir("path/to/file.rb")
70
- # # => creates the `path/to/file.rb` directory
71
- def self.mkdir(path)
72
- FileUtils.mkdir_p(path)
73
- end
74
-
75
- # Creates a directory for the given path.
76
- # It assumes that all the tokens, but the last, in `path` are meant to be
77
- # a directory, whereas the last is meant to be a file.
78
- # All the intermediate directories are created.
79
- #
80
- # @param path [String,Pathname] the path to directory
81
- #
82
- # @since 0.3.1
83
- #
84
- # @see .mkdir
85
- #
86
- # @example
87
- # require "dry/cli/utils/files"
88
- #
89
- # Dry::CLI::Utils::Files.mkdir_p("path/to/file.rb")
90
- # # => creates the `path/to` directory, but NOT `file.rb`
91
- #
92
- # # WRONG it doesn't create the last directory, check `.mkdir`
93
- # Dry::CLI::Utils::Files.mkdir_p("path/to/directory")
94
- # # => creates the `path/to` directory
95
- def self.mkdir_p(path)
96
- Pathname.new(path).dirname.mkpath
97
- end
98
-
99
- # Deletes given path (file).
100
- #
101
- # @param path [String,Pathname] the path to file
102
- #
103
- # @raise [Errno::ENOENT] if the path doesn't exist
104
- #
105
- # @since 0.3.1
106
- def self.delete(path)
107
- FileUtils.rm(path)
108
- end
109
-
110
- # Deletes given path (directory).
111
- #
112
- # @param path [String,Pathname] the path to file
113
- #
114
- # @raise [Errno::ENOENT] if the path doesn't exist
115
- #
116
- # @since 0.3.1
117
- def self.delete_directory(path)
118
- FileUtils.remove_entry_secure(path)
119
- end
120
-
121
- # Adds a new line at the top of the file
122
- #
123
- # @param path [String,Pathname] the path to file
124
- # @param line [String] the line to add
125
- #
126
- # @raise [Errno::ENOENT] if the path doesn't exist
127
- #
128
- # @see .append
129
- #
130
- # @since 0.3.1
131
- def self.unshift(path, line)
132
- content = ::File.readlines(path)
133
- content.unshift("#{line}\n")
134
-
135
- write(path, content)
136
- end
137
-
138
- # Adds a new line at the bottom of the file
139
- #
140
- # @param path [String,Pathname] the path to file
141
- # @param contents [String] the contents to add
142
- #
143
- # @raise [Errno::ENOENT] if the path doesn't exist
144
- #
145
- # @see .unshift
146
- #
147
- # @since 0.3.1
148
- def self.append(path, contents)
149
- mkdir_p(path)
150
-
151
- content = ::File.readlines(path)
152
- content << "\n" unless content.last.end_with?("\n")
153
- content << "#{contents}\n"
154
-
155
- write(path, content)
156
- end
157
-
158
- # Replace first line in `path` that contains `target` with `replacement`.
159
- #
160
- # @param path [String,Pathname] the path to file
161
- # @param target [String,Regexp] the target to replace
162
- # @param replacement [String] the replacement
163
- #
164
- # @raise [Errno::ENOENT] if the path doesn't exist
165
- # @raise [ArgumentError] if `target` cannot be found in `path`
166
- #
167
- # @see .replace_last_line
168
- #
169
- # @since 0.3.1
170
- def self.replace_first_line(path, target, replacement)
171
- content = ::File.readlines(path)
172
- content[index(content, path, target)] = "#{replacement}\n"
173
-
174
- write(path, content)
175
- end
176
-
177
- # Replace last line in `path` that contains `target` with `replacement`.
178
- #
179
- # @param path [String,Pathname] the path to file
180
- # @param target [String,Regexp] the target to replace
181
- # @param replacement [String] the replacement
182
- #
183
- # @raise [Errno::ENOENT] if the path doesn't exist
184
- # @raise [ArgumentError] if `target` cannot be found in `path`
185
- #
186
- # @see .replace_first_line
187
- #
188
- # @since 0.3.1
189
- def self.replace_last_line(path, target, replacement)
190
- content = ::File.readlines(path)
191
- content[-index(content.reverse, path, target) - 1] = "#{replacement}\n"
192
-
193
- write(path, content)
194
- end
195
-
196
- # Inject `contents` in `path` before `target`.
197
- #
198
- # @param path [String,Pathname] the path to file
199
- # @param target [String,Regexp] the target to replace
200
- # @param contents [String] the contents to inject
201
- #
202
- # @raise [Errno::ENOENT] if the path doesn't exist
203
- # @raise [ArgumentError] if `target` cannot be found in `path`
204
- #
205
- # @see .inject_line_after
206
- # @see .inject_line_before_last
207
- # @see .inject_line_after_last
208
- #
209
- # @since 0.3.1
210
- def self.inject_line_before(path, target, contents)
211
- _inject_line_before(path, target, contents, method(:index))
212
- end
213
-
214
- # Inject `contents` in `path` after last `target`.
215
- #
216
- # @param path [String,Pathname] the path to file
217
- # @param target [String,Regexp] the target to replace
218
- # @param contents [String] the contents to inject
219
- #
220
- # @raise [Errno::ENOENT] if the path doesn't exist
221
- # @raise [ArgumentError] if `target` cannot be found in `path`
222
- #
223
- # @see .inject_line_before
224
- # @see .inject_line_after
225
- # @see .inject_line_after_last
226
- #
227
- # @since 1.3.0
228
- def self.inject_line_before_last(path, target, contents)
229
- _inject_line_before(path, target, contents, method(:rindex))
230
- end
231
-
232
- # Inject `contents` in `path` after `target`.
233
- #
234
- # @param path [String,Pathname] the path to file
235
- # @param target [String,Regexp] the target to replace
236
- # @param contents [String] the contents to inject
237
- #
238
- # @raise [Errno::ENOENT] if the path doesn't exist
239
- # @raise [ArgumentError] if `target` cannot be found in `path`
240
- #
241
- # @see .inject_line_before
242
- # @see .inject_line_before_last
243
- # @see .inject_line_after_last
244
- #
245
- # @since 0.3.1
246
- def self.inject_line_after(path, target, contents)
247
- _inject_line_after(path, target, contents, method(:index))
248
- end
249
-
250
- # Inject `contents` in `path` after last `target`.
251
- #
252
- # @param path [String,Pathname] the path to file
253
- # @param target [String,Regexp] the target to replace
254
- # @param contents [String] the contents to inject
255
- #
256
- # @raise [Errno::ENOENT] if the path doesn't exist
257
- # @raise [ArgumentError] if `target` cannot be found in `path`
258
- #
259
- # @see .inject_line_before
260
- # @see .inject_line_after
261
- # @see .inject_line_before_last
262
- # @see .inject_line_after_last
263
- #
264
- # @since 1.3.0
265
- def self.inject_line_after_last(path, target, contents)
266
- _inject_line_after(path, target, contents, method(:rindex))
267
- end
268
-
269
- # Removes line from `path`, matching `target`.
270
- #
271
- # @param path [String,Pathname] the path to file
272
- # @param target [String,Regexp] the target to remove
273
- #
274
- # @raise [Errno::ENOENT] if the path doesn't exist
275
- # @raise [ArgumentError] if `target` cannot be found in `path`
276
- #
277
- # @since 0.3.1
278
- def self.remove_line(path, target)
279
- content = ::File.readlines(path)
280
- i = index(content, path, target)
281
-
282
- content.delete_at(i)
283
- write(path, content)
284
- end
285
-
286
- # Removes `target` block from `path`
287
- #
288
- # @param path [String,Pathname] the path to file
289
- # @param target [String] the target block to remove
290
- #
291
- # @raise [Errno::ENOENT] if the path doesn't exist
292
- # @raise [ArgumentError] if `target` cannot be found in `path`
293
- #
294
- # @since 0.3.1
295
- #
296
- # @example
297
- # require "dry/cli/utils/files"
298
- #
299
- # puts File.read("app.rb")
300
- #
301
- # # class App
302
- # # configure do
303
- # # root __dir__
304
- # # end
305
- # # end
306
- #
307
- # Dry::CLI::Utils::Files.remove_block("app.rb", "configure")
308
- #
309
- # puts File.read("app.rb")
310
- #
311
- # # class App
312
- # # end
313
- def self.remove_block(path, target)
314
- content = ::File.readlines(path)
315
- starting = index(content, path, target)
316
- line = content[starting]
317
- size = line[/\A[[:space:]]*/].bytesize
318
- closing = (' ' * size) + (target.match?(/{/) ? '}' : 'end')
319
- ending = starting + index(content[starting..-1], path, closing)
320
-
321
- content.slice!(starting..ending)
322
- write(path, content)
323
-
324
- remove_block(path, target) if match?(content, target)
325
- end
326
-
327
- # Checks if `path` exist
328
- #
329
- # @param path [String,Pathname] the path to file
330
- #
331
- # @return [TrueClass,FalseClass] the result of the check
332
- #
333
- # @since 0.3.1
334
- #
335
- # @example
336
- # require "dry/cli/utils/files"
337
- #
338
- # Dry::CLI::Utils::Files.exist?(__FILE__) # => true
339
- # Dry::CLI::Utils::Files.exist?(__dir__) # => true
340
- #
341
- # Dry::CLI::Utils::Files.exist?("missing_file") # => false
342
- def self.exist?(path)
343
- File.exist?(path)
344
- end
345
-
346
- # Checks if `path` is a directory
347
- #
348
- # @param path [String,Pathname] the path to directory
349
- #
350
- # @return [TrueClass,FalseClass] the result of the check
351
- #
352
- # @since 0.3.1
353
- #
354
- # @example
355
- # require "dry/cli/utils/files"
356
- #
357
- # Dry::CLI::Utils::Files.directory?(__dir__) # => true
358
- # Dry::CLI::Utils::Files.directory?(__FILE__) # => false
359
- #
360
- # Dry::CLI::Utils::Files.directory?("missing_directory") # => false
361
- def self.directory?(path)
362
- File.directory?(path)
363
- end
364
-
365
- # private
366
-
367
- # @since 0.3.1
368
- # @api private
369
- def self.match?(content, target)
370
- !line_number(content, target).nil?
371
- end
372
-
373
- private_class_method :match?
374
-
375
- # @since 0.3.1
376
- # @api private
377
- def self.open(path, mode, *content)
378
- ::File.open(path, mode) do |file|
379
- file.write(Array(content).flatten.join)
380
- end
381
- end
382
-
383
- private_class_method :open
384
-
385
- # @since 0.3.1
386
- # @api private
387
- def self.index(content, path, target)
388
- line_number(content, target) ||
389
- raise(ArgumentError, "Cannot find `#{target}' inside `#{path}'.")
390
- end
391
-
392
- private_class_method :index
393
-
394
- # @since 1.3.0
395
- # @api private
396
- def self.rindex(content, path, target)
397
- line_number(content, target, finder: content.method(:rindex)) ||
398
- raise(ArgumentError, "Cannot find `#{target}' inside `#{path}'.")
399
- end
400
-
401
- private_class_method :rindex
402
-
403
- # @since 1.3.0
404
- # @api private
405
- def self._inject_line_before(path, target, contents, finder)
406
- content = ::File.readlines(path)
407
- i = finder.call(content, path, target)
408
-
409
- content.insert(i, "#{contents}\n")
410
- write(path, content)
411
- end
412
-
413
- private_class_method :_inject_line_before
414
-
415
- # @since 1.3.0
416
- # @api private
417
- def self._inject_line_after(path, target, contents, finder)
418
- content = ::File.readlines(path)
419
- i = finder.call(content, path, target)
420
-
421
- content.insert(i + 1, "#{contents}\n")
422
- write(path, content)
423
- end
424
-
425
- private_class_method :_inject_line_after
426
-
427
- # @since 0.3.1
428
- # @api private
429
- def self.line_number(content, target, finder: content.method(:index))
430
- finder.call do |l|
431
- case target
432
- when ::String
433
- l.include?(target)
434
- when Regexp
435
- l =~ target
436
- end
437
- end
438
- end
439
-
440
- private_class_method :line_number
441
- end
442
- end
443
- end
444
- end