command_kit 0.1.0.pre1
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/.document +3 -0
- data/.github/workflows/ruby.yml +29 -0
- data/.gitignore +7 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +29 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +20 -0
- data/README.md +283 -0
- data/Rakefile +23 -0
- data/command_kit.gemspec +60 -0
- data/gemspec.yml +14 -0
- data/lib/command_kit.rb +1 -0
- data/lib/command_kit/arguments.rb +161 -0
- data/lib/command_kit/arguments/argument.rb +111 -0
- data/lib/command_kit/arguments/argument_value.rb +81 -0
- data/lib/command_kit/arguments/usage.rb +6 -0
- data/lib/command_kit/colors.rb +355 -0
- data/lib/command_kit/command.rb +42 -0
- data/lib/command_kit/command_name.rb +95 -0
- data/lib/command_kit/commands.rb +299 -0
- data/lib/command_kit/commands/auto_load.rb +153 -0
- data/lib/command_kit/commands/auto_load/subcommand.rb +90 -0
- data/lib/command_kit/commands/auto_require.rb +138 -0
- data/lib/command_kit/commands/command.rb +12 -0
- data/lib/command_kit/commands/help.rb +43 -0
- data/lib/command_kit/commands/parent_command.rb +21 -0
- data/lib/command_kit/commands/subcommand.rb +51 -0
- data/lib/command_kit/console.rb +141 -0
- data/lib/command_kit/description.rb +89 -0
- data/lib/command_kit/env.rb +43 -0
- data/lib/command_kit/env/home.rb +71 -0
- data/lib/command_kit/env/path.rb +71 -0
- data/lib/command_kit/examples.rb +99 -0
- data/lib/command_kit/exception_handler.rb +55 -0
- data/lib/command_kit/help.rb +62 -0
- data/lib/command_kit/help/man.rb +125 -0
- data/lib/command_kit/inflector.rb +84 -0
- data/lib/command_kit/main.rb +103 -0
- data/lib/command_kit/options.rb +179 -0
- data/lib/command_kit/options/option.rb +171 -0
- data/lib/command_kit/options/option_value.rb +90 -0
- data/lib/command_kit/options/parser.rb +227 -0
- data/lib/command_kit/options/quiet.rb +53 -0
- data/lib/command_kit/options/usage.rb +6 -0
- data/lib/command_kit/options/verbose.rb +55 -0
- data/lib/command_kit/options/version.rb +62 -0
- data/lib/command_kit/os.rb +47 -0
- data/lib/command_kit/pager.rb +115 -0
- data/lib/command_kit/printing.rb +32 -0
- data/lib/command_kit/printing/indent.rb +78 -0
- data/lib/command_kit/program_name.rb +57 -0
- data/lib/command_kit/stdio.rb +138 -0
- data/lib/command_kit/usage.rb +102 -0
- data/lib/command_kit/version.rb +4 -0
- data/lib/command_kit/xdg.rb +138 -0
- data/spec/arguments/argument_spec.rb +169 -0
- data/spec/arguments/argument_value_spec.rb +126 -0
- data/spec/arguments_spec.rb +213 -0
- data/spec/colors_spec.rb +470 -0
- data/spec/command_kit_spec.rb +8 -0
- data/spec/command_name_spec.rb +130 -0
- data/spec/command_spec.rb +49 -0
- data/spec/commands/auto_load/subcommand_spec.rb +82 -0
- data/spec/commands/auto_load_spec.rb +128 -0
- data/spec/commands/auto_require_spec.rb +142 -0
- data/spec/commands/fixtures/test_auto_load/cli/commands/test1.rb +10 -0
- data/spec/commands/fixtures/test_auto_load/cli/commands/test2.rb +10 -0
- data/spec/commands/fixtures/test_auto_require/lib/test_auto_require/cli/commands/test1.rb +10 -0
- data/spec/commands/help_spec.rb +66 -0
- data/spec/commands/parent_command_spec.rb +40 -0
- data/spec/commands/subcommand_spec.rb +99 -0
- data/spec/commands_spec.rb +767 -0
- data/spec/console_spec.rb +201 -0
- data/spec/description_spec.rb +203 -0
- data/spec/env/home_spec.rb +46 -0
- data/spec/env/path_spec.rb +78 -0
- data/spec/env_spec.rb +123 -0
- data/spec/examples_spec.rb +235 -0
- data/spec/exception_handler_spec.rb +103 -0
- data/spec/help_spec.rb +119 -0
- data/spec/inflector_spec.rb +104 -0
- data/spec/main_spec.rb +179 -0
- data/spec/options/option_spec.rb +258 -0
- data/spec/options/option_value_spec.rb +67 -0
- data/spec/options/parser_spec.rb +265 -0
- data/spec/options_spec.rb +137 -0
- data/spec/os_spec.rb +46 -0
- data/spec/pager_spec.rb +154 -0
- data/spec/printing/indent_spec.rb +130 -0
- data/spec/printing_spec.rb +76 -0
- data/spec/program_name_spec.rb +62 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/stdio_spec.rb +264 -0
- data/spec/usage_spec.rb +237 -0
- data/spec/xdg_spec.rb +191 -0
- metadata +156 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'command_kit/options'
|
|
2
|
+
|
|
3
|
+
module CommandKit
|
|
4
|
+
module Options
|
|
5
|
+
#
|
|
6
|
+
# Defines a `-q`,`--quiet` option.
|
|
7
|
+
#
|
|
8
|
+
# ## Examples
|
|
9
|
+
#
|
|
10
|
+
# include CommandKit::Options::Quiet
|
|
11
|
+
#
|
|
12
|
+
# def main(*argv)
|
|
13
|
+
# # ...
|
|
14
|
+
# puts "verbose output" unless quiet?
|
|
15
|
+
# # ...
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
module Quiet
|
|
19
|
+
include Options
|
|
20
|
+
|
|
21
|
+
module ModuleMethods
|
|
22
|
+
#
|
|
23
|
+
# Defines a `-q, --quiet` option.
|
|
24
|
+
#
|
|
25
|
+
# @param [Class, Module] context
|
|
26
|
+
# The class or module including {Quiet}.
|
|
27
|
+
#
|
|
28
|
+
def included(context)
|
|
29
|
+
super(context)
|
|
30
|
+
|
|
31
|
+
if context.class == Module
|
|
32
|
+
context.extend ModuleMethods
|
|
33
|
+
else
|
|
34
|
+
context.option :quiet, short: '-q', desc: 'Enables quiet output' do
|
|
35
|
+
@quiet = true
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
extend ModuleMethods
|
|
42
|
+
|
|
43
|
+
#
|
|
44
|
+
# Determines if quiet mode is enabled.
|
|
45
|
+
#
|
|
46
|
+
# @return [Boolean]
|
|
47
|
+
#
|
|
48
|
+
def quiet?
|
|
49
|
+
@quiet
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'command_kit/options'
|
|
2
|
+
|
|
3
|
+
module CommandKit
|
|
4
|
+
module Options
|
|
5
|
+
#
|
|
6
|
+
# Defines a `-v`,`--verbose` option.
|
|
7
|
+
#
|
|
8
|
+
# ## Examples
|
|
9
|
+
#
|
|
10
|
+
# include Options::Verbose
|
|
11
|
+
#
|
|
12
|
+
# def main(*argv)
|
|
13
|
+
# # ...
|
|
14
|
+
# puts "verbose output" if verbose?
|
|
15
|
+
# # ...
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
module Verbose
|
|
19
|
+
include Options
|
|
20
|
+
|
|
21
|
+
module ModuleMethods
|
|
22
|
+
#
|
|
23
|
+
# Defines a `-v, --verbose` option or extends {ModuleMethods}, depending
|
|
24
|
+
# on whether {Options::Verbose} is being included into a class or a
|
|
25
|
+
# module.
|
|
26
|
+
#
|
|
27
|
+
# @param [Class, Module] context
|
|
28
|
+
# The class or module including {Verbose}.
|
|
29
|
+
#
|
|
30
|
+
def included(context)
|
|
31
|
+
super(context)
|
|
32
|
+
|
|
33
|
+
if context.class == Module
|
|
34
|
+
context.extend ModuleMethods
|
|
35
|
+
else
|
|
36
|
+
context.option :verbose, short: '-v', desc: 'Enables verbose output' do
|
|
37
|
+
@verbose = true
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
extend ModuleMethods
|
|
44
|
+
|
|
45
|
+
#
|
|
46
|
+
# Determines if verbose mode is enabled.
|
|
47
|
+
#
|
|
48
|
+
# @return [Boolean]
|
|
49
|
+
#
|
|
50
|
+
def verbose?
|
|
51
|
+
@verbose
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'command_kit/options'
|
|
2
|
+
|
|
3
|
+
module CommandKit
|
|
4
|
+
module Options
|
|
5
|
+
#
|
|
6
|
+
# Defines a version option.
|
|
7
|
+
#
|
|
8
|
+
module Version
|
|
9
|
+
#
|
|
10
|
+
# Includes {Options}, extends {Version::ClassMethods}, and defines a
|
|
11
|
+
# `-V, --version` option.
|
|
12
|
+
#
|
|
13
|
+
def self.included(command)
|
|
14
|
+
command.include Options
|
|
15
|
+
command.extend ClassMethods
|
|
16
|
+
|
|
17
|
+
command.option :version, short: '-V',
|
|
18
|
+
desc: 'Prints the version and exits' do
|
|
19
|
+
print_version
|
|
20
|
+
exit(0)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
#
|
|
25
|
+
# Defines class-level methods.
|
|
26
|
+
#
|
|
27
|
+
module ClassMethods
|
|
28
|
+
#
|
|
29
|
+
# Gets or sets the version string.
|
|
30
|
+
#
|
|
31
|
+
# @param [String, nil] new_version
|
|
32
|
+
# If given a new version String, it will set the class'es version
|
|
33
|
+
# string.
|
|
34
|
+
#
|
|
35
|
+
# @return [String, nil]
|
|
36
|
+
# The classes version string.
|
|
37
|
+
#
|
|
38
|
+
def version(new_version=nil)
|
|
39
|
+
if new_version
|
|
40
|
+
@version = new_version
|
|
41
|
+
else
|
|
42
|
+
@version || (superclass.version if superclass.kind_of?(ClassMethods))
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
#
|
|
48
|
+
# @see ClassMethods#version
|
|
49
|
+
#
|
|
50
|
+
def version
|
|
51
|
+
self.class.version
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
# Prints the version.
|
|
56
|
+
#
|
|
57
|
+
def print_version
|
|
58
|
+
puts "#{command_name} #{version}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module CommandKit
|
|
2
|
+
#
|
|
3
|
+
# Provides methods for determining the current OS.
|
|
4
|
+
#
|
|
5
|
+
# ## Examples
|
|
6
|
+
#
|
|
7
|
+
# include CommandKit::OS
|
|
8
|
+
#
|
|
9
|
+
# def main(*argv)
|
|
10
|
+
# if linux?
|
|
11
|
+
# # ...
|
|
12
|
+
# elsif macos?
|
|
13
|
+
# # ...
|
|
14
|
+
# elsif windows?
|
|
15
|
+
# # ...
|
|
16
|
+
# end
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
module OS
|
|
20
|
+
#
|
|
21
|
+
# Determines if the current OS is Linux.
|
|
22
|
+
#
|
|
23
|
+
# @return [Boolean]
|
|
24
|
+
#
|
|
25
|
+
def linux?
|
|
26
|
+
RUBY_PLATFORM.include?('linux')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# Determines if the current OS is macOS.
|
|
31
|
+
#
|
|
32
|
+
# @return [Boolean]
|
|
33
|
+
#
|
|
34
|
+
def macos?
|
|
35
|
+
RUBY_PLATFORM.include?('darwin')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
#
|
|
39
|
+
# Determines if the current OS is Windows.
|
|
40
|
+
#
|
|
41
|
+
# @return [Boolean]
|
|
42
|
+
#
|
|
43
|
+
def windows?
|
|
44
|
+
Gem.win_platform?
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'command_kit/stdio'
|
|
4
|
+
require 'command_kit/env'
|
|
5
|
+
require 'command_kit/env/path'
|
|
6
|
+
|
|
7
|
+
module CommandKit
|
|
8
|
+
#
|
|
9
|
+
# Allows opening a pager, such as `less` or `more`.
|
|
10
|
+
#
|
|
11
|
+
# ## Examples
|
|
12
|
+
#
|
|
13
|
+
# class CLI < CommandKit::Command
|
|
14
|
+
#
|
|
15
|
+
# include CommandKit::Pager
|
|
16
|
+
#
|
|
17
|
+
# def run
|
|
18
|
+
# pager do |io|
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# ## Environment Variables
|
|
25
|
+
#
|
|
26
|
+
# * `PAGER` - The optional command to override the default pager (usually
|
|
27
|
+
# `less -r` or `more -r`).
|
|
28
|
+
# * `PATH` - The list of directories to search for the default pager program
|
|
29
|
+
# (either `less` or `more`).
|
|
30
|
+
#
|
|
31
|
+
# ## Alternatives
|
|
32
|
+
#
|
|
33
|
+
# * [tty-pager](https://github.com/piotrmurach/tty-pager#readme)
|
|
34
|
+
#
|
|
35
|
+
module Pager
|
|
36
|
+
include Stdio
|
|
37
|
+
include Env
|
|
38
|
+
include Env::Path
|
|
39
|
+
|
|
40
|
+
# Common pager commands.
|
|
41
|
+
PAGERS = ['less -r', 'more -r']
|
|
42
|
+
|
|
43
|
+
#
|
|
44
|
+
# Initializes the pager.
|
|
45
|
+
#
|
|
46
|
+
# @note
|
|
47
|
+
# Respects the `PAGER` env variable, or attemps to find `less` or
|
|
48
|
+
# `more` by searching the `PATH` env variable.
|
|
49
|
+
#
|
|
50
|
+
def initialize(**kwargs)
|
|
51
|
+
super(**kwargs)
|
|
52
|
+
|
|
53
|
+
@pager = env.fetch('PAGER') do
|
|
54
|
+
PAGERS.find do |command|
|
|
55
|
+
bin = command.split(' ',2).first
|
|
56
|
+
|
|
57
|
+
command_installed?(bin)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
#
|
|
63
|
+
# Opens a pager pipe.
|
|
64
|
+
#
|
|
65
|
+
# @yield [io]
|
|
66
|
+
# The given block will be passed the IO pipe to the pager.
|
|
67
|
+
#
|
|
68
|
+
# @yieldparam [IO]
|
|
69
|
+
# The IO pipe to the pager.
|
|
70
|
+
#
|
|
71
|
+
def pager
|
|
72
|
+
if !stdout.tty? || @pager.nil?
|
|
73
|
+
# fallback to stdout if the process does not have a console or we could
|
|
74
|
+
# not find a suitable pager command.
|
|
75
|
+
yield stdout
|
|
76
|
+
return
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
io = IO.popen(@pager,'w')
|
|
80
|
+
pid = io.pid
|
|
81
|
+
|
|
82
|
+
begin
|
|
83
|
+
yield io
|
|
84
|
+
rescue Errno::EPIPE
|
|
85
|
+
ensure
|
|
86
|
+
io.close
|
|
87
|
+
|
|
88
|
+
begin
|
|
89
|
+
Process.waitpid(pid)
|
|
90
|
+
rescue Errno::EPIPE, Errno::ECHILD
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
#
|
|
96
|
+
# Pages the data if it's longer the console's height, otherwise prints the
|
|
97
|
+
# data to {Stdio#stdout stdout}.
|
|
98
|
+
#
|
|
99
|
+
# @param [Array<String>, #to_s] data
|
|
100
|
+
# The data to print.
|
|
101
|
+
#
|
|
102
|
+
def print_or_page(data)
|
|
103
|
+
line_count = case data
|
|
104
|
+
when Array then data.length
|
|
105
|
+
else data.to_s.each_line.count
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
if line_count > console_height
|
|
109
|
+
pager { |io| io.puts(data) }
|
|
110
|
+
else
|
|
111
|
+
stdout.puts(data)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'command_kit/stdio'
|
|
4
|
+
|
|
5
|
+
module CommandKit
|
|
6
|
+
#
|
|
7
|
+
# Provides printing methods.
|
|
8
|
+
#
|
|
9
|
+
module Printing
|
|
10
|
+
include Stdio
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# Prints the error message to {Stdio#stderr stderr}.
|
|
14
|
+
#
|
|
15
|
+
# @param [String] message
|
|
16
|
+
# The error message.
|
|
17
|
+
#
|
|
18
|
+
def print_error(message)
|
|
19
|
+
stderr.puts message
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#
|
|
23
|
+
# Prints an exception to {Stdio#stderr stderr}.
|
|
24
|
+
#
|
|
25
|
+
# @param [Exception] error
|
|
26
|
+
# The error to print.
|
|
27
|
+
#
|
|
28
|
+
def print_exception(error)
|
|
29
|
+
print_error error.full_message(highlight: stderr.tty?)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module CommandKit
|
|
2
|
+
module Printing
|
|
3
|
+
#
|
|
4
|
+
# Adds the ability to automatically indent all calls to `puts`.
|
|
5
|
+
#
|
|
6
|
+
# ## Examples
|
|
7
|
+
#
|
|
8
|
+
# include Printing::Indent
|
|
9
|
+
#
|
|
10
|
+
# def main
|
|
11
|
+
# puts "regular output:"
|
|
12
|
+
#
|
|
13
|
+
# indent(4) do
|
|
14
|
+
# puts "indented output"
|
|
15
|
+
# puts "..."
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# puts "back to regular output"
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
module Indent
|
|
22
|
+
#
|
|
23
|
+
# Initializes the indententation level to zero.
|
|
24
|
+
#
|
|
25
|
+
def initialize(**kwargs)
|
|
26
|
+
@indent = 0
|
|
27
|
+
|
|
28
|
+
super(**kwargs)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
#
|
|
32
|
+
# Increases the indentation level by two, yields, then restores the
|
|
33
|
+
# indentation level.
|
|
34
|
+
#
|
|
35
|
+
# @param [Integer] n
|
|
36
|
+
# How much to increase the indentation level by.
|
|
37
|
+
#
|
|
38
|
+
# @yield []
|
|
39
|
+
# The given block will be called after the indentation level has been
|
|
40
|
+
# increased.
|
|
41
|
+
#
|
|
42
|
+
# @return [Integer]
|
|
43
|
+
# If no block is given, the indententation level will be returned.
|
|
44
|
+
#
|
|
45
|
+
def indent(n=2)
|
|
46
|
+
if block_given?
|
|
47
|
+
begin
|
|
48
|
+
original_indent = @indent
|
|
49
|
+
@indent += n
|
|
50
|
+
|
|
51
|
+
yield
|
|
52
|
+
ensure
|
|
53
|
+
@indent = original_indent
|
|
54
|
+
end
|
|
55
|
+
else
|
|
56
|
+
@indent
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
#
|
|
61
|
+
# Indents and prints the lines to stdout.
|
|
62
|
+
#
|
|
63
|
+
# @param [Array<String>] lines
|
|
64
|
+
# The lines to indent and print.
|
|
65
|
+
#
|
|
66
|
+
def puts(*lines)
|
|
67
|
+
if (@indent > 0 && !lines.empty?)
|
|
68
|
+
padding = " " * @indent
|
|
69
|
+
|
|
70
|
+
super(*lines.map { |line| "#{padding}#{line}" })
|
|
71
|
+
else
|
|
72
|
+
super(*lines)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|