dry-cli-help 0.1.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/CHANGELOG.md +9 -0
- data/LICENSE.txt +21 -0
- data/README.md +248 -0
- data/Rakefile +38 -0
- data/SPECIFICATION.md +200 -0
- data/lib/dry/cli/help/colors.rb +71 -0
- data/lib/dry/cli/help/configuration.rb +241 -0
- data/lib/dry/cli/help/formatter.rb +107 -0
- data/lib/dry/cli/help/integration.rb +84 -0
- data/lib/dry/cli/help/screens/base.rb +87 -0
- data/lib/dry/cli/help/screens/command.rb +126 -0
- data/lib/dry/cli/help/screens/listing.rb +100 -0
- data/lib/dry/cli/help/terminal.rb +39 -0
- data/lib/dry/cli/help/text.rb +66 -0
- data/lib/dry/cli/help/version.rb +15 -0
- data/lib/dry/cli/help.rb +60 -0
- data/lib/dry-cli-help.rb +3 -0
- data/sig/dry/cli/help.rbs +88 -0
- metadata +99 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
class CLI
|
|
5
|
+
module Help
|
|
6
|
+
module Screens
|
|
7
|
+
# The help printed for a registry level: `mycli`, `mycli -h`, and a group
|
|
8
|
+
# such as `mycli db` whose node has no command of its own.
|
|
9
|
+
class Listing < Base
|
|
10
|
+
SECTIONS = %i[banner usage commands options epilogue].freeze
|
|
11
|
+
|
|
12
|
+
# @param result [Dry::CLI::CommandRegistry::LookupResult] the level to list
|
|
13
|
+
def initialize(result:, **)
|
|
14
|
+
super(**)
|
|
15
|
+
@result = result
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
attr_reader :result
|
|
21
|
+
|
|
22
|
+
def top_level?
|
|
23
|
+
result.names.empty?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def banner?
|
|
27
|
+
top_level? || config.banner_on_subcommands
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def epilogue?
|
|
31
|
+
top_level?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def render_usage
|
|
35
|
+
program = format.paint(ProgramName.call(result.names), :command)
|
|
36
|
+
section(:usage, ["#{INDENT}#{program} COMMAND [OPTIONS]"])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def render_commands
|
|
40
|
+
blocks = []
|
|
41
|
+
ungrouped = command_rows.except(*grouped).values
|
|
42
|
+
blocks << section(:commands, definitions(ungrouped)) if ungrouped.any?
|
|
43
|
+
|
|
44
|
+
config.groups.each do |name, paths|
|
|
45
|
+
rows = paths.filter_map { command_rows[it] }
|
|
46
|
+
blocks << [format.title(name), *definitions(rows)] if rows.any?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
blocks.inject { |above, below| above + [""] + below }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def render_options
|
|
53
|
+
section(:options, definitions([HELP, *option_rows]))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def aligned_rows
|
|
57
|
+
command_rows.values + option_rows
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def grouped
|
|
61
|
+
@grouped ||= config.groups.flat_map(&:last)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Commands keyed by full path, so a group can name "db migrate".
|
|
65
|
+
def command_rows
|
|
66
|
+
@command_rows ||= entries.each_with_object({}) do |(name, node, aliases), rows|
|
|
67
|
+
next if name.start_with?("-")
|
|
68
|
+
|
|
69
|
+
term = [name, *aliases.reject { it.start_with?("-") }].join(", ")
|
|
70
|
+
rows[[*result.names, name].join(" ")] = Row.new(term:, text: describe_node(node))
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# A command reachable as `--version` or `-v` reads as an option, so it
|
|
75
|
+
# lists under Options by its dashed names.
|
|
76
|
+
def option_rows
|
|
77
|
+
@option_rows ||= entries.filter_map do |name, node, aliases|
|
|
78
|
+
dashed = [name, *aliases].select { it.start_with?("-") }
|
|
79
|
+
next if dashed.empty?
|
|
80
|
+
|
|
81
|
+
term = dashed.sort_by { [it.length, it] }.join(", ")
|
|
82
|
+
Row.new(term:, text: describe_node(node), term_style: :option)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def entries
|
|
87
|
+
@entries ||= visible(result.children).map do |name, node|
|
|
88
|
+
[name, node, aliases_of(node)]
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# dry-cli files an alias on the parent node, pointing at the child.
|
|
93
|
+
def aliases_of(node)
|
|
94
|
+
node.parent.aliases.filter_map { |name, target| name if target.equal?(node) }
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "io/console"
|
|
4
|
+
|
|
5
|
+
module Dry
|
|
6
|
+
class CLI
|
|
7
|
+
module Help
|
|
8
|
+
# The width of the terminal help prints to.
|
|
9
|
+
module Terminal
|
|
10
|
+
FALLBACK_WIDTH = 80
|
|
11
|
+
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
# `COLUMNS` wins over the console, so a user or a test can pin the width.
|
|
15
|
+
#
|
|
16
|
+
# @param env [Hash, ENV]
|
|
17
|
+
# @param console [IO, nil] the controlling terminal, nil without one
|
|
18
|
+
# @return [Integer]
|
|
19
|
+
def width(env: ENV, console: IO.console)
|
|
20
|
+
from_env(env) || from_console(console) || FALLBACK_WIDTH
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [Integer, nil]
|
|
24
|
+
def from_env(env)
|
|
25
|
+
columns = Integer(env.fetch("COLUMNS", ""), exception: false)
|
|
26
|
+
columns if columns&.positive?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @return [Integer, nil]
|
|
30
|
+
def from_console(console)
|
|
31
|
+
columns = console&.winsize&.last
|
|
32
|
+
columns if columns&.positive?
|
|
33
|
+
rescue SystemCallError
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
class CLI
|
|
5
|
+
module Help
|
|
6
|
+
# Word wrapping for descriptions, epilogues and anything else a user writes.
|
|
7
|
+
module Text
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
# Split text into lines no wider than `width`.
|
|
11
|
+
#
|
|
12
|
+
# Paragraphs split on blank lines and reflow, so a heredoc wraps to the
|
|
13
|
+
# terminal instead of keeping the line breaks of the editor it was
|
|
14
|
+
# written in. A line starting with whitespace prints verbatim, which
|
|
15
|
+
# keeps indented lists and code intact. Consecutive blank lines collapse
|
|
16
|
+
# to one. A word longer than `width` gets a line to itself.
|
|
17
|
+
#
|
|
18
|
+
# @param text [String, nil]
|
|
19
|
+
# @param width [Integer, nil] nil keeps every line as written
|
|
20
|
+
# @return [Array<String>] lines, with "" between paragraphs
|
|
21
|
+
def lines(text, width)
|
|
22
|
+
source = text.to_s.sub(/\A(?:[ \t]*\n)+/, "").rstrip
|
|
23
|
+
return source.lines(chomp: true) if width.nil?
|
|
24
|
+
|
|
25
|
+
reflow(source, width)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [Array<String>]
|
|
29
|
+
def reflow(source, width)
|
|
30
|
+
output = []
|
|
31
|
+
paragraph = []
|
|
32
|
+
flush = lambda do
|
|
33
|
+
output.concat(fill(paragraph.join(" "), width)) unless paragraph.empty?
|
|
34
|
+
paragraph.clear
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
source.each_line(chomp: true) do |line|
|
|
38
|
+
if line.strip.empty?
|
|
39
|
+
flush.call
|
|
40
|
+
output << "" unless output.last == ""
|
|
41
|
+
elsif line.start_with?(" ", "\t")
|
|
42
|
+
flush.call
|
|
43
|
+
output << line.rstrip
|
|
44
|
+
else
|
|
45
|
+
paragraph << line.strip
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
flush.call
|
|
49
|
+
output
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Greedy fill of one paragraph.
|
|
53
|
+
# @return [Array<String>]
|
|
54
|
+
def fill(text, width)
|
|
55
|
+
text.split.each_with_object([]) do |word, lines|
|
|
56
|
+
if lines.empty? || lines.last.length + 1 + word.length > width
|
|
57
|
+
lines << word.dup
|
|
58
|
+
else
|
|
59
|
+
lines.last << " " << word
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
# Reopened rather than defined: dry-cli declares `class CLI`, not a module,
|
|
5
|
+
# and getting that wrong raises TypeError the moment both are loaded.
|
|
6
|
+
#
|
|
7
|
+
# Declared here without requiring dry-cli, because the gemspec loads this
|
|
8
|
+
# file at build time when the dependency may not be installed. dry-cli's CLI
|
|
9
|
+
# inherits from Object, so an empty reopening is compatible either way.
|
|
10
|
+
class CLI
|
|
11
|
+
module Help
|
|
12
|
+
VERSION = "0.1.0"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/dry/cli/help.rb
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/cli"
|
|
4
|
+
require "pastel"
|
|
5
|
+
|
|
6
|
+
require_relative "help/version"
|
|
7
|
+
require_relative "help/colors"
|
|
8
|
+
require_relative "help/terminal"
|
|
9
|
+
require_relative "help/text"
|
|
10
|
+
require_relative "help/configuration"
|
|
11
|
+
require_relative "help/formatter"
|
|
12
|
+
require_relative "help/screens/base"
|
|
13
|
+
require_relative "help/screens/listing"
|
|
14
|
+
require_relative "help/screens/command"
|
|
15
|
+
require_relative "help/integration"
|
|
16
|
+
|
|
17
|
+
module Dry
|
|
18
|
+
class CLI
|
|
19
|
+
# Configurable, wrapped and colored help screens for dry-cli.
|
|
20
|
+
#
|
|
21
|
+
# Requiring this file changes the help every Dry::CLI in the process prints.
|
|
22
|
+
# Settings come from {.configure} for the whole process, and from a
|
|
23
|
+
# registry's `help` block for that registry.
|
|
24
|
+
module Help
|
|
25
|
+
class << self
|
|
26
|
+
# @yieldparam config [Configuration] the process-wide settings
|
|
27
|
+
# @return [Configuration]
|
|
28
|
+
def configure
|
|
29
|
+
yield config
|
|
30
|
+
config
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [Configuration] the process-wide settings
|
|
34
|
+
def config
|
|
35
|
+
@config ||= Configuration.new
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Forget every process-wide setting.
|
|
39
|
+
# @return [void]
|
|
40
|
+
def reset!
|
|
41
|
+
@config = nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The settings a registry renders with: its own `help` block over the
|
|
45
|
+
# process-wide settings. A single command passed to Dry::CLI.new has no
|
|
46
|
+
# `help` block, so it renders with the process-wide settings alone.
|
|
47
|
+
#
|
|
48
|
+
# @param registry [Module, Class, Dry::CLI::Command, nil]
|
|
49
|
+
# @return [Configuration]
|
|
50
|
+
def config_for(registry)
|
|
51
|
+
own = registry.help_config if registry.respond_to?(:help_config)
|
|
52
|
+
own ? config.merge(own) : config
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
Dry::CLI.prepend(Dry::CLI::Help::Integration::CLIMethods)
|
|
60
|
+
Dry::CLI::Registry.include(Dry::CLI::Help::Integration::RegistryMethods)
|
data/lib/dry-cli-help.rb
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module Dry
|
|
2
|
+
class CLI
|
|
3
|
+
module Help
|
|
4
|
+
VERSION: String
|
|
5
|
+
|
|
6
|
+
def self.configure: () { (Configuration) -> void } -> Configuration
|
|
7
|
+
def self.config: () -> Configuration
|
|
8
|
+
def self.reset!: () -> void
|
|
9
|
+
def self.config_for: (untyped registry) -> Configuration
|
|
10
|
+
|
|
11
|
+
module Colors
|
|
12
|
+
STYLES: Array[Symbol]
|
|
13
|
+
SETTINGS: Array[bool | Symbol]
|
|
14
|
+
|
|
15
|
+
def self.enabled: () -> (bool | Symbol)
|
|
16
|
+
def self.enabled=: (bool | Symbol) -> void
|
|
17
|
+
def self.pastel: () -> untyped
|
|
18
|
+
def self.enabled_for?: (bool | Symbol setting, untyped io) -> bool
|
|
19
|
+
|
|
20
|
+
def pastel: () -> untyped
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module Terminal
|
|
24
|
+
FALLBACK_WIDTH: Integer
|
|
25
|
+
|
|
26
|
+
def self.width: (?env: Hash[String, String], ?console: untyped) -> Integer
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module Text
|
|
30
|
+
def self.lines: (String? text, Integer? width) -> Array[String]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class Configuration
|
|
34
|
+
SECTIONS: Array[Symbol]
|
|
35
|
+
HEADINGS: Hash[Symbol, String]
|
|
36
|
+
STYLES: Hash[Symbol, Array[Symbol]]
|
|
37
|
+
DEFAULTS: Hash[Symbol, untyped]
|
|
38
|
+
MIN_WIDTH: Integer
|
|
39
|
+
|
|
40
|
+
def initialize: (?Hash[Symbol, untyped] values) -> void
|
|
41
|
+
|
|
42
|
+
def title: (?String? value) -> String?
|
|
43
|
+
def title=: (String?) -> void
|
|
44
|
+
def description: (?String? value) -> String?
|
|
45
|
+
def description=: (String?) -> void
|
|
46
|
+
def epilogue: (?String? value) -> String?
|
|
47
|
+
def epilogue=: (String?) -> void
|
|
48
|
+
def color: (?(bool | Symbol) value) -> (bool | Symbol)
|
|
49
|
+
def color=: (bool | Symbol) -> void
|
|
50
|
+
def wrap: (?bool value) -> bool
|
|
51
|
+
def wrap=: (bool) -> void
|
|
52
|
+
def width: (?(Integer | Symbol) value) -> (Integer | Symbol)
|
|
53
|
+
def width=: (Integer | Symbol) -> void
|
|
54
|
+
def margin: (?Integer value) -> Integer
|
|
55
|
+
def margin=: (Integer) -> void
|
|
56
|
+
def exit_code_without_arguments: (?Integer value) -> Integer
|
|
57
|
+
def exit_code_without_arguments=: (Integer) -> void
|
|
58
|
+
def banner_on_subcommands: (?bool value) -> bool
|
|
59
|
+
def banner_on_subcommands=: (bool) -> void
|
|
60
|
+
def heading_case: (?Symbol value) -> Symbol
|
|
61
|
+
def heading_case=: (Symbol) -> void
|
|
62
|
+
def command_order: (?Symbol value) -> Symbol
|
|
63
|
+
def command_order=: (Symbol) -> void
|
|
64
|
+
|
|
65
|
+
def heading: (Symbol section, String text) -> String
|
|
66
|
+
def headings: () -> Hash[Symbol, String]
|
|
67
|
+
def style: (Symbol element, *Symbol names) -> Array[Symbol]
|
|
68
|
+
def styles: () -> Hash[Symbol, Array[Symbol]]
|
|
69
|
+
def group: (String name, *(String | Array[String]) commands) -> Array[[String, Array[String]]]
|
|
70
|
+
def groups: () -> Array[[String, Array[String]]]
|
|
71
|
+
def sections: (*Symbol names) -> Array[Symbol]
|
|
72
|
+
def sections=: (Array[Symbol]) -> void
|
|
73
|
+
def hide: (*Symbol names) -> Array[Symbol]
|
|
74
|
+
def hidden: () -> Array[Symbol]
|
|
75
|
+
def visible_sections: () -> Array[Symbol]
|
|
76
|
+
def wrap_width: (Integer terminal_width) -> Integer?
|
|
77
|
+
def merge: (Configuration other) -> Configuration
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
module Integration
|
|
81
|
+
module RegistryMethods
|
|
82
|
+
def help: () ?{ (Configuration) -> void } -> Configuration
|
|
83
|
+
def help_config: () -> Configuration?
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: dry-cli-help
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Konstantin Gredeskoul
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: dry-cli
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 1.1.1
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '2'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: 1.1.1
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '2'
|
|
32
|
+
- !ruby/object:Gem::Dependency
|
|
33
|
+
name: pastel
|
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - "~>"
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '0.8'
|
|
39
|
+
type: :runtime
|
|
40
|
+
prerelease: false
|
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - "~>"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0.8'
|
|
46
|
+
description: Adds a title, description, epilogue, command groups, section ordering,
|
|
47
|
+
terminal-width wrapping and ANSI colors to the help dry-cli prints, configured through
|
|
48
|
+
a help block on the registry.
|
|
49
|
+
email:
|
|
50
|
+
- kigster@gmail.com
|
|
51
|
+
executables: []
|
|
52
|
+
extensions: []
|
|
53
|
+
extra_rdoc_files: []
|
|
54
|
+
files:
|
|
55
|
+
- CHANGELOG.md
|
|
56
|
+
- LICENSE.txt
|
|
57
|
+
- README.md
|
|
58
|
+
- Rakefile
|
|
59
|
+
- SPECIFICATION.md
|
|
60
|
+
- lib/dry-cli-help.rb
|
|
61
|
+
- lib/dry/cli/help.rb
|
|
62
|
+
- lib/dry/cli/help/colors.rb
|
|
63
|
+
- lib/dry/cli/help/configuration.rb
|
|
64
|
+
- lib/dry/cli/help/formatter.rb
|
|
65
|
+
- lib/dry/cli/help/integration.rb
|
|
66
|
+
- lib/dry/cli/help/screens/base.rb
|
|
67
|
+
- lib/dry/cli/help/screens/command.rb
|
|
68
|
+
- lib/dry/cli/help/screens/listing.rb
|
|
69
|
+
- lib/dry/cli/help/terminal.rb
|
|
70
|
+
- lib/dry/cli/help/text.rb
|
|
71
|
+
- lib/dry/cli/help/version.rb
|
|
72
|
+
- sig/dry/cli/help.rbs
|
|
73
|
+
homepage: https://github.com/kigster/dry-cli-help
|
|
74
|
+
licenses:
|
|
75
|
+
- MIT
|
|
76
|
+
metadata:
|
|
77
|
+
allowed_push_host: https://rubygems.org
|
|
78
|
+
homepage_uri: https://github.com/kigster/dry-cli-help
|
|
79
|
+
source_code_uri: https://github.com/kigster/dry-cli-help
|
|
80
|
+
changelog_uri: https://github.com/kigster/dry-cli-help/blob/main/CHANGELOG.md
|
|
81
|
+
rubygems_mfa_required: 'true'
|
|
82
|
+
rdoc_options: []
|
|
83
|
+
require_paths:
|
|
84
|
+
- lib
|
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '4.0'
|
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - ">="
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '0'
|
|
95
|
+
requirements: []
|
|
96
|
+
rubygems_version: 4.0.20
|
|
97
|
+
specification_version: 4
|
|
98
|
+
summary: Configurable, wrapped, colored help screens for dry-cli applications
|
|
99
|
+
test_files: []
|