command_kit 0.1.0.pre1 → 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.
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
data/Rakefile CHANGED
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  require 'rubygems'
4
2
 
5
3
  begin
@@ -21,3 +19,6 @@ task :default => :spec
21
19
  require 'yard'
22
20
  YARD::Rake::YardocTask.new
23
21
  task :doc => :yard
22
+
23
+ require 'rubocop/rake_task'
24
+ RuboCop::RakeTask.new
data/command_kit.gemspec CHANGED
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  require 'yaml'
4
2
 
5
3
  Gem::Specification.new do |gem|
@@ -20,11 +18,13 @@ Gem::Specification.new do |gem|
20
18
  gem.authors = Array(gemspec['authors'])
21
19
  gem.email = gemspec['email']
22
20
  gem.homepage = gemspec['homepage']
21
+ gem.metadata = gemspec['metadata'] if gemspec['metadata']
23
22
 
24
23
  glob = lambda { |patterns| gem.files & Dir[*patterns] }
25
24
 
26
- gem.files = `git ls-files`.split($/)
27
- gem.files = glob[gemspec['files']] if gemspec['files']
25
+ gem.files = if gemspec['files'] then glob[gemspec['files']]
26
+ else `git ls-files`.split($/)
27
+ end
28
28
 
29
29
  gem.executables = gemspec.fetch('executables') do
30
30
  glob['bin/*'].map { |path| File.basename(path) }
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path('../../lib',__FILE__))
4
+ require 'command_kit/command'
5
+ require 'command_kit/colors'
6
+
7
+ class ColorsCmd < CommandKit::Command
8
+
9
+ include CommandKit::Colors
10
+
11
+ description "Prints all of the standard ANSI colors"
12
+
13
+ def run
14
+ colors do |c|
15
+ puts c.black("Black") + "\t" + c.bold(c.black("Bold"))
16
+ puts c.red("Red") + "\t" + c.bold(c.red("Bold"))
17
+ puts c.green("Green") + "\t" + c.bold(c.green("Bold"))
18
+ puts c.yellow("Yellow") + "\t" + c.bold(c.yellow("Bold"))
19
+ puts c.blue("Blue") + "\t" + c.bold(c.blue("Bold"))
20
+ puts c.magenta("Magenta") + "\t" + c.bold(c.magenta("Bold"))
21
+ puts c.cyan("Cyan") + "\t" + c.bold(c.cyan("Bold"))
22
+ puts c.cyan("White") + "\t" + c.bold(c.white("Bold"))
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ if __FILE__ == $0
29
+ ColorsCmd.start
30
+ end
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path('../../lib',__FILE__))
4
+ require 'command_kit/command'
5
+
6
+ class Command < CommandKit::Command
7
+
8
+ usage '[OPTIONS] [-o OUTPUT] FILE'
9
+
10
+ option :count, short: '-c',
11
+ value: {
12
+ type: Integer,
13
+ default: 1
14
+ },
15
+ desc: "Number of times"
16
+
17
+ option :output, value: {
18
+ type: String,
19
+ usage: 'FILE'
20
+ },
21
+ short: '-o',
22
+ desc: "Optional output file"
23
+
24
+ option :verbose, short: '-v', desc: "Increase verbose level" do
25
+ @verbose += 1
26
+ end
27
+
28
+ argument :file, required: true,
29
+ usage: 'FILE',
30
+ desc: "Input file"
31
+
32
+ examples [
33
+ '-o path/to/output.txt path/to/input.txt',
34
+ '-v -c 2 -o path/to/output.txt path/to/input.txt'
35
+ ]
36
+
37
+ description "Example command"
38
+
39
+ def initialize
40
+ super
41
+
42
+ @verbose = 0
43
+ end
44
+
45
+ def run(file)
46
+ unless options.empty?
47
+ puts "Options:"
48
+ options.each do |name,value|
49
+ puts " #{name.inspect} => #{value.inspect}"
50
+ end
51
+ puts
52
+ end
53
+
54
+ puts "Arguments:"
55
+ puts " file = #{file.inspect}"
56
+ puts
57
+
58
+ puts "Custom Variables:"
59
+ puts " version = #{@verbose.inspect}"
60
+ end
61
+ end
62
+
63
+ if __FILE__ == $0
64
+ Command.start
65
+ end
data/examples/pager.rb ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path('../../lib',__FILE__))
4
+ require 'command_kit/command'
5
+ require 'command_kit/pager'
6
+
7
+ class PagerCmd < CommandKit::Command
8
+
9
+ include CommandKit::Pager
10
+
11
+ description "Demos using the pager"
12
+
13
+ def run
14
+ puts "Starting pager ..."
15
+
16
+ pager do |io|
17
+ 10.times do |i|
18
+ io.puts i
19
+ sleep 0.5
20
+ end
21
+ end
22
+
23
+ puts "Exiting pager ..."
24
+ end
25
+
26
+ end
27
+
28
+ if __FILE__ == $0
29
+ PagerCmd.start
30
+ end
data/gemspec.yml CHANGED
@@ -2,11 +2,19 @@ name: command_kit
2
2
  summary: A toolkit for building Ruby CLI commands
3
3
  description:
4
4
  A Ruby toolkit for building clean, correct, and robust CLI commands as
5
- Ruby classes.
5
+ plain-old Ruby classes.
6
+
6
7
  license: MIT
7
8
  authors: Postmodern
8
9
  email: postmodern.mod3@gmail.com
9
- homepage: https://github.com/postmodern/command_kit#readme
10
+ homepage: https://github.com/postmodern/command_kit.rb#readme
11
+
12
+ metadata:
13
+ documentation_uri: https://rubydoc.info/gems/command_kit
14
+ source_code_uri: https://github.com/postmodern/command_kit.rb
15
+ bug_tracker_uri: https://github.com/postmodern/command_kit.rb/issues
16
+ changelog_uri: https://github.com/postmodern/command_kit.rb/blob/main/ChangeLog.md
17
+
10
18
 
11
19
  required_ruby_version: ">= 2.7.0"
12
20
 
@@ -5,84 +5,56 @@ module CommandKit
5
5
  #
6
6
  # Represents a defined argument.
7
7
  #
8
+ # @api private
9
+ #
8
10
  class Argument < ArgumentValue
9
11
 
12
+ # The argument's name.
13
+ #
10
14
  # @return [Symbol]
11
15
  attr_reader :name
12
16
 
13
- # @return [Boolean]
14
- attr_reader :repeats
15
-
16
- # @return [String, nil]
17
+ # The argument's description.
18
+ #
19
+ # @return [String]
17
20
  attr_reader :desc
18
21
 
19
- # @return [Regexp, nil]
20
- attr_reader :pattern
21
-
22
- # @return [Proc, nil]
23
- attr_reader :parser
24
-
25
- # @return [Proc, nil]
26
- attr_reader :block
27
-
28
22
  #
29
23
  # Initializes the argument.
30
24
  #
31
25
  # @param [Symbol] name
32
- #
33
- # @param [Class, Hash, Array, Regexp] type
26
+ # The name of the argument.
34
27
  #
35
28
  # @param [String, nil] usage
36
- #
37
- # @param [Object, Proc, nil] default
29
+ # The usage string for the argument. Defaults to the argument's name.
38
30
  #
39
31
  # @param [Boolean] required
32
+ # Specifies whether the argument is required or optional.
40
33
  #
41
34
  # @param [Boolean] repeats
35
+ # Specifies whether the argument can be repeated multiple times.
42
36
  #
43
37
  # @param [String] desc
44
- #
45
- # @note `usage` will be assigned a default value based on `type` and
46
- # `name`.
38
+ # The description for the argument.
47
39
  #
48
40
  # @yield [(value)]
41
+ # If a block is given, it will be used to parse the argument's value.
42
+ # Note: not currently used.
49
43
  #
50
44
  # @yieldparam [Object, nil] value
51
45
  #
52
- def initialize(name, type: String,
53
- usage: name.to_s.upcase,
54
- default: nil,
46
+ def initialize(name, usage: name.to_s.upcase,
55
47
  required: true,
56
48
  repeats: false,
57
- desc: ,
58
- &block)
49
+ desc: )
59
50
  super(
60
- type: type,
61
51
  usage: usage,
62
- default: default,
63
52
  required: required
64
53
  )
65
54
 
66
55
  @name = name
67
56
  @repeats = repeats
68
57
  @desc = desc
69
-
70
- @pattern, @parser = self.class.parser(@type)
71
-
72
- @block = block
73
- end
74
-
75
- #
76
- # Looks up the option parser for the given `OptionParser` type.
77
- #
78
- # @param [Class] type
79
- # The `OptionParser` type class.
80
- #
81
- # @return [(Regexp, Proc), nil]
82
- # The matching pattern and converter proc.
83
- #
84
- def self.parser(type)
85
- OptionParser::DefaultList.search(:atype,type)
86
58
  end
87
59
 
88
60
  #
@@ -3,18 +3,10 @@ module CommandKit
3
3
  #
4
4
  # Represents an individual argument value.
5
5
  #
6
+ # @api private
7
+ #
6
8
  class ArgumentValue
7
9
 
8
- # The desired type of the argument value.
9
- #
10
- # @return [Class, Hash, Array, Regexp, nil]
11
- attr_reader :type
12
-
13
- # The default parsed value for the argument value.
14
- #
15
- # @return [Object, Proc, nil]
16
- attr_reader :default
17
-
18
10
  # Specifies whether the argument value is required or optional.
19
11
  #
20
12
  # @return [Boolean]
@@ -28,22 +20,14 @@ module CommandKit
28
20
  #
29
21
  # Initializes the argument value.
30
22
  #
31
- # @param [Class, Hash, Array, Regexp] type
32
- # The type of the argument value.
33
- #
34
23
  # @param [Boolean] required
35
24
  # Specifies whether the argument value is required or optional.
36
25
  #
37
26
  # @param [String] usage
38
27
  # The usage string to represent the argument value.
39
28
  #
40
- # @param [Object, Proc, nil] default
41
- # The default parsed value for the argument value.
42
- #
43
- def initialize(type: nil, required: true, default: nil, usage: )
44
- @type = type
29
+ def initialize(required: true, usage: )
45
30
  @required = required
46
- @default = default
47
31
  @usage = usage
48
32
  end
49
33
 
@@ -65,17 +49,6 @@ module CommandKit
65
49
  !@required
66
50
  end
67
51
 
68
- #
69
- # Returns a new default value.
70
- #
71
- # @return [Object]
72
- #
73
- def default_value
74
- if @default.respond_to?(:call) then @default.call
75
- else @default.dup
76
- end
77
- end
78
-
79
52
  end
80
53
  end
81
54
  end
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'command_kit/main'
1
4
  require 'command_kit/help'
2
5
  require 'command_kit/arguments/argument'
3
6
 
@@ -9,15 +12,45 @@ module CommandKit
9
12
  #
10
13
  # include CommandKit::Arguments
11
14
  #
12
- # argument :output, type: String,
13
- # desc: 'The output file'
15
+ # argument :output, desc: 'The output file'
16
+ #
17
+ # argument :input, desc: 'The input file(s)'
14
18
  #
15
- # argument :input, type: Array,
16
- # desc: 'The input file(s)'
19
+ # def run(output,input)
20
+ # end
21
+ #
22
+ # ### Optional Arguments
23
+ #
24
+ # argument :dir, required: false,
25
+ # desc: 'Can be omitted'
26
+ #
27
+ # def run(dir=nil)
28
+ # end
29
+ #
30
+ # ### Repeating Arguments
31
+ #
32
+ # argument :files, repeats: true,
33
+ # desc: 'Can be repeated one or more times'
34
+ #
35
+ # def run(*files)
36
+ # end
37
+ #
38
+ # ### Optional Repeating Arguments
39
+ #
40
+ # argument :files, required: true,
41
+ # repeats: true,
42
+ # desc: 'Can be repeated one or more times'
43
+ #
44
+ # def run(*files)
45
+ # end
17
46
  #
18
47
  module Arguments
48
+ include Main
19
49
  include Help
20
50
 
51
+ #
52
+ # @api private
53
+ #
21
54
  module ModuleMethods
22
55
  #
23
56
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether
@@ -49,6 +82,8 @@ module CommandKit
49
82
  # @return [Hash{Symbol => Argument}]
50
83
  # The defined argument for the class and it's superclass.
51
84
  #
85
+ # @api semipublic
86
+ #
52
87
  def arguments
53
88
  @arguments ||= if superclass.kind_of?(ClassMethods)
54
89
  superclass.arguments.dup
@@ -61,13 +96,22 @@ module CommandKit
61
96
  # Defines an argument for the class.
62
97
  #
63
98
  # @param [Symbol] name
64
- # The argument name.
99
+ # The name of the argument.
100
+ #
101
+ # @param [Hash{Symbol => Object}] kwargs
102
+ # Keyword arguments.
65
103
  #
66
- # @yield [(arg)]
67
- # If a block is given, it will be passed the parsed argument.
104
+ # @option kwargs [String, nil] usage
105
+ # The usage string for the argument. Defaults to the argument's name.
68
106
  #
69
- # @yieldparam [Object, nil] arg
70
- # The parsed argument.
107
+ # @option kwargs [Boolean] required
108
+ # Specifies whether the argument is required or optional.
109
+ #
110
+ # @option kwargs [Boolean] repeats
111
+ # Specifies whether the argument can be repeated multiple times.
112
+ #
113
+ # @option kwargs [String] desc
114
+ # The description for the argument.
71
115
  #
72
116
  # @return [Argument]
73
117
  # The newly defined argument.
@@ -79,14 +123,8 @@ module CommandKit
79
123
  # option :bar, usage: 'BAR',
80
124
  # desc: "Bar argument"
81
125
  #
82
- # @example With a custom block:
83
- # argument :bar, desc: "Bar argument" do |bar|
84
- # # ...
85
- # end
86
- #
87
126
  # @example With a custom type:
88
- # argument :bar, type: Integer,
89
- # desc: "Bar argument"
127
+ # argument :bar, desc: "Bar argument"
90
128
  #
91
129
  # @example With a default value:
92
130
  # argument :bar, default: "bar.txt",
@@ -100,8 +138,10 @@ module CommandKit
100
138
  # argument :bar, repeats: true,
101
139
  # desc: "Bar argument"
102
140
  #
103
- def argument(name,**kwargs,&block)
104
- arguments[name] = Argument.new(name,**kwargs,&block)
141
+ # @api public
142
+ #
143
+ def argument(name,**kwargs)
144
+ arguments[name] = Argument.new(name,**kwargs)
105
145
  end
106
146
  end
107
147
 
@@ -116,6 +156,8 @@ module CommandKit
116
156
  # The exit status code. If too few or too many arguments are given, then
117
157
  # an error message is printed and `1` is returned.
118
158
  #
159
+ # @api public
160
+ #
119
161
  def main(argv=[])
120
162
  required_args = self.class.arguments.each_value.count(&:required?)
121
163
  optional_args = self.class.arguments.each_value.count(&:optional?)
@@ -137,12 +179,14 @@ module CommandKit
137
179
  #
138
180
  # Prints any defined arguments, along with the usual `--help` information.
139
181
  #
182
+ # @api semipublic
183
+ #
140
184
  def help_arguments
141
185
  unless (arguments = self.class.arguments).empty?
142
186
  puts
143
187
  puts 'Arguments:'
144
188
 
145
- self.class.arguments.each_value do |arg|
189
+ arguments.each_value do |arg|
146
190
  puts " #{arg.usage.ljust(33)}#{arg.desc}"
147
191
  end
148
192
  end
@@ -152,8 +196,10 @@ module CommandKit
152
196
  # Calls the superclass'es `#help` method, if it's defined, then calls
153
197
  # {#help_arguments}.
154
198
  #
199
+ # @api public
200
+ #
155
201
  def help
156
- super if defined?(super)
202
+ super
157
203
 
158
204
  help_arguments
159
205
  end