command_kit 0.1.0.rc1 → 0.1.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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +1 -1
  3. data/README.md +7 -4
  4. data/gemspec.yml +1 -1
  5. data/lib/command_kit.rb +1 -0
  6. data/lib/command_kit/arguments.rb +16 -1
  7. data/lib/command_kit/arguments/argument.rb +2 -0
  8. data/lib/command_kit/arguments/argument_value.rb +2 -0
  9. data/lib/command_kit/colors.rb +32 -0
  10. data/lib/command_kit/command.rb +5 -0
  11. data/lib/command_kit/command_name.rb +9 -0
  12. data/lib/command_kit/commands.rb +30 -0
  13. data/lib/command_kit/commands/auto_load.rb +16 -0
  14. data/lib/command_kit/commands/auto_require.rb +16 -0
  15. data/lib/command_kit/commands/command.rb +3 -0
  16. data/lib/command_kit/commands/help.rb +2 -0
  17. data/lib/command_kit/commands/parent_command.rb +7 -0
  18. data/lib/command_kit/commands/subcommand.rb +12 -0
  19. data/lib/command_kit/description.rb +12 -1
  20. data/lib/command_kit/env.rb +4 -0
  21. data/lib/command_kit/env/home.rb +9 -0
  22. data/lib/command_kit/env/path.rb +15 -0
  23. data/lib/command_kit/examples.rb +12 -1
  24. data/lib/command_kit/exception_handler.rb +4 -0
  25. data/lib/command_kit/help.rb +7 -1
  26. data/lib/command_kit/help/man.rb +13 -0
  27. data/lib/command_kit/inflector.rb +2 -0
  28. data/lib/command_kit/interactive.rb +62 -1
  29. data/lib/command_kit/main.rb +11 -0
  30. data/lib/command_kit/options.rb +12 -0
  31. data/lib/command_kit/options/option.rb +2 -0
  32. data/lib/command_kit/options/option_value.rb +2 -0
  33. data/lib/command_kit/options/parser.rb +29 -0
  34. data/lib/command_kit/options/quiet.rb +3 -0
  35. data/lib/command_kit/options/verbose.rb +5 -0
  36. data/lib/command_kit/options/version.rb +6 -0
  37. data/lib/command_kit/os.rb +6 -0
  38. data/lib/command_kit/pager.rb +27 -0
  39. data/lib/command_kit/printing.rb +23 -0
  40. data/lib/command_kit/printing/indent.rb +23 -0
  41. data/lib/command_kit/program_name.rb +7 -0
  42. data/lib/command_kit/stdio.rb +24 -0
  43. data/lib/command_kit/terminal.rb +12 -0
  44. data/lib/command_kit/usage.rb +14 -0
  45. data/lib/command_kit/version.rb +1 -1
  46. data/lib/command_kit/xdg.rb +13 -0
  47. data/spec/arguments/argument_spec.rb +1 -1
  48. data/spec/arguments_spec.rb +3 -27
  49. data/spec/colors_spec.rb +21 -13
  50. data/spec/command_name_spec.rb +1 -1
  51. data/spec/command_spec.rb +4 -1
  52. data/spec/commands/auto_load/subcommand_spec.rb +1 -1
  53. data/spec/commands/auto_load_spec.rb +1 -1
  54. data/spec/commands/auto_require_spec.rb +2 -2
  55. data/spec/commands/help_spec.rb +1 -1
  56. data/spec/commands/parent_command_spec.rb +1 -1
  57. data/spec/commands/subcommand_spec.rb +1 -1
  58. data/spec/commands_spec.rb +1 -1
  59. data/spec/description_spec.rb +1 -25
  60. data/spec/env/home_spec.rb +1 -1
  61. data/spec/env/path_spec.rb +1 -1
  62. data/spec/examples_spec.rb +1 -25
  63. data/spec/help/man_spec.rb +1 -1
  64. data/spec/help_spec.rb +0 -25
  65. data/spec/inflector_spec.rb +1 -1
  66. data/spec/main_spec.rb +7 -7
  67. data/spec/options/option_spec.rb +3 -3
  68. data/spec/options/option_value_spec.rb +1 -1
  69. data/spec/options_spec.rb +1 -1
  70. data/spec/os_spec.rb +1 -1
  71. data/spec/pager_spec.rb +1 -1
  72. data/spec/printing/indent_spec.rb +1 -1
  73. data/spec/printing_spec.rb +10 -2
  74. data/spec/program_name_spec.rb +1 -1
  75. data/spec/spec_helper.rb +0 -3
  76. data/spec/terminal_spec.rb +1 -1
  77. data/spec/usage_spec.rb +1 -1
  78. data/spec/xdg_spec.rb +1 -1
  79. metadata +3 -3
@@ -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
 
@@ -3,6 +3,8 @@ module CommandKit
3
3
  #
4
4
  # Represents a registered subcommand.
5
5
  #
6
+ # @api private
7
+ #
6
8
  class Subcommand
7
9
 
8
10
  # The command class.
@@ -40,6 +42,16 @@ module CommandKit
40
42
  @aliases = aliases.map(&:to_s)
41
43
  end
42
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
+ #
43
55
  def self.summary(command)
44
56
  if command.respond_to?(:description)
45
57
  if (desc = command.description)
@@ -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,6 +53,8 @@ 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
@@ -64,6 +69,8 @@ module CommandKit
64
69
  #
65
70
  # @see ClassMethods#description
66
71
  #
72
+ # @api semipublic
73
+ #
67
74
  def description
68
75
  self.class.description
69
76
  end
@@ -71,6 +78,8 @@ module CommandKit
71
78
  #
72
79
  # Prints the {ClassMethods#description description}, if set.
73
80
  #
81
+ # @api semipublic
82
+ #
74
83
  def help_description
75
84
  if (description = self.description)
76
85
  puts
@@ -82,8 +91,10 @@ module CommandKit
82
91
  # Calls the superclass'es `#help` method, if it's defined, then calls
83
92
  # {#help_description}.
84
93
  #
94
+ # @api public
95
+ #
85
96
  def help
86
- super if defined?(super)
97
+ super
87
98
 
88
99
  help_description
89
100
  end
@@ -23,6 +23,8 @@ module CommandKit
23
23
  # The environment variables hash.
24
24
  #
25
25
  # @return [Hash{String => String}]
26
+ #
27
+ # @api public
26
28
  attr_reader :env
27
29
 
28
30
  #
@@ -34,6 +36,8 @@ module CommandKit
34
36
  # @param [Hash{Symbol => Object}] kwargs
35
37
  # Additional keyword arguments.
36
38
  #
39
+ # @api public
40
+ #
37
41
  def initialize(env: ENV, **kwargs)
38
42
  @env = env
39
43
 
@@ -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
 
@@ -17,6 +17,8 @@ module CommandKit
17
17
  # The home directory.
18
18
  #
19
19
  # @return [String]
20
+ #
21
+ # @api semipublic
20
22
  attr_reader :path_dirs
21
23
 
22
24
  #
@@ -25,6 +27,8 @@ module CommandKit
25
27
  # @param [Hash{Symbol => Object}] kwargs
26
28
  # Additional keyword arguments.
27
29
  #
30
+ # @api public
31
+ #
28
32
  def initialize(**kwargs)
29
33
  super(**kwargs)
30
34
 
@@ -41,6 +45,8 @@ module CommandKit
41
45
  # The absolute path to the executable file, or `nil` if the command
42
46
  # could not be found in any of the {#path_dirs}.
43
47
  #
48
+ # @api public
49
+ #
44
50
  def find_command(name)
45
51
  name = name.to_s
46
52
 
@@ -63,6 +69,15 @@ module CommandKit
63
69
  # Specifies whether a command with the given name exists in one of the
64
70
  # {#path_dirs}.
65
71
  #
72
+ # @example
73
+ # if command_installed?("docker")
74
+ # # ...
75
+ # else
76
+ # abort "Docker is not installed. Aborting"
77
+ # end
78
+ #
79
+ # @api public
80
+ #
66
81
  def command_installed?(name)
67
82
  !find_command(name).nil?
68
83
  end
@@ -17,6 +17,9 @@ module CommandKit
17
17
  include Help
18
18
  include CommandName
19
19
 
20
+ #
21
+ # @api private
22
+ #
20
23
  module ModuleMethods
21
24
  #
22
25
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether
@@ -57,6 +60,8 @@ module CommandKit
57
60
  # "-o output.txt path/to/file"
58
61
  # ]
59
62
  #
63
+ # @api public
64
+ #
60
65
  def examples(new_examples=nil)
61
66
  if new_examples
62
67
  @examples = Array(new_examples)
@@ -71,6 +76,8 @@ module CommandKit
71
76
  #
72
77
  # @see ClassMethods#examples
73
78
  #
79
+ # @api semipublic
80
+ #
74
81
  def examples
75
82
  self.class.examples
76
83
  end
@@ -78,6 +85,8 @@ module CommandKit
78
85
  #
79
86
  # Prints the command class'es example commands.
80
87
  #
88
+ # @api semipublic
89
+ #
81
90
  def help_examples
82
91
  if (examples = self.examples)
83
92
  puts
@@ -92,8 +101,10 @@ module CommandKit
92
101
  # Calls the superclass'es `#help` method, if it's defined, then calls
93
102
  # {#help_examples}.
94
103
  #
104
+ # @api public
105
+ #
95
106
  def help
96
- super if defined?(super)
107
+ super
97
108
 
98
109
  help_examples
99
110
  end
@@ -33,6 +33,8 @@ module CommandKit
33
33
  # @return [Integer]
34
34
  # The exit status of the command.
35
35
  #
36
+ # @api public
37
+ #
36
38
  def main(argv=[])
37
39
  super(argv)
38
40
  rescue Interrupt, Errno::EPIPE => error
@@ -47,6 +49,8 @@ module CommandKit
47
49
  # @param [Exception] error
48
50
  # The raised exception.
49
51
  #
52
+ # @api semipublic
53
+ #
50
54
  def on_exception(error)
51
55
  print_exception(error)
52
56
  exit(1)
@@ -15,6 +15,9 @@ module CommandKit
15
15
  # MyCmd.help
16
16
  #
17
17
  module Help
18
+ #
19
+ # @api private
20
+ #
18
21
  module ModuleMethods
19
22
  #
20
23
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether {Help}
@@ -48,6 +51,8 @@ module CommandKit
48
51
  #
49
52
  # @see Help#help
50
53
  #
54
+ # @api public
55
+ #
51
56
  def help(**kwargs)
52
57
  new(**kwargs).help
53
58
  end
@@ -58,8 +63,9 @@ module CommandKit
58
63
  #
59
64
  # @abstract
60
65
  #
66
+ # @api public
67
+ #
61
68
  def help
62
- super if defined?(super)
63
69
  end
64
70
  end
65
71
  end
@@ -24,6 +24,9 @@ module CommandKit
24
24
  include Help
25
25
  include Stdio
26
26
 
27
+ #
28
+ # @api private
29
+ #
27
30
  module ModuleMethods
28
31
  #
29
32
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether
@@ -61,6 +64,8 @@ module CommandKit
61
64
  # @example
62
65
  # man_dir "#{__dir__}/../../man"
63
66
  #
67
+ # @api public
68
+ #
64
69
  def man_dir(new_man_dir=nil)
65
70
  if new_man_dir
66
71
  @man_dir = new_man_dir
@@ -80,6 +85,8 @@ module CommandKit
80
85
  # @return [String]
81
86
  # The class'es or superclass'es man-page file name.
82
87
  #
88
+ # @api public
89
+ #
83
90
  def man_page(new_man_page=nil)
84
91
  if new_man_page
85
92
  @man_page = new_man_page
@@ -102,6 +109,8 @@ module CommandKit
102
109
  # Specifies whether the `man` command was successful or not.
103
110
  # Returns `nil` when the `man` command is not installed.
104
111
  #
112
+ # @api public
113
+ #
105
114
  def man(page, section: nil)
106
115
  if section
107
116
  system('man',section.to_s,page.to_s)
@@ -124,6 +133,8 @@ module CommandKit
124
133
  # @raise [NotImplementedError]
125
134
  # {ClassMethods#man_dir .man_dir} does not have a value.
126
135
  #
136
+ # @api semipublic
137
+ #
127
138
  def help_man(man_page=self.class.man_page)
128
139
  unless self.class.man_dir
129
140
  raise(NotImplementedError,"#{self.class}.man_dir not set")
@@ -145,6 +156,8 @@ module CommandKit
145
156
  # if `TERM` is `dumb` or `$stdout` is not a TTY, fallsback to printing
146
157
  # the usual `--help` output.
147
158
  #
159
+ # @api public
160
+ #
148
161
  def help
149
162
  if stdout.tty?
150
163
  if help_man.nil?
@@ -8,6 +8,8 @@ module CommandKit
8
8
  # If you need something more powerful, checkout
9
9
  # [dry-inflector](https://dry-rb.org/gems/dry-inflector/0.1/)
10
10
  #
11
+ # @api semipublic
12
+ #
11
13
  module Inflector
12
14
  #
13
15
  # Removes the namespace from a constant name.
@@ -1,6 +1,39 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'command_kit/stdio'
2
4
 
3
5
  module CommandKit
6
+ #
7
+ # Provides methods for asking the user for input.
8
+ #
9
+ # ## Examples
10
+ #
11
+ # first_name = ask("First name")
12
+ # last_name = ask("Last name")
13
+ #
14
+ # ### Asking for secret input
15
+ #
16
+ # password = ask_secret("Password")
17
+ #
18
+ # ### Asking Y/N?
19
+ #
20
+ # if ask_yes_or_no("Proceed anyways?")
21
+ # # ...
22
+ # else
23
+ # stderr.puts "Aborting!"
24
+ # end
25
+ #
26
+ # ### Asking multi-choice questions
27
+ #
28
+ # ask_multiple_choice("Select a flavor", %w[Apple Orange Lemon Lime])
29
+ # # 1) Apple
30
+ # # 2) Orange
31
+ # # 3) Lemon
32
+ # # 4) Lime
33
+ # # Select a flavor: 4
34
+ # #
35
+ # # => "Lime"
36
+ #
4
37
  module Interactive
5
38
  include Stdio
6
39
 
@@ -19,6 +52,23 @@ module CommandKit
19
52
  # @return [String]
20
53
  # The user input.
21
54
  #
55
+ # @example
56
+ # first_name = ask("First name")
57
+ # last_name = ask("Last name")
58
+ #
59
+ # @example Default value:
60
+ # ask("Country", default: "EU")
61
+ # # Country [EU]: <enter>
62
+ # # => "EU"
63
+ #
64
+ # @example Required non-empty input:
65
+ # ask("Email", required: true)
66
+ # # Email: <enter>
67
+ # # Email: bob@example.com<enter>
68
+ # # => "bob@example.com"
69
+ #
70
+ # @api public
71
+ #
22
72
  def ask(prompt, default: nil, required: false)
23
73
  prompt = prompt.chomp
24
74
  prompt << " [#{default}]" if default
@@ -56,7 +106,14 @@ module CommandKit
56
106
  # @example
57
107
  # ask_yes_or_no("Proceed anyways?")
58
108
  # # Proceed anyways? (Y/N): Y
59
- # # => :yes
109
+ # # => true
110
+ #
111
+ # @example Default value:
112
+ # ask_yes_or_no("Proceed anyways?", default: true)
113
+ # # Proceed anyways? (Y/N) [Y]: <enter>
114
+ # # => true
115
+ #
116
+ # @api public
60
117
  #
61
118
  def ask_yes_or_no(prompt, default: nil, **kwargs)
62
119
  default = case default
@@ -123,6 +180,8 @@ module CommandKit
123
180
  # #
124
181
  # # => "All of the above"
125
182
  #
183
+ # @api public
184
+ #
126
185
  def ask_multiple_choice(prompt,choices,**kwargs)
127
186
  choices = case choices
128
187
  when Array
@@ -164,6 +223,8 @@ module CommandKit
164
223
  # # Password:
165
224
  # # => "s3cr3t"
166
225
  #
226
+ # @api public
227
+ #
167
228
  def ask_secret(prompt, required: true)
168
229
  if stdin.respond_to?(:noecho)
169
230
  stdin.noecho do