dry-cli 0.4.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 +7 -0
- data/.codeclimate.yml +12 -0
- data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +10 -0
- data/.github/ISSUE_TEMPLATE/---bug-report.md +30 -0
- data/.github/ISSUE_TEMPLATE/---feature-request.md +18 -0
- data/.github/workflows/custom_ci.yml +77 -0
- data/.github/workflows/docsite.yml +34 -0
- data/.github/workflows/sync_configs.yml +34 -0
- data/.gitignore +11 -0
- data/.rspec +4 -0
- data/.rubocop.yml +89 -0
- data/CHANGELOG.md +70 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/CONTRIBUTING.md +29 -0
- data/Gemfile +18 -0
- data/LICENSE +20 -0
- data/README.md +31 -0
- data/Rakefile +16 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/docsite/source/index.html.md +588 -0
- data/dry-cli.gemspec +36 -0
- data/lib/dry/cli.rb +129 -0
- data/lib/dry/cli/banner.rb +127 -0
- data/lib/dry/cli/command.rb +367 -0
- data/lib/dry/cli/command_registry.rb +211 -0
- data/lib/dry/cli/errors.rb +39 -0
- data/lib/dry/cli/option.rb +132 -0
- data/lib/dry/cli/parser.rb +140 -0
- data/lib/dry/cli/program_name.rb +21 -0
- data/lib/dry/cli/registry.rb +328 -0
- data/lib/dry/cli/usage.rb +91 -0
- data/lib/dry/cli/utils/files.rb +443 -0
- data/lib/dry/cli/version.rb +8 -0
- data/script/ci +51 -0
- metadata +169 -0
data/dry-cli.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'dry/cli/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'dry-cli'
|
9
|
+
spec.version = Dry::CLI::VERSION
|
10
|
+
spec.authors = ['Luca Guidi']
|
11
|
+
spec.email = ['me@lucaguidi.com']
|
12
|
+
spec.licenses = ['MIT']
|
13
|
+
|
14
|
+
spec.summary = 'Dry CLI'
|
15
|
+
spec.description = 'Common framework to build command line interfaces with Ruby'
|
16
|
+
spec.homepage = 'http://dry-rb.org'
|
17
|
+
|
18
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
19
|
+
spec.metadata['source_code_uri'] = 'https://github.com/dry-rb/dry-cli'
|
20
|
+
|
21
|
+
spec.bindir = 'exe'
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
26
|
+
f.match(%r{^(test|spec|features)/})
|
27
|
+
end
|
28
|
+
|
29
|
+
spec.add_dependency 'concurrent-ruby', '~> 1.0'
|
30
|
+
spec.add_dependency 'hanami-utils', '~> 1.3'
|
31
|
+
|
32
|
+
spec.add_development_dependency 'bundler', '>= 1.6', '< 3'
|
33
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
34
|
+
spec.add_development_dependency 'rspec', '~> 3.7'
|
35
|
+
spec.add_development_dependency 'simplecov', '~> 0.17.1'
|
36
|
+
end
|
data/lib/dry/cli.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Dry
|
4
|
+
#
|
5
|
+
# @since 0.1.0
|
6
|
+
module Dry
|
7
|
+
# General purpose Command Line Interface (CLI) framework for Ruby
|
8
|
+
#
|
9
|
+
# @since 0.1.0
|
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
|
+
|
19
|
+
# Check if command
|
20
|
+
#
|
21
|
+
# @param command [Object] the command to check
|
22
|
+
#
|
23
|
+
# @return [TrueClass,FalseClass] true if instance of `Dry::CLI::Command`
|
24
|
+
#
|
25
|
+
# @since 0.1.0
|
26
|
+
# @api private
|
27
|
+
def self.command?(command)
|
28
|
+
case command
|
29
|
+
when Class
|
30
|
+
command.ancestors.include?(Command)
|
31
|
+
else
|
32
|
+
command.is_a?(Command)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Create a new instance
|
37
|
+
#
|
38
|
+
# @param registry [Dry::CLI::Registry] a registry
|
39
|
+
#
|
40
|
+
# @return [Dry::CLI] the new instance
|
41
|
+
# @since 0.1.0
|
42
|
+
def initialize(registry)
|
43
|
+
@commands = registry
|
44
|
+
end
|
45
|
+
|
46
|
+
# Invoke the CLI
|
47
|
+
#
|
48
|
+
# @param arguments [Array<string>] the command line arguments (defaults to `ARGV`)
|
49
|
+
# @param out [IO] the standard output (defaults to `$stdout`)
|
50
|
+
#
|
51
|
+
# @since 0.1.0
|
52
|
+
def call(arguments: ARGV, out: $stdout)
|
53
|
+
result = commands.get(arguments)
|
54
|
+
|
55
|
+
if result.found?
|
56
|
+
command, args = parse(result, out)
|
57
|
+
|
58
|
+
result.before_callbacks.run(command, args)
|
59
|
+
command.call(args)
|
60
|
+
result.after_callbacks.run(command, args)
|
61
|
+
else
|
62
|
+
usage(result, out)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# @since 0.1.0
|
69
|
+
# @api private
|
70
|
+
attr_reader :commands
|
71
|
+
|
72
|
+
# Parse arguments for a command.
|
73
|
+
#
|
74
|
+
# It may exit in case of error, or in case of help.
|
75
|
+
#
|
76
|
+
# @param result [Dry::CLI::CommandRegistry::LookupResult]
|
77
|
+
# @param out [IO] sta output
|
78
|
+
#
|
79
|
+
# @return [Array<Dry:CLI::Command, Array>] returns an array where the
|
80
|
+
# first element is a command and the second one is the list of arguments
|
81
|
+
#
|
82
|
+
# @since 0.1.0
|
83
|
+
# @api private
|
84
|
+
def parse(result, out)
|
85
|
+
command = result.command
|
86
|
+
return [command, result.arguments] unless command?(command)
|
87
|
+
|
88
|
+
result = Parser.call(command, result.arguments, result.names)
|
89
|
+
|
90
|
+
if result.help?
|
91
|
+
Banner.call(command, out)
|
92
|
+
exit(0)
|
93
|
+
end
|
94
|
+
|
95
|
+
if result.error?
|
96
|
+
out.puts(result.error)
|
97
|
+
exit(1)
|
98
|
+
end
|
99
|
+
|
100
|
+
[command, result.arguments]
|
101
|
+
end
|
102
|
+
|
103
|
+
# Prints the command usage and exit.
|
104
|
+
#
|
105
|
+
# @param result [Dry::CLI::CommandRegistry::LookupResult]
|
106
|
+
# @param out [IO] sta output
|
107
|
+
#
|
108
|
+
# @since 0.1.0
|
109
|
+
# @api private
|
110
|
+
def usage(result, out)
|
111
|
+
Usage.call(result, out)
|
112
|
+
exit(1)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Check if command
|
116
|
+
#
|
117
|
+
# @param command [Object] the command to check
|
118
|
+
#
|
119
|
+
# @return [TrueClass,FalseClass] true if instance of `Dry::CLI::Command`
|
120
|
+
#
|
121
|
+
# @since 0.1.0
|
122
|
+
# @api private
|
123
|
+
#
|
124
|
+
# @see .command?
|
125
|
+
def command?(command)
|
126
|
+
CLI.command?(command)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/cli/program_name'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
class CLI
|
7
|
+
# Command banner
|
8
|
+
#
|
9
|
+
# @since 0.1.0
|
10
|
+
# @api private
|
11
|
+
module Banner
|
12
|
+
# Prints command banner
|
13
|
+
#
|
14
|
+
# @param command [Dry::CLI::Command] the command
|
15
|
+
# @param out [IO] standard output
|
16
|
+
#
|
17
|
+
# @since 0.1.0
|
18
|
+
# @api private
|
19
|
+
def self.call(command, out)
|
20
|
+
output = [
|
21
|
+
command_name(command),
|
22
|
+
command_name_and_arguments(command),
|
23
|
+
command_description(command),
|
24
|
+
command_arguments(command),
|
25
|
+
command_options(command),
|
26
|
+
command_examples(command)
|
27
|
+
].compact.join("\n")
|
28
|
+
|
29
|
+
out.puts output
|
30
|
+
end
|
31
|
+
|
32
|
+
# @since 0.1.0
|
33
|
+
# @api private
|
34
|
+
def self.command_name(command)
|
35
|
+
"Command:\n #{full_command_name(command)}"
|
36
|
+
end
|
37
|
+
|
38
|
+
# @since 0.1.0
|
39
|
+
# @api private
|
40
|
+
def self.command_name_and_arguments(command)
|
41
|
+
"\nUsage:\n #{full_command_name(command)}#{arguments(command)}"
|
42
|
+
end
|
43
|
+
|
44
|
+
# @since 0.1.0
|
45
|
+
# @api private
|
46
|
+
def self.command_examples(command)
|
47
|
+
return if command.examples.empty?
|
48
|
+
|
49
|
+
"\nExamples:\n#{command.examples.map { |example| " #{full_command_name(command)} #{example}" }.join("\n")}" # rubocop:disable Metrics/LineLength
|
50
|
+
end
|
51
|
+
|
52
|
+
# @since 0.1.0
|
53
|
+
# @api private
|
54
|
+
def self.command_description(command)
|
55
|
+
return if command.description.nil?
|
56
|
+
|
57
|
+
"\nDescription:\n #{command.description}"
|
58
|
+
end
|
59
|
+
|
60
|
+
# @since 0.1.0
|
61
|
+
# @api private
|
62
|
+
def self.command_arguments(command)
|
63
|
+
return if command.arguments.empty?
|
64
|
+
|
65
|
+
"\nArguments:\n#{extended_command_arguments(command)}"
|
66
|
+
end
|
67
|
+
|
68
|
+
# @since 0.1.0
|
69
|
+
# @api private
|
70
|
+
def self.command_options(command)
|
71
|
+
"\nOptions:\n#{extended_command_options(command)}"
|
72
|
+
end
|
73
|
+
|
74
|
+
# @since 0.1.0
|
75
|
+
# @api private
|
76
|
+
def self.full_command_name(command)
|
77
|
+
ProgramName.call(command.command_name)
|
78
|
+
end
|
79
|
+
|
80
|
+
# @since 0.1.0
|
81
|
+
# @api private
|
82
|
+
def self.arguments(command)
|
83
|
+
required_arguments = command.required_arguments
|
84
|
+
optional_arguments = command.optional_arguments
|
85
|
+
|
86
|
+
required = required_arguments.map { |arg| arg.name.upcase }.join(' ') if required_arguments.any? # rubocop:disable Metrics/LineLength
|
87
|
+
optional = optional_arguments.map { |arg| "[#{arg.name.upcase}]" }.join(' ') if optional_arguments.any? # rubocop:disable Metrics/LineLength
|
88
|
+
result = [required, optional].compact
|
89
|
+
|
90
|
+
" #{result.join(' ')}" unless result.empty?
|
91
|
+
end
|
92
|
+
|
93
|
+
# @since 0.1.0
|
94
|
+
# @api private
|
95
|
+
def self.extended_command_arguments(command)
|
96
|
+
command.arguments.map do |argument|
|
97
|
+
" #{argument.name.to_s.upcase.ljust(20)}\t# #{'REQUIRED ' if argument.required?}#{argument.desc}" # rubocop:disable Metrics/LineLength
|
98
|
+
end.join("\n")
|
99
|
+
end
|
100
|
+
|
101
|
+
# @since 0.1.0
|
102
|
+
# @api private
|
103
|
+
#
|
104
|
+
# rubocop:disable Metrics/AbcSize
|
105
|
+
def self.extended_command_options(command)
|
106
|
+
result = command.options.map do |option|
|
107
|
+
name = Hanami::Utils::String.dasherize(option.name)
|
108
|
+
name = if option.boolean?
|
109
|
+
"[no-]#{name}"
|
110
|
+
else
|
111
|
+
"#{name}=VALUE"
|
112
|
+
end
|
113
|
+
|
114
|
+
name = "#{name}, #{option.aliases.map { |a| a.start_with?('--') ? "#{a}=VALUE" : "#{a} VALUE" }.join(', ')}" unless option.aliases.empty? # rubocop:disable Metrics/LineLength
|
115
|
+
name = " --#{name.ljust(30)}"
|
116
|
+
name = "#{name}\t# #{option.desc}"
|
117
|
+
name = "#{name}, default: #{option.default.inspect}" unless option.default.nil?
|
118
|
+
name
|
119
|
+
end
|
120
|
+
|
121
|
+
result << " --#{'help, -h'.ljust(30)}\t# Print this help"
|
122
|
+
result.join("\n")
|
123
|
+
end
|
124
|
+
# rubocop:enable Metrics/AbcSize
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,367 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'concurrent/array'
|
5
|
+
require 'dry/cli/option'
|
6
|
+
|
7
|
+
module Dry
|
8
|
+
class CLI
|
9
|
+
# Base class for commands
|
10
|
+
#
|
11
|
+
# @since 0.1.0
|
12
|
+
class Command
|
13
|
+
# @since 0.1.0
|
14
|
+
# @api private
|
15
|
+
def self.inherited(base)
|
16
|
+
super
|
17
|
+
base.extend ClassMethods
|
18
|
+
end
|
19
|
+
|
20
|
+
# @since 0.1.0
|
21
|
+
# @api private
|
22
|
+
module ClassMethods
|
23
|
+
# @since 0.1.0
|
24
|
+
# @api private
|
25
|
+
def self.extended(base)
|
26
|
+
super
|
27
|
+
|
28
|
+
base.class_eval do
|
29
|
+
@description = nil
|
30
|
+
@examples = Concurrent::Array.new
|
31
|
+
@arguments = Concurrent::Array.new
|
32
|
+
@options = Concurrent::Array.new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @since 0.1.0
|
37
|
+
# @api private
|
38
|
+
attr_reader :description
|
39
|
+
|
40
|
+
# @since 0.1.0
|
41
|
+
# @api private
|
42
|
+
attr_reader :examples
|
43
|
+
|
44
|
+
# @since 0.1.0
|
45
|
+
# @api private
|
46
|
+
attr_reader :arguments
|
47
|
+
|
48
|
+
# @since 0.1.0
|
49
|
+
# @api private
|
50
|
+
attr_reader :options
|
51
|
+
end
|
52
|
+
|
53
|
+
# Set the description of the command
|
54
|
+
#
|
55
|
+
# @param description [String] the description
|
56
|
+
#
|
57
|
+
# @since 0.1.0
|
58
|
+
#
|
59
|
+
# @example
|
60
|
+
# require "dry/cli"
|
61
|
+
#
|
62
|
+
# class Echo < Dry::CLI::Command
|
63
|
+
# desc "Prints given input"
|
64
|
+
#
|
65
|
+
# def call(*)
|
66
|
+
# # ...
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
def self.desc(description)
|
70
|
+
@description = description
|
71
|
+
end
|
72
|
+
|
73
|
+
# Describe the usage of the command
|
74
|
+
#
|
75
|
+
# @param examples [Array<String>] one or more examples
|
76
|
+
#
|
77
|
+
# @since 0.1.0
|
78
|
+
#
|
79
|
+
# @example
|
80
|
+
# require "dry/cli"
|
81
|
+
#
|
82
|
+
# class Server < Dry::CLI::Command
|
83
|
+
# example [
|
84
|
+
# " # Basic usage (it uses the bundled server engine)",
|
85
|
+
# "--server=webrick # Force `webrick` server engine",
|
86
|
+
# "--host=0.0.0.0 # Bind to a host",
|
87
|
+
# "--port=2306 # Bind to a port",
|
88
|
+
# "--no-code-reloading # Disable code reloading"
|
89
|
+
# ]
|
90
|
+
#
|
91
|
+
# def call(*)
|
92
|
+
# # ...
|
93
|
+
# end
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# # $ foo server --help
|
97
|
+
# # # ...
|
98
|
+
# #
|
99
|
+
# # Examples:
|
100
|
+
# # foo server # Basic usage (it uses the bundled server engine)
|
101
|
+
# # foo server --server=webrick # Force `webrick` server engine
|
102
|
+
# # foo server --host=0.0.0.0 # Bind to a host
|
103
|
+
# # foo server --port=2306 # Bind to a port
|
104
|
+
# # foo server --no-code-reloading # Disable code reloading
|
105
|
+
def self.example(*examples)
|
106
|
+
@examples += examples.flatten
|
107
|
+
end
|
108
|
+
|
109
|
+
# Specify an argument
|
110
|
+
#
|
111
|
+
# @param name [Symbol] the argument name
|
112
|
+
# @param options [Hash] a set of options
|
113
|
+
#
|
114
|
+
# @since 0.1.0
|
115
|
+
#
|
116
|
+
# @example Optional argument
|
117
|
+
# require "dry/cli"
|
118
|
+
#
|
119
|
+
# class Hello < Dry::CLI::Command
|
120
|
+
# argument :name
|
121
|
+
#
|
122
|
+
# def call(name: nil, **)
|
123
|
+
# if name.nil?
|
124
|
+
# puts "Hello, stranger"
|
125
|
+
# else
|
126
|
+
# puts "Hello, #{name}"
|
127
|
+
# end
|
128
|
+
# end
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# # $ foo hello
|
132
|
+
# # Hello, stranger
|
133
|
+
#
|
134
|
+
# # $ foo hello Luca
|
135
|
+
# # Hello, Luca
|
136
|
+
#
|
137
|
+
# @example Required argument
|
138
|
+
# require "dry/cli"
|
139
|
+
#
|
140
|
+
# class Hello < Dry::CLI::Command
|
141
|
+
# argument :name, required: true
|
142
|
+
#
|
143
|
+
# def call(name:, **)
|
144
|
+
# puts "Hello, #{name}"
|
145
|
+
# end
|
146
|
+
# end
|
147
|
+
#
|
148
|
+
# # $ foo hello Luca
|
149
|
+
# # Hello, Luca
|
150
|
+
#
|
151
|
+
# # $ foo hello
|
152
|
+
# # ERROR: "foo hello" was called with no arguments
|
153
|
+
# # Usage: "foo hello NAME"
|
154
|
+
#
|
155
|
+
# @example Multiple arguments
|
156
|
+
# require "dry/cli"
|
157
|
+
#
|
158
|
+
# module Generate
|
159
|
+
# class Action < Dry::CLI::Command
|
160
|
+
# argument :app, required: true
|
161
|
+
# argument :action, required: true
|
162
|
+
#
|
163
|
+
# def call(app:, action:, **)
|
164
|
+
# puts "Generating action: #{action} for app: #{app}"
|
165
|
+
# end
|
166
|
+
# end
|
167
|
+
# end
|
168
|
+
#
|
169
|
+
# # $ foo generate action web home
|
170
|
+
# # Generating action: home for app: web
|
171
|
+
#
|
172
|
+
# # $ foo generate action
|
173
|
+
# # ERROR: "foo generate action" was called with no arguments
|
174
|
+
# # Usage: "foo generate action APP ACTION"
|
175
|
+
#
|
176
|
+
# @example Description
|
177
|
+
# require "dry/cli"
|
178
|
+
#
|
179
|
+
# class Hello < Dry::CLI::Command
|
180
|
+
# argument :name, desc: "The name of the person to greet"
|
181
|
+
#
|
182
|
+
# def call(name: nil, **)
|
183
|
+
# # ...
|
184
|
+
# end
|
185
|
+
# end
|
186
|
+
#
|
187
|
+
# # $ foo hello --help
|
188
|
+
# # Command:
|
189
|
+
# # foo hello
|
190
|
+
# #
|
191
|
+
# # Usage:
|
192
|
+
# # foo hello [NAME]
|
193
|
+
# #
|
194
|
+
# # Arguments:
|
195
|
+
# # NAME # The name of the person to greet
|
196
|
+
# #
|
197
|
+
# # Options:
|
198
|
+
# # --help, -h # Print this help
|
199
|
+
def self.argument(name, options = {})
|
200
|
+
@arguments << Argument.new(name, options)
|
201
|
+
end
|
202
|
+
|
203
|
+
# Command line option (aka optional argument)
|
204
|
+
#
|
205
|
+
# @param name [Symbol] the param name
|
206
|
+
# @param options [Hash] a set of options
|
207
|
+
#
|
208
|
+
# @since 0.1.0
|
209
|
+
#
|
210
|
+
# @example Basic usage
|
211
|
+
# require "dry/cli"
|
212
|
+
#
|
213
|
+
# class Console < Dry::CLI::Command
|
214
|
+
# param :engine
|
215
|
+
#
|
216
|
+
# def call(engine: nil, **)
|
217
|
+
# puts "starting console (engine: #{engine || :irb})"
|
218
|
+
# end
|
219
|
+
# end
|
220
|
+
#
|
221
|
+
# # $ foo console
|
222
|
+
# # starting console (engine: irb)
|
223
|
+
#
|
224
|
+
# # $ foo console --engine=pry
|
225
|
+
# # starting console (engine: pry)
|
226
|
+
#
|
227
|
+
# @example List values
|
228
|
+
# require "dry/cli"
|
229
|
+
#
|
230
|
+
# class Console < Dry::CLI::Command
|
231
|
+
# param :engine, values: %w(irb pry ripl)
|
232
|
+
#
|
233
|
+
# def call(engine: nil, **)
|
234
|
+
# puts "starting console (engine: #{engine || :irb})"
|
235
|
+
# end
|
236
|
+
# end
|
237
|
+
#
|
238
|
+
# # $ foo console
|
239
|
+
# # starting console (engine: irb)
|
240
|
+
#
|
241
|
+
# # $ foo console --engine=pry
|
242
|
+
# # starting console (engine: pry)
|
243
|
+
#
|
244
|
+
# # $ foo console --engine=foo
|
245
|
+
# # Error: Invalid param provided
|
246
|
+
#
|
247
|
+
# @example Description
|
248
|
+
# require "dry/cli"
|
249
|
+
#
|
250
|
+
# class Console < Dry::CLI::Command
|
251
|
+
# param :engine, desc: "Force a console engine"
|
252
|
+
#
|
253
|
+
# def call(engine: nil, **)
|
254
|
+
# # ...
|
255
|
+
# end
|
256
|
+
# end
|
257
|
+
#
|
258
|
+
# # $ foo console --help
|
259
|
+
# # # ...
|
260
|
+
# #
|
261
|
+
# # Options:
|
262
|
+
# # --engine=VALUE # Force a console engine: (irb/pry/ripl)
|
263
|
+
# # --help, -h # Print this help
|
264
|
+
#
|
265
|
+
# @example Boolean
|
266
|
+
# require "dry/cli"
|
267
|
+
#
|
268
|
+
# class Server < Dry::CLI::Command
|
269
|
+
# param :code_reloading, type: :boolean, default: true
|
270
|
+
#
|
271
|
+
# def call(code_reloading:, **)
|
272
|
+
# puts "staring server (code reloading: #{code_reloading})"
|
273
|
+
# end
|
274
|
+
# end
|
275
|
+
#
|
276
|
+
# # $ foo server
|
277
|
+
# # starting server (code reloading: true)
|
278
|
+
#
|
279
|
+
# # $ foo server --no-code-reloading
|
280
|
+
# # starting server (code reloading: false)
|
281
|
+
#
|
282
|
+
# # $ foo server --help
|
283
|
+
# # # ...
|
284
|
+
# #
|
285
|
+
# # Options:
|
286
|
+
# # --[no]-code-reloading
|
287
|
+
#
|
288
|
+
# @example Aliases
|
289
|
+
# require "dry/cli"
|
290
|
+
#
|
291
|
+
# class Server < Dry::CLI::Command
|
292
|
+
# param :port, aliases: ["-p"]
|
293
|
+
#
|
294
|
+
# def call(options)
|
295
|
+
# puts "staring server (port: #{options.fetch(:port, 2300)})"
|
296
|
+
# end
|
297
|
+
# end
|
298
|
+
#
|
299
|
+
# # $ foo server
|
300
|
+
# # starting server (port: 2300)
|
301
|
+
#
|
302
|
+
# # $ foo server --port=2306
|
303
|
+
# # starting server (port: 2306)
|
304
|
+
#
|
305
|
+
# # $ foo server -p 2306
|
306
|
+
# # starting server (port: 2306)
|
307
|
+
#
|
308
|
+
# # $ foo server --help
|
309
|
+
# # # ...
|
310
|
+
# #
|
311
|
+
# # Options:
|
312
|
+
# # --port=VALUE, -p VALUE
|
313
|
+
def self.option(name, options = {})
|
314
|
+
@options << Option.new(name, options)
|
315
|
+
end
|
316
|
+
|
317
|
+
# @since 0.1.0
|
318
|
+
# @api private
|
319
|
+
def self.params
|
320
|
+
(@arguments + @options).uniq
|
321
|
+
end
|
322
|
+
|
323
|
+
# @since 0.1.0
|
324
|
+
# @api private
|
325
|
+
def self.default_params
|
326
|
+
params.each_with_object({}) do |param, result|
|
327
|
+
result[param.name] = param.default unless param.default.nil?
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
# @since 0.1.0
|
332
|
+
# @api private
|
333
|
+
def self.required_arguments
|
334
|
+
arguments.select(&:required?)
|
335
|
+
end
|
336
|
+
|
337
|
+
# @since 0.1.0
|
338
|
+
# @api private
|
339
|
+
def self.optional_arguments
|
340
|
+
arguments.reject(&:required?)
|
341
|
+
end
|
342
|
+
|
343
|
+
extend Forwardable
|
344
|
+
|
345
|
+
delegate %i[
|
346
|
+
description
|
347
|
+
examples
|
348
|
+
arguments
|
349
|
+
options
|
350
|
+
params
|
351
|
+
default_params
|
352
|
+
required_arguments
|
353
|
+
optional_arguments
|
354
|
+
] => 'self.class'
|
355
|
+
|
356
|
+
# @since 0.1.0
|
357
|
+
# @api private
|
358
|
+
attr_reader :command_name
|
359
|
+
|
360
|
+
# @since 0.1.0
|
361
|
+
# @api private
|
362
|
+
def initialize(command_name:, **)
|
363
|
+
@command_name = command_name
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|