command_kit 0.1.0 → 0.2.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 +4 -4
- data/.github/workflows/ruby.yml +15 -0
- data/.rubocop.yml +138 -0
- data/ChangeLog.md +29 -0
- data/Gemfile +3 -0
- data/README.md +141 -121
- data/Rakefile +3 -2
- data/command_kit.gemspec +4 -4
- data/examples/command.rb +1 -1
- data/gemspec.yml +7 -0
- data/lib/command_kit/arguments.rb +1 -1
- data/lib/command_kit/colors.rb +221 -45
- data/lib/command_kit/command.rb +1 -1
- data/lib/command_kit/commands.rb +4 -4
- data/lib/command_kit/help/man.rb +4 -25
- data/lib/command_kit/inflector.rb +47 -17
- data/lib/command_kit/main.rb +7 -9
- data/lib/command_kit/man.rb +44 -0
- data/lib/command_kit/open_app.rb +69 -0
- data/lib/command_kit/options/option.rb +1 -6
- data/lib/command_kit/options/parser.rb +15 -17
- data/lib/command_kit/options.rb +2 -2
- data/lib/command_kit/os/linux.rb +157 -0
- data/lib/command_kit/os.rb +159 -11
- data/lib/command_kit/package_manager.rb +200 -0
- data/lib/command_kit/pager.rb +46 -4
- data/lib/command_kit/printing/indent.rb +2 -2
- data/lib/command_kit/printing.rb +1 -1
- data/lib/command_kit/sudo.rb +40 -0
- data/lib/command_kit/terminal.rb +5 -0
- data/lib/command_kit/version.rb +1 -1
- data/spec/arguments/argument_spec.rb +1 -1
- data/spec/colors_spec.rb +256 -0
- data/spec/commands_spec.rb +1 -1
- data/spec/exception_handler_spec.rb +1 -1
- data/spec/help/man_spec.rb +0 -32
- data/spec/inflector_spec.rb +70 -8
- data/spec/man_spec.rb +46 -0
- data/spec/open_app_spec.rb +85 -0
- data/spec/options/option_spec.rb +2 -2
- data/spec/os/linux_spec.rb +154 -0
- data/spec/os_spec.rb +200 -13
- data/spec/package_manager_spec.rb +806 -0
- data/spec/pager_spec.rb +71 -6
- data/spec/sudo_spec.rb +51 -0
- data/spec/terminal_spec.rb +30 -0
- data/spec/usage_spec.rb +1 -1
- metadata +19 -4
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'strscan'
|
4
|
+
|
3
5
|
module CommandKit
|
4
6
|
#
|
5
7
|
# A very simple inflector.
|
@@ -33,16 +35,37 @@ module CommandKit
|
|
33
35
|
# @return [String]
|
34
36
|
# The resulting under_scored name.
|
35
37
|
#
|
38
|
+
# @raise [ArgumentError]
|
39
|
+
# The given string contained non-alpha-numeric characters.
|
40
|
+
#
|
36
41
|
def self.underscore(name)
|
37
|
-
|
38
|
-
|
42
|
+
scanner = StringScanner.new(name.to_s)
|
43
|
+
new_string = String.new
|
44
|
+
|
45
|
+
until scanner.eos?
|
46
|
+
if (separator = scanner.scan(/[_-]+/))
|
47
|
+
new_string << '_' * separator.length
|
48
|
+
else
|
49
|
+
if (capitalized = scanner.scan(/[A-Z][a-z\d]+/))
|
50
|
+
new_string << capitalized
|
51
|
+
elsif (uppercase = scanner.scan(/[A-Z][A-Z\d]*(?=[A-Z_-]|$)/))
|
52
|
+
new_string << uppercase
|
53
|
+
elsif (lowercase = scanner.scan(/[a-z][a-z\d]*/))
|
54
|
+
new_string << lowercase
|
55
|
+
else
|
56
|
+
raise(ArgumentError,"cannot convert string to underscored: #{scanner.string.inspect}")
|
57
|
+
end
|
39
58
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
59
|
+
if (separator = scanner.scan(/[_-]+/))
|
60
|
+
new_string << '_' * separator.length
|
61
|
+
elsif !scanner.eos?
|
62
|
+
new_string << '_'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
44
66
|
|
45
|
-
|
67
|
+
new_string.downcase!
|
68
|
+
new_string
|
46
69
|
end
|
47
70
|
|
48
71
|
#
|
@@ -67,20 +90,27 @@ module CommandKit
|
|
67
90
|
# @return [String]
|
68
91
|
# The CamelCased name.
|
69
92
|
#
|
93
|
+
# @raise [ArgumentError]
|
94
|
+
# The given under_scored string contained non-alpha-numeric characters.
|
95
|
+
#
|
70
96
|
def self.camelize(name)
|
71
|
-
|
72
|
-
|
73
|
-
# sourced from: https://github.com/dry-rb/dry-inflector/blob/c918f967ff82611da374eb0847a77b7e012d3fa8/lib/dry/inflector.rb#L329-L334
|
74
|
-
name.sub!(/^[a-z\d]*/,&:capitalize)
|
75
|
-
name.gsub!(%r{(?:[_-]|(/))([a-z\d]*)}i) do |match|
|
76
|
-
slash = Regexp.last_match(1)
|
77
|
-
word = Regexp.last_match(2)
|
97
|
+
scanner = StringScanner.new(name.to_s)
|
98
|
+
new_string = String.new
|
78
99
|
|
79
|
-
|
100
|
+
until scanner.eos?
|
101
|
+
if (word = scanner.scan(/[A-Za-z\d]+/))
|
102
|
+
word.capitalize!
|
103
|
+
new_string << word
|
104
|
+
elsif scanner.scan(/[_-]+/)
|
105
|
+
# skip
|
106
|
+
elsif scanner.scan(/\//)
|
107
|
+
new_string << '::'
|
108
|
+
else
|
109
|
+
raise(ArgumentError,"cannot convert string to CamelCase: #{scanner.string.inspect}")
|
110
|
+
end
|
80
111
|
end
|
81
112
|
|
82
|
-
|
83
|
-
name
|
113
|
+
new_string
|
84
114
|
end
|
85
115
|
end
|
86
116
|
end
|
data/lib/command_kit/main.rb
CHANGED
@@ -49,15 +49,13 @@ module CommandKit
|
|
49
49
|
# @api public
|
50
50
|
#
|
51
51
|
def start(argv=ARGV, **kwargs)
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
exit 0
|
60
|
-
end
|
52
|
+
exit main(argv, **kwargs)
|
53
|
+
rescue Interrupt
|
54
|
+
# https://tldp.org/LDP/abs/html/exitcodes.html
|
55
|
+
exit 130
|
56
|
+
rescue Errno::EPIPE
|
57
|
+
# STDOUT pipe broken
|
58
|
+
exit 0
|
61
59
|
end
|
62
60
|
|
63
61
|
#
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CommandKit
|
4
|
+
#
|
5
|
+
# Allows displaying man pages.
|
6
|
+
#
|
7
|
+
# ## Examples
|
8
|
+
#
|
9
|
+
# man "passwd"
|
10
|
+
# man "passwd", section: 5
|
11
|
+
#
|
12
|
+
# @since 0.2.0
|
13
|
+
#
|
14
|
+
module Man
|
15
|
+
#
|
16
|
+
# Displays the given man page.
|
17
|
+
#
|
18
|
+
# @param [String] page
|
19
|
+
# The man page file name.
|
20
|
+
#
|
21
|
+
# @param [Integer, String, nil] section
|
22
|
+
# The optional section number to specify.
|
23
|
+
#
|
24
|
+
# @return [Boolean, nil]
|
25
|
+
# Specifies whether the `man` command was successful or not.
|
26
|
+
# Returns `nil` when the `man` command is not installed.
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# man "passwd"
|
30
|
+
#
|
31
|
+
# @example Display a man-page from a specific section:
|
32
|
+
# man "passwd", section: 5
|
33
|
+
#
|
34
|
+
# @api public
|
35
|
+
#
|
36
|
+
def man(page, section: nil)
|
37
|
+
if section
|
38
|
+
system('man',section.to_s,page.to_s)
|
39
|
+
else
|
40
|
+
system('man',page.to_s)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'command_kit/os'
|
2
|
+
require 'command_kit/env/path'
|
3
|
+
|
4
|
+
module CommandKit
|
5
|
+
#
|
6
|
+
# Allows opening a file or a URI with the system's preferred application for
|
7
|
+
# that file type or URI scheme.
|
8
|
+
#
|
9
|
+
# ## Examples
|
10
|
+
#
|
11
|
+
# open_app_for "movie.avi"
|
12
|
+
# open_app_for "https://github.com/postmodern/command_kit.rb"
|
13
|
+
#
|
14
|
+
# @since 0.2.0
|
15
|
+
#
|
16
|
+
module OpenApp
|
17
|
+
include OS
|
18
|
+
include Env::Path
|
19
|
+
|
20
|
+
#
|
21
|
+
# Initializes the command and determines which open command to use.
|
22
|
+
#
|
23
|
+
# @param [Hash{Symbol => Object}] kwargs
|
24
|
+
# Additional keyword arguments.
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
#
|
28
|
+
def initialize(**kwargs)
|
29
|
+
super(**kwargs)
|
30
|
+
|
31
|
+
@open_command = if macos?
|
32
|
+
'open'
|
33
|
+
elsif linux? || bsd?
|
34
|
+
if command_installed?('xdg-open')
|
35
|
+
'xdg-open'
|
36
|
+
end
|
37
|
+
elsif windows?
|
38
|
+
if command_installed?('invoke-item')
|
39
|
+
'invoke-item'
|
40
|
+
else
|
41
|
+
'start'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Opens a file or URI using the system's preferred application for that
|
48
|
+
# file type or URI scheme.
|
49
|
+
#
|
50
|
+
# @param [String, URI] file_or_uri
|
51
|
+
# The file path or URI to open.
|
52
|
+
#
|
53
|
+
# @return [Boolean, nil]
|
54
|
+
# Specifies whether the file or URI was successfully opened or not.
|
55
|
+
# If the open command could not be determined, `nil` is returned.
|
56
|
+
#
|
57
|
+
# @example Open a file:
|
58
|
+
# open_app_for "movie.avi"
|
59
|
+
#
|
60
|
+
# @example Open a URI:
|
61
|
+
# open_app_for "https://github.com/postmodern/command_kit.rb"
|
62
|
+
#
|
63
|
+
def open_app_for(file_or_uri)
|
64
|
+
if @open_command
|
65
|
+
system(@open_command,file_or_uri.to_s)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -35,11 +35,6 @@ module CommandKit
|
|
35
35
|
# @return [OptionValue, nil]
|
36
36
|
attr_reader :value
|
37
37
|
|
38
|
-
# The option's description.
|
39
|
-
#
|
40
|
-
# @return [String]
|
41
|
-
attr_reader :desc
|
42
|
-
|
43
38
|
# The optional block that will receive the parsed option value.
|
44
39
|
#
|
45
40
|
# @return [Proc, nil]
|
@@ -128,7 +123,7 @@ module CommandKit
|
|
128
123
|
end
|
129
124
|
|
130
125
|
#
|
131
|
-
# The separator
|
126
|
+
# The separator character between the option and option value.
|
132
127
|
#
|
133
128
|
# @return ['=', ' ', nil]
|
134
129
|
#
|
@@ -118,23 +118,21 @@ module CommandKit
|
|
118
118
|
# @api semipublic
|
119
119
|
#
|
120
120
|
def parse_options(argv)
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
on_parse_error(error)
|
137
|
-
end
|
121
|
+
option_parser.parse(argv)
|
122
|
+
rescue OptionParser::InvalidOption => error
|
123
|
+
on_invalid_option(error)
|
124
|
+
rescue OptionParser::AmbiguousOption => error
|
125
|
+
on_ambiguous_option(error)
|
126
|
+
rescue OptionParser::InvalidArgument => error
|
127
|
+
on_invalid_argument(error)
|
128
|
+
rescue OptionParser::MissingArgument => error
|
129
|
+
on_missing_argument(error)
|
130
|
+
rescue OptionParser::NeedlessArgument => error
|
131
|
+
on_needless_argument(error)
|
132
|
+
rescue OptionParser::AmbiguousArgument => error
|
133
|
+
on_ambiguous_argument(error)
|
134
|
+
rescue OptionParser::ParseError => error
|
135
|
+
on_parse_error(error)
|
138
136
|
end
|
139
137
|
|
140
138
|
#
|
data/lib/command_kit/options.rb
CHANGED
@@ -22,7 +22,7 @@ module CommandKit
|
|
22
22
|
# @bar = arg.split(':')
|
23
23
|
# end
|
24
24
|
#
|
25
|
-
# ### initialize and using
|
25
|
+
# ### initialize and using instance variables
|
26
26
|
#
|
27
27
|
# option :number, value: {type: Integer},
|
28
28
|
# desc: 'Numbers' do |num|
|
@@ -197,7 +197,7 @@ module CommandKit
|
|
197
197
|
# {Parser#option_parser option parser}.
|
198
198
|
#
|
199
199
|
# @param [Hash{Symbol => Object}] options
|
200
|
-
# Optional
|
200
|
+
# Optional prepopulated options hash.
|
201
201
|
#
|
202
202
|
# @note
|
203
203
|
# The {#option_parser} will populate {#options} and also call any
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'command_kit/os'
|
2
|
+
|
3
|
+
module CommandKit
|
4
|
+
module OS
|
5
|
+
#
|
6
|
+
# Provides methods for determining the specific type of Linux.
|
7
|
+
#
|
8
|
+
# ## Example
|
9
|
+
#
|
10
|
+
# require 'command_kit/command'
|
11
|
+
# require 'command_kit/os/linux'
|
12
|
+
#
|
13
|
+
# class Command < CommandKit::Command
|
14
|
+
#
|
15
|
+
# include CommandKit::OS::Linux
|
16
|
+
#
|
17
|
+
# def run
|
18
|
+
# if debian_linux?
|
19
|
+
# # ...
|
20
|
+
# elsif redhat_linux?
|
21
|
+
# # ...
|
22
|
+
# elsif suse_linux?
|
23
|
+
# # ...
|
24
|
+
# elsif arch_linux?
|
25
|
+
# # ...
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# @since 0.2.0
|
31
|
+
#
|
32
|
+
module Linux
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
#
|
36
|
+
module ModuleMethods
|
37
|
+
#
|
38
|
+
# Extends {ClassMethods} or {ModuleMethods}, depending on whether
|
39
|
+
# {OS} is being included into a class or a module..
|
40
|
+
#
|
41
|
+
# @param [Class, Module] context
|
42
|
+
# The class or module which is including {OS}.
|
43
|
+
#
|
44
|
+
def included(context)
|
45
|
+
super(context)
|
46
|
+
|
47
|
+
if context.class == Module
|
48
|
+
context.extend ModuleMethods
|
49
|
+
else
|
50
|
+
context.extend ClassMethods
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
extend ModuleMethods
|
56
|
+
|
57
|
+
module ClassMethods
|
58
|
+
#
|
59
|
+
# Determines the specific Linux distro.
|
60
|
+
#
|
61
|
+
# @return [:fedora, :redhat, :debian, :suse, :arch, nil]
|
62
|
+
# Returns the type of Linux distro or `nil` if the Linux distro could
|
63
|
+
# not be determined.
|
64
|
+
#
|
65
|
+
# @api semipublic
|
66
|
+
#
|
67
|
+
def linux_distro
|
68
|
+
if File.file?('/etc/fedora-release') then :fedora
|
69
|
+
elsif File.file?('/etc/redhat-release') then :redhat
|
70
|
+
elsif File.file?('/etc/debian_version') then :debian
|
71
|
+
elsif File.file?('/etc/SuSE-release') then :suse
|
72
|
+
elsif File.file?('/etc/arch-release') then :arch
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# The Linux distro.
|
78
|
+
#
|
79
|
+
# @return [:fedora, :redhat, :debian, :suse, :arch, nil]
|
80
|
+
#
|
81
|
+
# @api public
|
82
|
+
attr_reader :linux_distro
|
83
|
+
|
84
|
+
#
|
85
|
+
# Initializes the command.
|
86
|
+
#
|
87
|
+
# @param [:fedora, :redhat, :debian, :suse, :arch, nil] linux_distro
|
88
|
+
# Overrides the default detected Linux distro.
|
89
|
+
#
|
90
|
+
# @param [Hash{Symbol => Object}] kwargs
|
91
|
+
# Additional keyword arguments.
|
92
|
+
#
|
93
|
+
# @api public
|
94
|
+
#
|
95
|
+
def initialize(linux_distro: self.class.linux_distro, **kwargs)
|
96
|
+
super(**kwargs)
|
97
|
+
|
98
|
+
@linux_distro = linux_distro
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Determines if the current OS is RedHat Linux based distro.
|
103
|
+
#
|
104
|
+
# @return [Boolean]
|
105
|
+
#
|
106
|
+
# @api public
|
107
|
+
#
|
108
|
+
def redhat_linux?
|
109
|
+
@linux_distro == :redhat
|
110
|
+
end
|
111
|
+
|
112
|
+
#
|
113
|
+
# Determines if the current OS is Fedora Linux based distro.
|
114
|
+
#
|
115
|
+
# @return [Boolean]
|
116
|
+
#
|
117
|
+
# @api public
|
118
|
+
#
|
119
|
+
def fedora_linux?
|
120
|
+
@linux_distro == :fedora
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# Determines if the current OS is Debian Linux based distro.
|
125
|
+
#
|
126
|
+
# @return [Boolean]
|
127
|
+
#
|
128
|
+
# @api public
|
129
|
+
#
|
130
|
+
def debian_linux?
|
131
|
+
@linux_distro == :debian
|
132
|
+
end
|
133
|
+
|
134
|
+
#
|
135
|
+
# Determines if the current OS is SUSE Linux based distro.
|
136
|
+
#
|
137
|
+
# @return [Boolean]
|
138
|
+
#
|
139
|
+
# @api public
|
140
|
+
#
|
141
|
+
def suse_linux?
|
142
|
+
@linux_distro == :suse
|
143
|
+
end
|
144
|
+
|
145
|
+
#
|
146
|
+
# Determines if the current OS is Arch Linux based distro.
|
147
|
+
#
|
148
|
+
# @return [Boolean]
|
149
|
+
#
|
150
|
+
# @api public
|
151
|
+
#
|
152
|
+
def arch_linux?
|
153
|
+
@linux_distro == :arch
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|