command_kit 0.1.0.pre1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +15 -0
  3. data/.rubocop.yml +138 -0
  4. data/ChangeLog.md +34 -2
  5. data/Gemfile +3 -0
  6. data/README.md +135 -214
  7. data/Rakefile +3 -2
  8. data/command_kit.gemspec +4 -4
  9. data/examples/colors.rb +30 -0
  10. data/examples/command.rb +65 -0
  11. data/examples/pager.rb +30 -0
  12. data/gemspec.yml +10 -2
  13. data/lib/command_kit/arguments/argument.rb +16 -44
  14. data/lib/command_kit/arguments/argument_value.rb +3 -30
  15. data/lib/command_kit/arguments.rb +66 -20
  16. data/lib/command_kit/colors.rb +253 -45
  17. data/lib/command_kit/command.rb +50 -3
  18. data/lib/command_kit/command_name.rb +9 -0
  19. data/lib/command_kit/commands/auto_load/subcommand.rb +3 -0
  20. data/lib/command_kit/commands/auto_load.rb +16 -0
  21. data/lib/command_kit/commands/auto_require.rb +16 -0
  22. data/lib/command_kit/commands/command.rb +3 -0
  23. data/lib/command_kit/commands/help.rb +2 -0
  24. data/lib/command_kit/commands/parent_command.rb +7 -0
  25. data/lib/command_kit/commands/subcommand.rb +15 -0
  26. data/lib/command_kit/commands.rb +40 -4
  27. data/lib/command_kit/description.rb +15 -2
  28. data/lib/command_kit/env/home.rb +9 -0
  29. data/lib/command_kit/env/path.rb +15 -0
  30. data/lib/command_kit/env.rb +4 -0
  31. data/lib/command_kit/examples.rb +15 -2
  32. data/lib/command_kit/exception_handler.rb +4 -0
  33. data/lib/command_kit/help/man.rb +74 -47
  34. data/lib/command_kit/help.rb +10 -1
  35. data/lib/command_kit/inflector.rb +49 -17
  36. data/lib/command_kit/interactive.rb +239 -0
  37. data/lib/command_kit/main.rb +20 -9
  38. data/lib/command_kit/man.rb +44 -0
  39. data/lib/command_kit/open_app.rb +69 -0
  40. data/lib/command_kit/options/option.rb +36 -9
  41. data/lib/command_kit/options/option_value.rb +42 -3
  42. data/lib/command_kit/options/parser.rb +44 -17
  43. data/lib/command_kit/options/quiet.rb +3 -0
  44. data/lib/command_kit/options/verbose.rb +5 -0
  45. data/lib/command_kit/options/version.rb +6 -0
  46. data/lib/command_kit/options.rb +59 -10
  47. data/lib/command_kit/os/linux.rb +157 -0
  48. data/lib/command_kit/os.rb +165 -11
  49. data/lib/command_kit/package_manager.rb +200 -0
  50. data/lib/command_kit/pager.rb +84 -9
  51. data/lib/command_kit/printing/indent.rb +25 -2
  52. data/lib/command_kit/printing.rb +23 -0
  53. data/lib/command_kit/program_name.rb +7 -0
  54. data/lib/command_kit/stdio.rb +24 -0
  55. data/lib/command_kit/sudo.rb +40 -0
  56. data/lib/command_kit/terminal.rb +159 -0
  57. data/lib/command_kit/usage.rb +14 -0
  58. data/lib/command_kit/version.rb +1 -1
  59. data/lib/command_kit/xdg.rb +21 -1
  60. data/lib/command_kit.rb +1 -0
  61. data/spec/arguments/argument_spec.rb +5 -41
  62. data/spec/arguments/argument_value_spec.rb +1 -61
  63. data/spec/arguments_spec.rb +8 -25
  64. data/spec/colors_spec.rb +277 -13
  65. data/spec/command_name_spec.rb +1 -1
  66. data/spec/command_spec.rb +4 -1
  67. data/spec/commands/auto_load/subcommand_spec.rb +1 -1
  68. data/spec/commands/auto_load_spec.rb +1 -1
  69. data/spec/commands/auto_require_spec.rb +2 -2
  70. data/spec/commands/help_spec.rb +1 -1
  71. data/spec/commands/parent_command_spec.rb +1 -1
  72. data/spec/commands/subcommand_spec.rb +1 -1
  73. data/spec/commands_spec.rb +2 -2
  74. data/spec/description_spec.rb +1 -25
  75. data/spec/env/home_spec.rb +1 -1
  76. data/spec/env/path_spec.rb +1 -1
  77. data/spec/examples_spec.rb +1 -25
  78. data/spec/exception_handler_spec.rb +1 -1
  79. data/spec/help/man_spec.rb +316 -0
  80. data/spec/help_spec.rb +0 -25
  81. data/spec/inflector_spec.rb +71 -9
  82. data/spec/interactive_spec.rb +415 -0
  83. data/spec/main_spec.rb +7 -7
  84. data/spec/man_spec.rb +46 -0
  85. data/spec/open_app_spec.rb +85 -0
  86. data/spec/options/option_spec.rb +48 -9
  87. data/spec/options/option_value_spec.rb +53 -4
  88. data/spec/options_spec.rb +1 -1
  89. data/spec/os/linux_spec.rb +154 -0
  90. data/spec/os_spec.rb +201 -14
  91. data/spec/package_manager_spec.rb +806 -0
  92. data/spec/pager_spec.rb +78 -15
  93. data/spec/printing/indent_spec.rb +1 -1
  94. data/spec/printing_spec.rb +10 -2
  95. data/spec/program_name_spec.rb +1 -1
  96. data/spec/spec_helper.rb +0 -3
  97. data/spec/sudo_spec.rb +51 -0
  98. data/spec/{console_spec.rb → terminal_spec.rb} +65 -35
  99. data/spec/usage_spec.rb +2 -2
  100. data/spec/xdg_spec.rb +1 -1
  101. metadata +32 -13
  102. data/lib/command_kit/arguments/usage.rb +0 -6
  103. data/lib/command_kit/console.rb +0 -141
  104. data/lib/command_kit/options/usage.rb +0 -6
@@ -12,6 +12,9 @@ module CommandKit
12
12
  # end
13
13
  #
14
14
  module Main
15
+ #
16
+ # @api private
17
+ #
15
18
  module ModuleMethods
16
19
  #
17
20
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether {Main}
@@ -43,16 +46,16 @@ module CommandKit
43
46
  # @param [Array<String>] argv
44
47
  # The Array of command-line arguments.
45
48
  #
49
+ # @api public
50
+ #
46
51
  def start(argv=ARGV, **kwargs)
47
- begin
48
- exit main(argv, **kwargs)
49
- rescue Interrupt
50
- # https://tldp.org/LDP/abs/html/exitcodes.html
51
- exit 130
52
- rescue Errno::EPIPE
53
- # STDOUT pipe broken
54
- exit 0
55
- 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
56
59
  end
57
60
 
58
61
  #
@@ -68,6 +71,8 @@ module CommandKit
68
71
  # @return [Integer]
69
72
  # The exit status of the command.
70
73
  #
74
+ # @api public
75
+ #
71
76
  def main(argv=[], **kwargs)
72
77
  new(**kwargs).main(argv)
73
78
  end
@@ -82,6 +87,10 @@ module CommandKit
82
87
  # @return [Integer]
83
88
  # The exit status code.
84
89
  #
90
+ # @note `argv` is splatted into {#run}.
91
+ #
92
+ # @api public
93
+ #
85
94
  def main(argv=[])
86
95
  run(*argv)
87
96
  return 0
@@ -97,6 +106,8 @@ module CommandKit
97
106
  #
98
107
  # @abstract
99
108
  #
109
+ # @api public
110
+ #
100
111
  def run(*args)
101
112
  end
102
113
  end
@@ -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
@@ -11,26 +11,32 @@ module CommandKit
11
11
  #
12
12
  # Represents a defined option.
13
13
  #
14
+ # @api private
15
+ #
14
16
  class Option
15
17
 
18
+ # The option's name.
19
+ #
16
20
  # @return [Symbol]
17
21
  attr_reader :name
18
22
 
23
+ # The option's optional short-flag.
24
+ #
19
25
  # @return [String, nil]
20
26
  attr_reader :short
21
27
 
28
+ # The option's long-flag.
29
+ #
22
30
  # @return [String]
23
31
  attr_reader :long
24
32
 
25
- # @return [Boolean]
26
- attr_reader :equals
27
-
33
+ # The option value's type.
34
+ #
28
35
  # @return [OptionValue, nil]
29
36
  attr_reader :value
30
37
 
31
- # @return [String]
32
- attr_reader :desc
33
-
38
+ # The optional block that will receive the parsed option value.
39
+ #
34
40
  # @return [Proc, nil]
35
41
  attr_reader :block
36
42
 
@@ -38,26 +44,39 @@ module CommandKit
38
44
  # Initializes the option.
39
45
  #
40
46
  # @param [Symbol] name
47
+ # The name of the option.
41
48
  #
42
49
  # @param [String, nil] short
50
+ # Optional short-flag for the option.
43
51
  #
44
52
  # @param [String, nil] long
53
+ # Optional explicit long-flag for the option.
45
54
  #
46
55
  # @param [Boolean] equals
56
+ # Specifies whether the option is of the form (`--opt=value`).
47
57
  #
48
- # @param [Hash{Symbol => Object}, nil] value
58
+ # @param [Hash{Symbol => Object}, true, false, nil] value
49
59
  # Keyword arguments for {OptionValue#initialize}, or `nil` if the option
50
60
  # has no additional value.
51
61
  #
52
62
  # @option value [Class, Hash, Array, Regexp] type
63
+ # The type of the option's value.
53
64
  #
54
65
  # @option value [String, nil] usage
66
+ # The usage string for the option's value.
55
67
  #
56
68
  # @param [String] desc
69
+ # The description for the option.
57
70
  #
58
71
  # @yield [(value)]
72
+ # If a block is given, it will be called when the option is parsed.
59
73
  #
60
74
  # @yieldparam [Object, nil] value
75
+ # The given block will be passed the parsed option's value.
76
+ #
77
+ # @raise [TypeError]
78
+ # The `value` keyword argument was not a `Hash`, `true`, `false`, or
79
+ # `nil`.
61
80
  #
62
81
  def initialize(name, short: nil,
63
82
  long: self.class.default_long_opt(name),
@@ -69,7 +88,13 @@ module CommandKit
69
88
  @short = short
70
89
  @long = long
71
90
  @equals = equals
72
- @value = OptionValue.new(**value) if value
91
+ @value = case value
92
+ when Hash then OptionValue.new(**value)
93
+ when true then OptionValue.new()
94
+ when false, nil then nil
95
+ else
96
+ raise(TypeError,"value: keyword must be Hash, true, false, or nil")
97
+ end
73
98
  @desc = desc
74
99
  @block = block
75
100
  end
@@ -79,8 +104,10 @@ module CommandKit
79
104
  # (ex: `:long_opt`).
80
105
  #
81
106
  # @param [Symbol] name
107
+ # The option name.
82
108
  #
83
109
  # @return [String]
110
+ # The long-flag for the option.
84
111
  #
85
112
  def self.default_long_opt(name)
86
113
  "--#{Inflector.dasherize(name)}"
@@ -96,7 +123,7 @@ module CommandKit
96
123
  end
97
124
 
98
125
  #
99
- # The separator characer between the option and option value.
126
+ # The separator character between the option and option value.
100
127
  #
101
128
  # @return ['=', ' ', nil]
102
129
  #
@@ -14,8 +14,11 @@ module CommandKit
14
14
  #
15
15
  # Represents an additional argument associated with an option flag.
16
16
  #
17
+ # @api private
18
+ #
17
19
  class OptionValue < Arguments::ArgumentValue
18
20
 
21
+ # Maps OptionParser types to USAGE strings.
19
22
  USAGES = {
20
23
  # NOTE: NilClass and Object are intentionally omitted
21
24
  Date => 'DATE',
@@ -36,27 +39,52 @@ module CommandKit
36
39
  Regexp => '/REGEXP/'
37
40
  }
38
41
 
42
+ # The desired type of the argument value.
43
+ #
44
+ # @return [Class, Hash, Array, Regexp, nil]
45
+ attr_reader :type
46
+
47
+ # The default parsed value for the argument value.
48
+ #
49
+ # @return [Object, Proc, nil]
50
+ attr_reader :default
51
+
39
52
  #
40
53
  # Initializes the option value.
41
54
  #
42
55
  # @param [Class, Hash, Array, Regexp] type
56
+ # The type of the option value.
57
+ #
58
+ # @param [Object, Proc, nil] default
59
+ # The default parsed value for the option value.
43
60
  #
44
61
  # @param [String, nil] usage
62
+ # The optional usage string for the option value.
45
63
  #
46
64
  # @param [Hash{Symbol => Object}] kwargs
65
+ # Additional keyword arguments.
66
+ #
67
+ # @option kwargs [Boolean] required
68
+ # Specifies whether the option value is required or optional.
47
69
  #
48
- def initialize(type: String,
49
- usage: self.class.default_usage(type),
70
+ def initialize(type: String,
71
+ default: nil,
72
+ usage: self.class.default_usage(type),
50
73
  **kwargs)
51
- super(type: type, usage: usage, **kwargs)
74
+ super(usage: usage, **kwargs)
75
+
76
+ @type = type
77
+ @default = default
52
78
  end
53
79
 
54
80
  #
55
81
  # Returns the default option value usage for the given type.
56
82
  #
57
83
  # @param [Class, Hash, Array, Regexp] type
84
+ # The option value type.
58
85
  #
59
86
  # @return [String, nil]
87
+ # A default usage string based on the option value type.
60
88
  #
61
89
  # @raise [TypeError]
62
90
  # The given type was not a Class, Hash, Array, or Regexp.
@@ -85,6 +113,17 @@ module CommandKit
85
113
  string
86
114
  end
87
115
 
116
+ #
117
+ # Returns a new default value.
118
+ #
119
+ # @return [Object]
120
+ #
121
+ def default_value
122
+ if @default.respond_to?(:call) then @default.call
123
+ else @default.dup
124
+ end
125
+ end
126
+
88
127
  end
89
128
  end
90
129
  end
@@ -33,6 +33,9 @@ module CommandKit
33
33
  include Main
34
34
  include Printing
35
35
 
36
+ #
37
+ # @api private
38
+ #
36
39
  module ModuleMethods
37
40
  #
38
41
  # Sets {CommandKit::Usage::ClassMethods#usage .usage} or extends
@@ -58,6 +61,8 @@ module CommandKit
58
61
  # The option parser.
59
62
  #
60
63
  # @return [OptionParser]
64
+ #
65
+ # @api semipublic
61
66
  attr_reader :option_parser
62
67
 
63
68
  #
@@ -65,6 +70,8 @@ module CommandKit
65
70
  #
66
71
  # @return [OptionParser]
67
72
  #
73
+ # @api public
74
+ #
68
75
  def initialize(**kwargs)
69
76
  super(**kwargs)
70
77
 
@@ -91,6 +98,8 @@ module CommandKit
91
98
  # @return [Integer]
92
99
  # The exit status code.
93
100
  #
101
+ # @api public
102
+ #
94
103
  def main(argv=[])
95
104
  super(parse_options(argv))
96
105
  rescue SystemExit => system_exit
@@ -106,24 +115,24 @@ module CommandKit
106
115
  # @return [Array<String>]
107
116
  # The remaining non-option arguments.
108
117
  #
118
+ # @api semipublic
119
+ #
109
120
  def parse_options(argv)
110
- begin
111
- option_parser.parse(argv)
112
- rescue OptionParser::InvalidOption => error
113
- on_invalid_option(error)
114
- rescue OptionParser::AmbiguousOption => error
115
- on_ambiguous_option(error)
116
- rescue OptionParser::InvalidArgument => error
117
- on_invalid_argument(error)
118
- rescue OptionParser::MissingArgument => error
119
- on_missing_argument(error)
120
- rescue OptionParser::NeedlessArgument => error
121
- on_needless_argument(error)
122
- rescue OptionParser::AmbiguousArgument => error
123
- on_ambiguous_argument(error)
124
- rescue OptionParser::ParseError => error
125
- on_parse_error(error)
126
- 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)
127
136
  end
128
137
 
129
138
  #
@@ -132,6 +141,8 @@ module CommandKit
132
141
  # @param [OptionParser::ParseError] error
133
142
  # The error from `OptionParser`.
134
143
  #
144
+ # @api semipublic
145
+ #
135
146
  def on_parse_error(error)
136
147
  print_error("#{command_name}: #{error.message}")
137
148
  print_error("Try '#{command_name} --help' for more information.")
@@ -145,6 +156,8 @@ module CommandKit
145
156
  #
146
157
  # @see on_parse_error
147
158
  #
159
+ # @api semipublic
160
+ #
148
161
  def on_invalid_option(error)
149
162
  on_parse_error(error)
150
163
  end
@@ -157,6 +170,8 @@ module CommandKit
157
170
  #
158
171
  # @see on_parse_error
159
172
  #
173
+ # @api semipublic
174
+ #
160
175
  def on_ambiguous_option(error)
161
176
  on_parse_error(error)
162
177
  end
@@ -169,6 +184,8 @@ module CommandKit
169
184
  #
170
185
  # @see on_parse_error
171
186
  #
187
+ # @api semipublic
188
+ #
172
189
  def on_invalid_argument(error)
173
190
  on_parse_error(error)
174
191
  end
@@ -181,6 +198,8 @@ module CommandKit
181
198
  #
182
199
  # @see on_parse_error
183
200
  #
201
+ # @api semipublic
202
+ #
184
203
  def on_missing_argument(error)
185
204
  on_parse_error(error)
186
205
  end
@@ -193,6 +212,8 @@ module CommandKit
193
212
  #
194
213
  # @see on_parse_error
195
214
  #
215
+ # @api semipublic
216
+ #
196
217
  def on_needless_argument(error)
197
218
  on_parse_error(error)
198
219
  end
@@ -205,6 +226,8 @@ module CommandKit
205
226
  #
206
227
  # @see on_parse_error
207
228
  #
229
+ # @api semipublic
230
+ #
208
231
  def on_ambiguous_argument(error)
209
232
  on_parse_error(error)
210
233
  end
@@ -212,6 +235,8 @@ module CommandKit
212
235
  #
213
236
  # Prints the `--help` output.
214
237
  #
238
+ # @api semipublic
239
+ #
215
240
  def help_options
216
241
  puts option_parser
217
242
  end
@@ -219,6 +244,8 @@ module CommandKit
219
244
  #
220
245
  # @see #help_options
221
246
  #
247
+ # @api public
248
+ #
222
249
  def help
223
250
  help_options
224
251
  end