Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 74 additions & 34 deletions lib/dry/cli/banner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class CLI
#
# @since 0.1.0
# @api private
module Banner
module Banner # rubocop:disable Metrics/ModuleLength
# Prints command banner
#
# @param command [Dry::CLI::Command] the command
Expand All @@ -17,17 +17,56 @@ module Banner
# @since 0.1.0
# @api private
def self.call(command, name)
extended_arguments = extended_command_arguments(command)
extended_examples = extended_command_examples(command, name)
extended_options = extended_command_options(command)
indent = capture_indent(extended_arguments, extended_options, extended_examples)

[
command_name(name),
command_name_and_arguments(command, name),
command_description(command),
command_subcommands(command),
command_arguments(command),
command_options(command),
command_examples(command, name)
command_arguments(extended_arguments, indent),
command_options(extended_options, indent),
command_examples(extended_examples, indent)
].compact.join("\n")
end

# @since unreleased
# @api private
def self.capture_indent(extended_arguments, extended_options, extended_examples)
strings = extended_arguments + extended_options + extended_examples
strings.map { |string, _| string.length }.max + 1
end

# @since unreleased
# @api private
def self.build_option_right(option)
description = option.desc
unless option.default.nil?
description = "#{description}, default: #{option.default.inspect}"
end
description
end

# @since unreleased
# @api private
def self.build_option_left(option)
name = Inflector.dasherize(option.name)
name = if option.boolean?
"--[no-]#{name}"
elsif option.flag?
"--#{name}"
elsif option.array?
"--#{name}=VALUE1,VALUE2,.."
else
"--#{name}=VALUE"
end
name = "#{name}, #{option.alias_names.join(", ")}" if option.aliases.any?
name
end

# @since 0.1.0
# @api private
def self.command_name(name)
Expand All @@ -46,10 +85,13 @@ def self.command_name_and_arguments(command, name)

# @since 0.1.0
# @api private
def self.command_examples(command, name)
return if command.examples.empty?
def self.command_examples(extended_examples, indent)
return if extended_examples.empty?

"\nExamples:\n#{command.examples.map { |example| " #{name} #{example}" }.join("\n")}"
examples = extended_examples.map { |example, description|
" #{example.ljust(indent)} # #{description}"
}
"\nExamples:\n#{examples.join("\n")}"
end

# @since 0.1.0
Expand All @@ -68,16 +110,22 @@ def self.command_subcommands(command)

# @since 0.1.0
# @api private
def self.command_arguments(command)
return if command.arguments.empty?
def self.command_arguments(extended_arguments, indent)
return if extended_arguments.empty?

"\nArguments:\n#{extended_command_arguments(command)}"
arguments = extended_arguments.map { |argument, description|
" #{argument.ljust(indent)} # #{description}"
}
"\nArguments:\n#{arguments.join("\n")}"
end

# @since 0.1.0
# @api private
def self.command_options(command)
"\nOptions:\n#{extended_command_options(command)}"
def self.command_options(extended_options, indent)
options = extended_options.map { |option, description|
" #{option.ljust(indent)} # #{description}"
}
"\nOptions:\n#{options.join("\n")}"
end

# @since 0.1.0
Expand All @@ -97,34 +145,26 @@ def self.arguments(command)
# @api private
def self.extended_command_arguments(command)
command.arguments.map do |argument|
" #{argument.name.to_s.upcase.ljust(32)} # #{"REQUIRED " if argument.required?}#{argument.desc}" # rubocop:disable Layout/LineLength
end.join("\n")
[argument.name.to_s.upcase, "#{"REQUIRED " if argument.required?}#{argument.desc}"]
end
end

# @since 0.1.0
# @api private
#
def self.extended_command_options(command)
result = command.options.map do |option|
name = Inflector.dasherize(option.name)
name = if option.boolean?
"[no-]#{name}"
elsif option.flag?
name
elsif option.array?
"#{name}=VALUE1,VALUE2,.."
else
"#{name}=VALUE"
end
name = "#{name}, #{option.alias_names.join(", ")}" if option.aliases.any?
name = " --#{name.ljust(30)}"
name = "#{name} # #{option.desc}"
name = "#{name}, default: #{option.default.inspect}" unless option.default.nil?
name
def self.extended_command_examples(command, name)
command.examples.map do |example, description|
["#{name} #{example}", description]
end
end

result << " --#{"help, -h".ljust(30)} # Print this help"
result.join("\n")
# @since 0.1.0
# @api private
#
def self.extended_command_options(command)
result = command.options.map { |option|
[build_option_left(option), build_option_right(option)]
}
result << ["--help, -h", "Print this help"]
end

def self.build_subcommands_list(subcommands)
Expand Down
16 changes: 7 additions & 9 deletions lib/dry/cli/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,11 @@ def self.desc(description)
# require "dry/cli"
#
# class Server < Dry::CLI::Command
# example [
# " # Basic usage (it uses the bundled server engine)",
# "--server=webrick # Force `webrick` server engine",
# "--host=0.0.0.0 # Bind to a host",
# "--port=2306 # Bind to a port",
# "--no-code-reloading # Disable code reloading"
# ]
# example "", "Basic usage (it uses the bundled server engine)",
# example "--server=webrick, "Force `webrick` server engine",
# example "--host=0.0.0.0, "Bind to a host",
# example "--port=2306", "Bind to a port",
# example "--no-code-reloading", "Disable code reloading"
#
# def call(*)
# # ...
Expand All @@ -100,8 +98,8 @@ def self.desc(description)
# # foo server --host=0.0.0.0 # Bind to a host
# # foo server --port=2306 # Bind to a port
# # foo server --no-code-reloading # Disable code reloading
def self.example(*examples)
@examples += examples.flatten(1)
def self.example(example, description = "")
@examples.push([example, description])
end

# Specify an argument
Expand Down
2 changes: 1 addition & 1 deletion lib/dry/cli/inline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class CLI
# methods below
#
# DSL consists of 5 methods:
# `desc`, `example`, `argument`, `option` 
# `desc`, `example`, `argument`, `option`
# — are similar to methods from Command class
#
# `run` accepts a block to execute
Expand Down
10 changes: 5 additions & 5 deletions spec/integration/inline_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@
Baz command line interface

Arguments:
MANDATORY_ARG # REQUIRED Mandatory argument
OPTIONAL_ARG # Optional argument (has to have default value in call method)
MANDATORY_ARG # REQUIRED Mandatory argument
OPTIONAL_ARG # Optional argument (has to have default value in call method)

Options:
--option-one=VALUE, -1 VALUE # Option one
--[no-]boolean-option, -b # Option boolean
--option-one=VALUE, -1 VALUE # Option one
--[no-]boolean-option, -b # Option boolean
--option-with-default=VALUE, -d VALUE # Option default, default: "test"
--help, -h # Print this help
--help, -h # Print this help
OUTPUT
expect(output).to eq(expected_output)
end
Expand Down
14 changes: 7 additions & 7 deletions spec/integration/single_command_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,23 @@
output = `baz -h`
expected_output = <<~OUTPUT
Command:
#{cmd}
baz

Usage:
#{cmd} MANDATORY_ARG [OPTIONAL_ARG]
baz MANDATORY_ARG [OPTIONAL_ARG]

Description:
Baz command line interface

Arguments:
MANDATORY_ARG # REQUIRED Mandatory argument
OPTIONAL_ARG # Optional argument (has to have default value in call method)
MANDATORY_ARG # REQUIRED Mandatory argument
OPTIONAL_ARG # Optional argument (has to have default value in call method)

Options:
--option-one=VALUE, -1 VALUE # Option one
--[no-]boolean-option, -b # Option boolean
--option-one=VALUE, -1 VALUE # Option one
--[no-]boolean-option, -b # Option boolean
--option-with-default=VALUE, -d VALUE # Option default, default: "test"
--help, -h # Print this help
--help, -h # Print this help
OUTPUT
expect(output).to eq(expected_output)
end
Expand Down
11 changes: 5 additions & 6 deletions spec/integration/subcommands_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,16 @@
Generate a model

Arguments:
MODEL # REQUIRED Model name (eg. `user`)
MODEL # REQUIRED Model name (eg. `user`)

Options:
--[no-]skip-migration # Skip migration, default: false
--help, -h # Print this help
--[no-]skip-migration # Skip migration, default: false
--help, -h # Print this help

Examples:
foo generate model user # Generate `User` entity, `UserRepository` repository, and the migration
foo generate model user --skip-migration # Generate `User` entity and `UserRepository` repository
foo generate model user # Generate `User` entity, `UserRepository` repository, and the migration
foo generate model user --skip-migration # Generate `User` entity and `UserRepository` repository
DESC

expect(output).to eq(expected)
end
end
Expand Down
14 changes: 5 additions & 9 deletions spec/support/fixtures/based
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,18 @@ module Based
option :num, desc: "number of lines to display"
option :tail, desc: "continually stream log", type: :boolean

example [
"APP_NAME",
"APP_NAME --num=50",
"APP_NAME --tail"
]
example "APP_NAME"
example "APP_NAME --num=50"
example "APP_NAME --tail"
end

class Addons < Base
desc "Lists your add-ons and attachments"

option :json, desc: "return add-ons in json format", type: :boolean, default: false

example [
"APP_NAME",
"APP_NAME --json"
]
example "APP_NAME"
example "APP_NAME --json"
end

register "run", Run
Expand Down
Loading