dry-cli 0.6.0 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 448f01b536f8cc664a86d2b4e558e8db6062c6ac31256c32d507828f1d3b7519
4
- data.tar.gz: 254448f2e0b031cfc0077021ca3d0876b81b5eb9c7b1a7e51afce0d9524c95cc
3
+ metadata.gz: acfd9b78000e6f3ecef16d4eee260eee84bb4661251966e367e4f9a2001d3dac
4
+ data.tar.gz: df37074af2d1ed387d24ab90ed37920247cc40dc4c508c1325f08e9cb395c277
5
5
  SHA512:
6
- metadata.gz: b114fa6217a59fdca38df56819e016a47337843ad357a52cd0f006bac2048dc2ee9253439a0353721412edac8e3a4d1f3c9601e7b3ccb729c86f5899d53fc2d6
7
- data.tar.gz: 1edf50dce27e4d1cc9e9cc868c14d18e9399e62b0edcebf5a3166559bc8ef66a7fa79a04d0ac0c2ef5c6ffa1384aecb80ca967c16136f9dceb3721123e9c53a4
6
+ metadata.gz: c2b0035c9df7cbe7cf01fabd69a0207aaab945a13debf8b4a1441d764853343ac2a2dd1264c395aeda3296957870fcdcd87fa44041f6948d490afde897f2d837
7
+ data.tar.gz: c74d981d7c23200a652f75013977a9ea3b720c1264e184fd93af83fcb33995ec00c6284021a7e29b3ea215e9ac5c51aaccbf2ff03c821f215aa52d433bf1fd62
data/CHANGELOG.md CHANGED
@@ -1,17 +1,46 @@
1
+ <!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
2
+
3
+ ## 0.7.0 2020-05-08
4
+
5
+
6
+ ### Added
7
+
8
+ - Inheritable attributes for subclasses of commands (@IvanShamatov)
9
+ - Ability to register instances, not only classes as Commands (@IvanShamatov)
10
+ - Add support for subcommands with a parent command (@unrooty)
11
+
12
+ ### Fixed
13
+
14
+ - Safely rescue pipe exception, when you CLI app is producing output for piped CLI app (IvanShamatov)
15
+ - Safely rescue keyboard interrupts (@IvanShamatov)
16
+ - [Internal] Don't run specs twice (@jodosha)
17
+ - Update inline call with keyward arguments (@flash-gordon)
18
+
19
+ ### Changed
20
+
21
+ - Extracted Dry::CLI::Utils::Files into dry-files (@jodosha)
22
+ - Drop 2.3 ruby support (@IvanShamatov)
23
+ - [Internal] Changelog, issue templates (@solnic)
24
+ - Documentation updates (@davydovanton)
25
+ - Remove concurrent-ruby as runtime dependency (@jodosha)
26
+ - [Internal] Banner and Parses refactoring (@IvanShamatov)
27
+
28
+ [Compare v0.6.0...v0.7.0](https://github.com/dry-rb/dry-cli/compare/v0.6.0...v0.7.0)
29
+
1
30
  ## 0.6.0 2020-03-06
2
31
 
3
32
 
4
33
  ### Added
5
34
 
6
- - [Ivan Shamatov] Ability to pass command along with registry (for a singular command case)
7
- - [Nikita Shilnikov] [Internal] Backported ability to run gem's CI against ruby 2.3
8
- - [Ivan Shamatov] Inline syntax for commands
9
- - [Ivan Shamatov] Introduced stderr to any diagnostic output
35
+ - Ability to pass command along with registry (for a singular command case) (@IvanShamatov)
36
+ - [Internal] Backported ability to run gem's CI against ruby 2.3 (@flash-gordon)
37
+ - Inline syntax for commands (@IvanShamatov)
38
+ - Introduced stderr to any diagnostic output (@IvanShamatov)
10
39
 
11
40
  ### Fixed
12
41
 
13
- - [John Ledbetter & Luca Guidi] Fix ruby 2.7 warnings
14
- - [Ivan Shamatov] Fix banner, when option is a type of Array
42
+ - [John Ledbetter & Luca Guidi] Fix ruby 2.7 warnings (@jodosha)
43
+ - Fix banner, when option is a type of Array (@IvanShamatov)
15
44
 
16
45
 
17
46
  [Compare v0.5.1...v0.6.0](https://github.com/dry-rb/dry-cli/compare/v0.5.1...v0.6.0)
@@ -21,15 +50,15 @@
21
50
 
22
51
  ### Added
23
52
 
24
- - [Ivan Shamatov] Anonymous Registry sintax
25
- - [Ivan Shamatov] [Internal] Specs refactored, more unit specs added
26
- - [Luca Guidi] [Internal] removed `dry-inflector` as runtime dependency
27
- - [Ivan Shamatov] [Internal] Refactored Command class (command_name property removed)
28
- - [Piotr Solnica, Luca Guidi, Nikita Shilnikov & Christian Georgii] [Internal] Adapt gem to dry-rb style
53
+ - Anonymous Registry sintax (@IvanShamatov)
54
+ - [Internal] Specs refactored, more unit specs added (@IvanShamatov)
55
+ - [Internal] removed `dry-inflector` as runtime dependency (@jodosha)
56
+ - [Internal] Refactored Command class (command_name property removed) (@IvanShamatov)
57
+ - [Internal] Adapt gem to dry-rb style (@jodosha, @flash-gordon, @solnic, @cgeorgii)
29
58
 
30
59
  ### Fixed
31
60
 
32
- - [Piotr Solnica] Added missing 'set' require
61
+ - Added missing 'set' require (@solnic)
33
62
 
34
63
 
35
64
  [Compare v0.5.0...v0.5.1](https://github.com/dry-rb/dry-cli/compare/v0.5.0...v0.5.1)
@@ -39,7 +68,7 @@
39
68
 
40
69
  ### Added
41
70
 
42
- - [Ivan Shamatov, Piotr Solnica, Luca Guidi] [Internal] removed runtime and development dependency against `hanami-utils`
71
+ - [Internal] removed runtime and development dependency against `hanami-utils` (@jodosha, @IvanShamatov, @solnic)
43
72
 
44
73
 
45
74
  [Compare v0.4.0...v0.5.0](https://github.com/dry-rb/dry-cli/compare/v0.4.0...v0.5.0)
@@ -49,7 +78,7 @@
49
78
 
50
79
  ### Added
51
80
 
52
- - [Ivan Shamatov, Piotr Solnica, Luca Guidi] `hanami-cli` => `dry-cli`
81
+ - `hanami-cli` => `dry-cli` (@jodosha, @IvanShamatov, @solnic)
53
82
 
54
83
 
55
84
  [Compare v0.3.1...v0.4.0](https://github.com/dry-rb/dry-cli/compare/v0.3.1...v0.4.0)
@@ -59,8 +88,8 @@
59
88
 
60
89
  ### Added
61
90
 
62
- - [Luca Guidi] Official support for Ruby: MRI 2.6
63
- - [Luca Guidi] Support `bundler` 2.0+
91
+ - Official support for Ruby: MRI 2.6 (@jodosha)
92
+ - Support `bundler` 2.0+ (@jodosha)
64
93
 
65
94
 
66
95
  [Compare v0.3.0...v0.3.1](https://github.com/dry-rb/dry-cli/compare/v0.3.0...v0.3.1)
@@ -76,14 +105,14 @@
76
105
 
77
106
  ### Added
78
107
 
79
- - [Anton Davydov & Alfonso Uceda] Introduce array type for arguments (`foo exec test spec/bookshelf/entities spec/bookshelf/repositories`)
80
- - [Anton Davydov & Alfonso Uceda] Introduce array type for options (`foo generate config --apps=web,api`)
81
- - [Alfonso Uceda] Introduce variadic arguments (`foo run ruby:latest -- ruby -v`)
82
- - [Luca Guidi] Official support for JRuby 9.2.0.0
108
+ - Introduce array type for arguments (`foo exec test spec/bookshelf/entities spec/bookshelf/repositories`) (@davydovanton, @AlfonsoUceda)
109
+ - Introduce array type for options (`foo generate config --apps=web,api`) (@davydovanton, @AlfonsoUceda)
110
+ - Introduce variadic arguments (`foo run ruby:latest -- ruby -v`)
111
+ - Official support for JRuby 9.2.0.0 (@jodosha, @AlfonsoUceda)
83
112
 
84
113
  ### Fixed
85
114
 
86
- - [Anton Davydov] Print informative message when unknown or wrong option is passed (`"test" was called with arguments "--framework=unknown"`)
115
+ - Print informative message when unknown or wrong option is passed (`"test" was called with arguments "--framework=unknown"`) (@davydovanton)
87
116
 
88
117
 
89
118
  [Compare v0.2.0...v0.3.0.beta1](https://github.com/dry-rb/dry-cli/compare/v0.2.0...v0.3.0.beta1)
@@ -111,11 +140,11 @@
111
140
 
112
141
  ### Added
113
142
 
114
- - [Anton Davydov & Luca Guidi] Support objects as callbacks
143
+ - Support objects as callbacks (@jodosha, @davydovanton)
115
144
 
116
145
  ### Fixed
117
146
 
118
- - [Anton Davydov & Luca Guidi] Ensure callbacks' context of execution (aka `self`) to be the command that is being executed
147
+ - Ensure callbacks' context of execution (aka `self`) to be the command that is being executed (@jodosha, @davydovanton)
119
148
 
120
149
 
121
150
  [Compare v0.2.0.beta1...v0.2.0.beta2](https://github.com/dry-rb/dry-cli/compare/v0.2.0.beta1...v0.2.0.beta2)
@@ -125,7 +154,7 @@
125
154
 
126
155
  ### Added
127
156
 
128
- - [Anton Davydov] Register `before`/`after` callbacks for commands
157
+ - Register `before`/`after` callbacks for commands (@davydovanton)
129
158
 
130
159
 
131
160
  [Compare v0.1.1...v0.2.0.beta1](https://github.com/dry-rb/dry-cli/compare/v0.1.1...v0.2.0.beta1)
@@ -135,12 +164,12 @@
135
164
 
136
165
  ### Added
137
166
 
138
- - [Luca Guidi] Official support for Ruby: MRI 2.5
167
+ - Official support for Ruby: MRI 2.5 (@jodosha)
139
168
 
140
169
  ### Fixed
141
170
 
142
- - [Alfonso Uceda] Ensure default values for arguments to be sent to commands
143
- - [Alfonso Uceda] Ensure to fail when a missing required argument isn't provider, but an option is provided instead
171
+ - Ensure default values for arguments to be sent to commands (@AlfonsoUceda)
172
+ - Ensure to fail when a missing required argument isn't provider, but an option is provided instead (@AlfonsoUceda)
144
173
 
145
174
 
146
175
  [Compare v0.1.0...v0.1.1](https://github.com/dry-rb/dry-cli/compare/v0.1.0...v0.1.1)
@@ -168,7 +197,7 @@
168
197
 
169
198
  ### Added
170
199
 
171
- -  [Alfonso Uceda] Allow default value for arguments
200
+ - Allow default value for arguments (@AlfonsoUceda)
172
201
 
173
202
 
174
203
  [Compare v0.1.0.beta1...v0.1.0.beta2](https://github.com/dry-rb/dry-cli/compare/v0.1.0.beta1...v0.1.0.beta2)
@@ -178,12 +207,12 @@
178
207
 
179
208
  ### Added
180
209
 
181
- -  [Alfonso Uceda, Luca Guidi] Commands banner and usage
182
- -  [Alfonso Uceda] Added support for subcommands
183
- - [Alfonso Uceda] Validations for arguments and options
184
- - [Alfonso Uceda] Commands arguments and options
185
- - [Alfonso Uceda] Commands description
186
- - [Alfonso Uceda, Oana Sipos] Commands aliases
187
- - [Luca Guidi] Exit on unknown command
188
- - [Luca Guidi, Alfonso Uceda, Oana Sipos] Command lookup
189
- - [Luca Guidi, Tim Riley] Trie based registry to register commands and allow third-parties to override/add commands
210
+ - Commands banner and usage (@jodosha, @AlfonsoUceda)
211
+ - Added support for subcommands (@AlfonsoUceda)
212
+ - Validations for arguments and options (@AlfonsoUceda)
213
+ - Commands arguments and options (@AlfonsoUceda)
214
+ - Commands description (@AlfonsoUceda)
215
+ - Commands aliases (@AlfonsoUceda, @oana-sipos)
216
+ - Exit on unknown command (@jodosha)
217
+ - Command lookup (@AlfonsoUceda, @oana-sipos)
218
+ - Trie based registry to register commands and allow third-parties to override/add commands (@jodosha, @timriley)
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015-2020 dry-rb team
3
+ Copyright (c) 2015-2021 dry-rb team
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
data/README.md CHANGED
@@ -21,7 +21,7 @@
21
21
 
22
22
  This library officially supports the following Ruby versions:
23
23
 
24
- * MRI >= `2.4`
24
+ * MRI >= `2.5`
25
25
  * jruby >= `9.2`
26
26
 
27
27
  ## License
data/dry-cli.gemspec CHANGED
@@ -25,13 +25,12 @@ Gem::Specification.new do |spec|
25
25
  spec.metadata['source_code_uri'] = 'https://github.com/dry-rb/dry-cli'
26
26
  spec.metadata['bug_tracker_uri'] = 'https://github.com/dry-rb/dry-cli/issues'
27
27
 
28
- spec.required_ruby_version = ">= 2.3.0"
28
+ spec.required_ruby_version = ">= 2.4.0"
29
29
 
30
30
  # to update dependencies edit project.yml
31
- spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
32
-
33
31
  spec.add_development_dependency "bundler", ">= 1.6", "< 3"
34
32
  spec.add_development_dependency "rake", "~> 13.0"
35
33
  spec.add_development_dependency "rspec", "~> 3.7"
34
+ spec.add_development_dependency "rubocop", "~> 0.82"
36
35
  spec.add_development_dependency "simplecov", "~> 0.17.1"
37
36
  end
data/lib/dry/cli.rb CHANGED
@@ -8,14 +8,14 @@ module Dry
8
8
  #
9
9
  # @since 0.1.0
10
10
  class CLI
11
- require 'dry/cli/version'
12
- require 'dry/cli/errors'
13
- require 'dry/cli/command'
14
- require 'dry/cli/registry'
15
- require 'dry/cli/parser'
16
- require 'dry/cli/usage'
17
- require 'dry/cli/banner'
18
- require 'dry/cli/inflector'
11
+ require "dry/cli/version"
12
+ require "dry/cli/errors"
13
+ require "dry/cli/command"
14
+ require "dry/cli/registry"
15
+ require "dry/cli/parser"
16
+ require "dry/cli/usage"
17
+ require "dry/cli/banner"
18
+ require "dry/cli/inflector"
19
19
 
20
20
  # Check if command
21
21
  #
@@ -62,9 +62,11 @@ module Dry
62
62
  # @since 0.1.0
63
63
  def call(arguments: ARGV, out: $stdout, err: $stderr)
64
64
  @out, @err = out, err
65
- return perform_command(arguments) if kommand
66
-
67
- perform_registry(arguments)
65
+ kommand ? perform_command(arguments) : perform_registry(arguments)
66
+ rescue SignalException => e
67
+ signal_exception(e)
68
+ rescue Errno::EPIPE
69
+ # no op
68
70
  end
69
71
 
70
72
  private
@@ -106,16 +108,13 @@ module Dry
106
108
  # @api private
107
109
  def perform_registry(arguments)
108
110
  result = registry.get(arguments)
111
+ return usage(result) unless result.found?
109
112
 
110
- if result.found?
111
- command, args = parse(result.command, result.arguments, result.names)
113
+ command, args = parse(result.command, result.arguments, result.names)
112
114
 
113
- result.before_callbacks.run(command, args)
114
- command.call(**args)
115
- result.after_callbacks.run(command, args)
116
- else
117
- usage(result)
118
- end
115
+ result.before_callbacks.run(command, args)
116
+ command.call(**args)
117
+ result.after_callbacks.run(command, args)
119
118
  end
120
119
 
121
120
  # Parse arguments for a command.
@@ -131,26 +130,37 @@ module Dry
131
130
  # @since 0.6.0
132
131
  # @api private
133
132
  def parse(command, arguments, names)
134
- result = Parser.call(command, arguments, names)
133
+ prog_name = ProgramName.call(names)
135
134
 
136
- if result.help?
137
- out.puts Banner.call(command, names)
138
- exit(0)
139
- end
135
+ result = Parser.call(command, arguments, prog_name)
140
136
 
141
- if result.error?
142
- err.puts(result.error)
143
- exit(1)
144
- end
137
+ return help(command, prog_name) if result.help?
138
+
139
+ return error(result) if result.error?
145
140
 
146
- [command.new, result.arguments]
141
+ [build_command(command), result.arguments]
142
+ end
143
+
144
+ # @since 0.6.0
145
+ # @api private
146
+ def build_command(command)
147
+ command.is_a?(Class) ? command.new : command
148
+ end
149
+
150
+ # @since 0.6.0
151
+ # @api private
152
+ def help(command, prog_name)
153
+ out.puts Banner.call(command, prog_name)
154
+ exit(0) # Successful exit
155
+ end
156
+
157
+ # @since 0.6.0
158
+ # @api private
159
+ def error(result)
160
+ err.puts(result.error)
161
+ exit(1)
147
162
  end
148
163
 
149
- # Prints the command usage and exit.
150
- #
151
- # @param result [Dry::CLI::CommandRegistry::LookupResult]
152
- # @param out [IO] sta output
153
- #
154
164
  # @since 0.1.0
155
165
  # @api private
156
166
  def usage(result)
@@ -158,6 +168,15 @@ module Dry
158
168
  exit(1)
159
169
  end
160
170
 
171
+ # Handles Exit codes for signals
172
+ # Fatal error signal "n". Say 130 = 128 + 2 (SIGINT) or 137 = 128 + 9 (SIGKILL)
173
+ #
174
+ # @since 0.7.0
175
+ # @api private
176
+ def signal_exception(exception)
177
+ exit(128 + exception.signo)
178
+ end
179
+
161
180
  # Check if command
162
181
  #
163
182
  # @param command [Object] the command to check
@@ -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
@@ -16,15 +16,15 @@ module Dry
16
16
  #
17
17
  # @since 0.1.0
18
18
  # @api private
19
- def self.call(command, names)
20
- full_command_name = full_command_name(names)
19
+ def self.call(command, name)
21
20
  [
22
- command_name(full_command_name),
23
- command_name_and_arguments(command, full_command_name),
21
+ command_name(name),
22
+ command_name_and_arguments(command, name),
24
23
  command_description(command),
24
+ command_subcommands(command),
25
25
  command_arguments(command),
26
26
  command_options(command),
27
- command_examples(command, full_command_name)
27
+ command_examples(command, name)
28
28
  ].compact.join("\n")
29
29
  end
30
30
 
@@ -37,7 +37,11 @@ module Dry
37
37
  # @since 0.1.0
38
38
  # @api private
39
39
  def self.command_name_and_arguments(command, name)
40
- "\nUsage:\n #{name}#{arguments(command)}"
40
+ usage = "\nUsage:\n #{name}#{arguments(command)}"
41
+
42
+ return usage + " | #{name} SUBCOMMAND" if command.subcommands.any?
43
+
44
+ usage
41
45
  end
42
46
 
43
47
  # @since 0.1.0
@@ -45,7 +49,7 @@ module Dry
45
49
  def self.command_examples(command, name)
46
50
  return if command.examples.empty?
47
51
 
48
- "\nExamples:\n#{command.examples.map { |example| " #{name} #{example}" }.join("\n")}" # rubocop:disable Metrics/LineLength
52
+ "\nExamples:\n#{command.examples.map { |example| " #{name} #{example}" }.join("\n")}"
49
53
  end
50
54
 
51
55
  # @since 0.1.0
@@ -56,6 +60,12 @@ module Dry
56
60
  "\nDescription:\n #{command.description}"
57
61
  end
58
62
 
63
+ def self.command_subcommands(command)
64
+ return if command.subcommands.empty?
65
+
66
+ "\nSubcommands:\n#{build_subcommands_list(command.subcommands)}"
67
+ end
68
+
59
69
  # @since 0.1.0
60
70
  # @api private
61
71
  def self.command_arguments(command)
@@ -70,30 +80,24 @@ module Dry
70
80
  "\nOptions:\n#{extended_command_options(command)}"
71
81
  end
72
82
 
73
- # @since 0.1.0
74
- # @api private
75
- def self.full_command_name(names)
76
- ProgramName.call(names)
77
- end
78
-
79
83
  # @since 0.1.0
80
84
  # @api private
81
85
  def self.arguments(command)
82
86
  required_arguments = command.required_arguments
83
87
  optional_arguments = command.optional_arguments
84
88
 
85
- required = required_arguments.map { |arg| arg.name.upcase }.join(' ') if required_arguments.any? # rubocop:disable Metrics/LineLength
86
- optional = optional_arguments.map { |arg| "[#{arg.name.upcase}]" }.join(' ') if optional_arguments.any? # rubocop:disable Metrics/LineLength
89
+ required = required_arguments.map { |arg| arg.name.upcase }.join(" ") if required_arguments.any? # rubocop:disable Metrics/LineLength
90
+ optional = optional_arguments.map { |arg| "[#{arg.name.upcase}]" }.join(" ") if optional_arguments.any? # rubocop:disable Metrics/LineLength
87
91
  result = [required, optional].compact
88
92
 
89
- " #{result.join(' ')}" unless result.empty?
93
+ " #{result.join(" ")}" unless result.empty?
90
94
  end
91
95
 
92
96
  # @since 0.1.0
93
97
  # @api private
94
98
  def self.extended_command_arguments(command)
95
99
  command.arguments.map do |argument|
96
- " #{argument.name.to_s.upcase.ljust(20)}\t# #{'REQUIRED ' if argument.required?}#{argument.desc}" # rubocop:disable Metrics/LineLength
100
+ " #{argument.name.to_s.upcase.ljust(32)} # #{"REQUIRED " if argument.required?}#{argument.desc}" # rubocop:disable Metrics/LineLength
97
101
  end.join("\n")
98
102
  end
99
103
 
@@ -110,16 +114,22 @@ module Dry
110
114
  else
111
115
  "#{name}=VALUE"
112
116
  end
113
- name = "#{name}, #{option.alias_names.join(', ')}" if option.aliases.any?
117
+ name = "#{name}, #{option.alias_names.join(", ")}" if option.aliases.any?
114
118
  name = " --#{name.ljust(30)}"
115
- name = "#{name}\t# #{option.desc}"
119
+ name = "#{name} # #{option.desc}"
116
120
  name = "#{name}, default: #{option.default.inspect}" unless option.default.nil?
117
121
  name
118
122
  end
119
123
 
120
- result << " --#{'help, -h'.ljust(30)}\t# Print this help"
124
+ result << " --#{"help, -h".ljust(30)} # Print this help"
121
125
  result.join("\n")
122
126
  end
127
+
128
+ def self.build_subcommands_list(subcommands)
129
+ subcommands.map do |subcommand_name, subcommand|
130
+ " #{subcommand_name.ljust(32)} # #{subcommand.command.description}"
131
+ end.join("\n")
132
+ end
123
133
  end
124
134
  end
125
135
  end