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
@@ -31,6 +31,9 @@ module CommandKit
31
31
  # # => "foo-cmd"
32
32
  #
33
33
  module CommandName
34
+ #
35
+ # @api private
36
+ #
34
37
  module ModuleMethods
35
38
  #
36
39
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether
@@ -65,6 +68,8 @@ module CommandKit
65
68
  #
66
69
  # @return [String]
67
70
  #
71
+ # @api public
72
+ #
68
73
  def command_name(new_command_name=nil)
69
74
  if new_command_name
70
75
  @command_name = new_command_name.to_s
@@ -77,6 +82,8 @@ module CommandKit
77
82
  # The commands name.
78
83
  #
79
84
  # @return [String]
85
+ #
86
+ # @api public
80
87
  attr_reader :command_name
81
88
 
82
89
  #
@@ -86,6 +93,8 @@ module CommandKit
86
93
  # Overrides the command name. Defaults to
87
94
  # {ClassMethods#command_name self.class.command_name}.
88
95
  #
96
+ # @api public
97
+ #
89
98
  def initialize(command_name: self.class.command_name, **kwargs)
90
99
  @command_name = command_name
91
100
 
@@ -3,6 +3,9 @@ require 'command_kit/commands/subcommand'
3
3
  module CommandKit
4
4
  module Commands
5
5
  class AutoLoad < Module
6
+ #
7
+ # Represents a registered subcommand that will be auto-loaded.
8
+ #
6
9
  class Subcommand < Commands::Subcommand
7
10
 
8
11
  # The fully qualified constant of the command class.
@@ -40,16 +40,22 @@ module CommandKit
40
40
  # The auto-load subcommands.
41
41
  #
42
42
  # @return [Hash{String => Subcommand}]
43
+ #
44
+ # @api private
43
45
  attr_reader :commands
44
46
 
45
47
  # The path to the directory containing the command files.
46
48
  #
47
49
  # @return [String]
50
+ #
51
+ # @api private
48
52
  attr_reader :dir
49
53
 
50
54
  # The namespace that the will contain the command classes.
51
55
  #
52
56
  # @return [String]
57
+ #
58
+ # @api private
53
59
  attr_reader :namespace
54
60
 
55
61
  #
@@ -65,6 +71,8 @@ module CommandKit
65
71
  # If a block is given, it will be used to explicitly map the files
66
72
  # within {#dir} as commands.
67
73
  #
74
+ # @api public
75
+ #
68
76
  def initialize(dir: , namespace: )
69
77
  @commands = {}
70
78
 
@@ -106,6 +114,8 @@ module CommandKit
106
114
  # @option kwargs [Array<String>] aliases
107
115
  # Optional alias names for the subcommand.
108
116
  #
117
+ # @api public
118
+ #
109
119
  def command(name, constant, file, **kwargs)
110
120
  @commands[name.to_s] = Subcommand.new(
111
121
  "#{@namespace}::#{constant}",
@@ -123,6 +133,8 @@ module CommandKit
123
133
  # @return [String]
124
134
  # The joined absolute path.
125
135
  #
136
+ # @api private
137
+ #
126
138
  def join(path)
127
139
  File.join(@dir,path)
128
140
  end
@@ -133,6 +145,8 @@ module CommandKit
133
145
  # @return [Array<String>]
134
146
  # The paths to the `.rb` files in the directory.
135
147
  #
148
+ # @api private
149
+ #
136
150
  def files
137
151
  Dir.glob(join('*.rb'))
138
152
  end
@@ -144,6 +158,8 @@ module CommandKit
144
158
  # @param [Class] command
145
159
  # The command class including {AutoLoad}.
146
160
  #
161
+ # @api private
162
+ #
147
163
  def included(command)
148
164
  command.include Commands
149
165
  command.commands.merge!(@commands)
@@ -27,11 +27,15 @@ module CommandKit
27
27
  # The directory to attempt to require command files within.
28
28
  #
29
29
  # @return [String]
30
+ #
31
+ # @api private
30
32
  attr_reader :dir
31
33
 
32
34
  # The namespace to lookup command classes within.
33
35
  #
34
36
  # @return [String]
37
+ #
38
+ # @api private
35
39
  attr_reader :namespace
36
40
 
37
41
  #
@@ -43,6 +47,8 @@ module CommandKit
43
47
  # @param [String] namespace
44
48
  # The namespace to search for command classes in.
45
49
  #
50
+ # @api public
51
+ #
46
52
  def initialize(dir: , namespace: )
47
53
  @dir = dir
48
54
  @namespace = namespace
@@ -57,6 +63,8 @@ module CommandKit
57
63
  # @return [String]
58
64
  # The path to the file that should contain the command.
59
65
  #
66
+ # @api private
67
+ #
60
68
  def join(name)
61
69
  File.join(@dir,name)
62
70
  end
@@ -70,6 +78,8 @@ module CommandKit
70
78
  #
71
79
  # @raise [LoadError]
72
80
  #
81
+ # @api private
82
+ #
73
83
  def require(file_name)
74
84
  super(join(file_name))
75
85
  end
@@ -86,6 +96,8 @@ module CommandKit
86
96
  # @raise [NameError]
87
97
  # The command class could not be found within the {#namespace}.
88
98
  #
99
+ # @api private
100
+ #
89
101
  def const_get(constant)
90
102
  Object.const_get("::#{@namespace}::#{constant}",false)
91
103
  end
@@ -100,6 +112,8 @@ module CommandKit
100
112
  # The command's class, or `nil` if the command cannot be loaded from
101
113
  # {#dir} or found within {#namespace}.
102
114
  #
115
+ # @api private
116
+ #
103
117
  def command(command_name)
104
118
  file_name = Inflector.underscore(command_name)
105
119
 
@@ -125,6 +139,8 @@ module CommandKit
125
139
  # @param [Class] command
126
140
  # The command class including {AutoRequire}.
127
141
  #
142
+ # @api private
143
+ #
128
144
  def included(command)
129
145
  command.include Commands
130
146
  command.commands.default_proc = ->(hash,key) {
@@ -3,6 +3,9 @@ require 'command_kit/commands/parent_command'
3
3
 
4
4
  module CommandKit
5
5
  module Commands
6
+ #
7
+ # @api public
8
+ #
6
9
  class Command < CommandKit::Command
7
10
 
8
11
  include ParentCommand
@@ -8,6 +8,8 @@ module CommandKit
8
8
  #
9
9
  # The default help command.
10
10
  #
11
+ # @api semipublic
12
+ #
11
13
  class Help < Command
12
14
 
13
15
  include ParentCommand
@@ -1,15 +1,22 @@
1
1
  module CommandKit
2
2
  module Commands
3
+ #
4
+ # Allows a command to be aware of it's parent command.
5
+ #
3
6
  module ParentCommand
4
7
 
5
8
  # The parent command instance.
6
9
  #
7
10
  # @return [Object<Commands>]
11
+ #
12
+ # @api semipublic
8
13
  attr_reader :parent_command
9
14
 
10
15
  #
11
16
  # Initializes the command and sets {#parent_command}.
12
17
  #
18
+ # @api public
19
+ #
13
20
  def initialize(parent_command: , **kwargs)
14
21
  @parent_command = parent_command
15
22
 
@@ -1,5 +1,10 @@
1
1
  module CommandKit
2
2
  module Commands
3
+ #
4
+ # Represents a registered subcommand.
5
+ #
6
+ # @api private
7
+ #
3
8
  class Subcommand
4
9
 
5
10
  # The command class.
@@ -37,6 +42,16 @@ module CommandKit
37
42
  @aliases = aliases.map(&:to_s)
38
43
  end
39
44
 
45
+ #
46
+ # Derives a summary from the command's description.
47
+ #
48
+ # @param [Class] command
49
+ # The command class.
50
+ #
51
+ # @return [String, nil]
52
+ # If the command responds to a `#description` method, the first sentence
53
+ # of the description will be returned. Otherwise `nil` is returned.
54
+ #
40
55
  def self.summary(command)
41
56
  if command.respond_to?(:description)
42
57
  if (desc = command.description)
@@ -41,6 +41,9 @@ module CommandKit
41
41
  include Stdio
42
42
  include Env
43
43
 
44
+ #
45
+ # @api private
46
+ #
44
47
  module ModuleMethods
45
48
  #
46
49
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether
@@ -73,13 +76,15 @@ module CommandKit
73
76
  #
74
77
  # @return [Hash{String => Subcommand}]
75
78
  # The Hash of sub-command names and command classes.
79
+ #
80
+ # @api semipublic
76
81
  #
77
82
  def commands
78
83
  @commands ||= if superclass.kind_of?(ClassMethods)
79
- superclass.commands.dup
80
- else
81
- {}
82
- end
84
+ superclass.commands.dup
85
+ else
86
+ {}
87
+ end
83
88
  end
84
89
 
85
90
  #
@@ -88,6 +93,8 @@ module CommandKit
88
93
  # @return [Hash{String => String}]
89
94
  # The Hash of command aliases to primary command names.
90
95
  #
96
+ # @api semipublic
97
+ #
91
98
  def command_aliases
92
99
  @command_aliases ||= if superclass.kind_of?(ClassMethods)
93
100
  superclass.command_aliases.dup
@@ -125,6 +132,8 @@ module CommandKit
125
132
  # @example
126
133
  # command 'foo-bar', FooBar
127
134
  #
135
+ # @api public
136
+ #
128
137
  def command(name=nil, command_class, **kwargs)
129
138
  name = if name then name.to_s
130
139
  else command_class.command_name
@@ -145,8 +154,12 @@ module CommandKit
145
154
  # Gets the command.
146
155
  #
147
156
  # @param [String] name
157
+ # The command name.
148
158
  #
149
159
  # @return [Class#main, nil]
160
+ # The command class or `nil` if no command could be found.
161
+ #
162
+ # @api private
150
163
  #
151
164
  def get_command(name)
152
165
  name = name.to_s
@@ -158,6 +171,14 @@ module CommandKit
158
171
  end
159
172
  end
160
173
 
174
+ #
175
+ # Initializes the command.
176
+ #
177
+ # @note Adds a special rule to the {Options#option_parser #option_parser} to
178
+ # stop parsing options when the first non-option is encountered.
179
+ #
180
+ # @api public
181
+ #
161
182
  def initialize(**kwargs)
162
183
  super(**kwargs)
163
184
 
@@ -175,6 +196,8 @@ module CommandKit
175
196
  # @return [Object#main, nil]
176
197
  # The initialized subcommand.
177
198
  #
199
+ # @api private
200
+ #
178
201
  def command(name)
179
202
  unless (command_class = self.class.get_command(name))
180
203
  return
@@ -219,6 +242,8 @@ module CommandKit
219
242
  # @return [Integer]
220
243
  # The exit status of the command.
221
244
  #
245
+ # @api semipublic
246
+ #
222
247
  def invoke(name,*argv)
223
248
  if (command = command(name))
224
249
  command.main(argv)
@@ -231,6 +256,9 @@ module CommandKit
231
256
  # Prints an error about an unknown command and exits with an error code.
232
257
  #
233
258
  # @param [String] name
259
+ # The command name.
260
+ #
261
+ # @api semipublic
234
262
  #
235
263
  def command_not_found(name)
236
264
  print_error "'#{name}' is not a #{command_name} command. See `#{command_name} help`"
@@ -250,6 +278,8 @@ module CommandKit
250
278
  #
251
279
  # @see command_not_found
252
280
  #
281
+ # @api semipublic
282
+ #
253
283
  def on_unknown_command(name,argv=[])
254
284
  command_not_found(name)
255
285
  end
@@ -259,6 +289,8 @@ module CommandKit
259
289
  #
260
290
  # @note If no subcommand is given, {#help} will be called.
261
291
  #
292
+ # @api public
293
+ #
262
294
  def run(command=nil,*argv)
263
295
  if command
264
296
  exit invoke(command,*argv)
@@ -270,6 +302,8 @@ module CommandKit
270
302
  #
271
303
  # Prints the available commands and their summaries.
272
304
  #
305
+ # @api semipublic
306
+ #
273
307
  def help_commands
274
308
  unless self.class.commands.empty?
275
309
  puts
@@ -290,6 +324,8 @@ module CommandKit
290
324
  #
291
325
  # Prints help information and available commands.
292
326
  #
327
+ # @api public
328
+ #
293
329
  def help
294
330
  super
295
331
 
@@ -13,6 +13,9 @@ module CommandKit
13
13
  module Description
14
14
  include Help
15
15
 
16
+ #
17
+ # @api private
18
+ #
16
19
  module ModuleMethods
17
20
  #
18
21
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether
@@ -50,11 +53,15 @@ module CommandKit
50
53
  # @example
51
54
  # description "Does things and stuff"
52
55
  #
56
+ # @api public
57
+ #
53
58
  def description(new_description=nil)
54
59
  if new_description
55
60
  @description = new_description
56
61
  else
57
- @description || (superclass.description if superclass.kind_of?(ClassMethods))
62
+ @description || if superclass.kind_of?(ClassMethods)
63
+ superclass.description
64
+ end
58
65
  end
59
66
  end
60
67
  end
@@ -62,6 +69,8 @@ module CommandKit
62
69
  #
63
70
  # @see ClassMethods#description
64
71
  #
72
+ # @api semipublic
73
+ #
65
74
  def description
66
75
  self.class.description
67
76
  end
@@ -69,6 +78,8 @@ module CommandKit
69
78
  #
70
79
  # Prints the {ClassMethods#description description}, if set.
71
80
  #
81
+ # @api semipublic
82
+ #
72
83
  def help_description
73
84
  if (description = self.description)
74
85
  puts
@@ -80,8 +91,10 @@ module CommandKit
80
91
  # Calls the superclass'es `#help` method, if it's defined, then calls
81
92
  # {#help_description}.
82
93
  #
94
+ # @api public
95
+ #
83
96
  def help
84
- super if defined?(super)
97
+ super
85
98
 
86
99
  help_description
87
100
  end
@@ -14,6 +14,9 @@ module CommandKit
14
14
  module Home
15
15
  include Env
16
16
 
17
+ #
18
+ # @api private
19
+ #
17
20
  module ModuleMethods
18
21
  #
19
22
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether
@@ -44,6 +47,8 @@ module CommandKit
44
47
  #
45
48
  # @return [String]
46
49
  #
50
+ # @api semipublic
51
+ #
47
52
  def home_dir
48
53
  Gem.user_home
49
54
  end
@@ -52,6 +57,8 @@ module CommandKit
52
57
  # The home directory.
53
58
  #
54
59
  # @return [String]
60
+ #
61
+ # @api public
55
62
  attr_reader :home_dir
56
63
 
57
64
  #
@@ -61,6 +68,8 @@ module CommandKit
61
68
  # @param [Hash{Symbol => Object}] kwargs
62
69
  # Additional keyword arguments.
63
70
  #
71
+ # @api public
72
+ #
64
73
  def initialize(**kwargs)
65
74
  super(**kwargs)
66
75