command_kit 0.1.0.pre1

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 (98) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.github/workflows/ruby.yml +29 -0
  4. data/.gitignore +7 -0
  5. data/.rspec +1 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +29 -0
  8. data/Gemfile +14 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +283 -0
  11. data/Rakefile +23 -0
  12. data/command_kit.gemspec +60 -0
  13. data/gemspec.yml +14 -0
  14. data/lib/command_kit.rb +1 -0
  15. data/lib/command_kit/arguments.rb +161 -0
  16. data/lib/command_kit/arguments/argument.rb +111 -0
  17. data/lib/command_kit/arguments/argument_value.rb +81 -0
  18. data/lib/command_kit/arguments/usage.rb +6 -0
  19. data/lib/command_kit/colors.rb +355 -0
  20. data/lib/command_kit/command.rb +42 -0
  21. data/lib/command_kit/command_name.rb +95 -0
  22. data/lib/command_kit/commands.rb +299 -0
  23. data/lib/command_kit/commands/auto_load.rb +153 -0
  24. data/lib/command_kit/commands/auto_load/subcommand.rb +90 -0
  25. data/lib/command_kit/commands/auto_require.rb +138 -0
  26. data/lib/command_kit/commands/command.rb +12 -0
  27. data/lib/command_kit/commands/help.rb +43 -0
  28. data/lib/command_kit/commands/parent_command.rb +21 -0
  29. data/lib/command_kit/commands/subcommand.rb +51 -0
  30. data/lib/command_kit/console.rb +141 -0
  31. data/lib/command_kit/description.rb +89 -0
  32. data/lib/command_kit/env.rb +43 -0
  33. data/lib/command_kit/env/home.rb +71 -0
  34. data/lib/command_kit/env/path.rb +71 -0
  35. data/lib/command_kit/examples.rb +99 -0
  36. data/lib/command_kit/exception_handler.rb +55 -0
  37. data/lib/command_kit/help.rb +62 -0
  38. data/lib/command_kit/help/man.rb +125 -0
  39. data/lib/command_kit/inflector.rb +84 -0
  40. data/lib/command_kit/main.rb +103 -0
  41. data/lib/command_kit/options.rb +179 -0
  42. data/lib/command_kit/options/option.rb +171 -0
  43. data/lib/command_kit/options/option_value.rb +90 -0
  44. data/lib/command_kit/options/parser.rb +227 -0
  45. data/lib/command_kit/options/quiet.rb +53 -0
  46. data/lib/command_kit/options/usage.rb +6 -0
  47. data/lib/command_kit/options/verbose.rb +55 -0
  48. data/lib/command_kit/options/version.rb +62 -0
  49. data/lib/command_kit/os.rb +47 -0
  50. data/lib/command_kit/pager.rb +115 -0
  51. data/lib/command_kit/printing.rb +32 -0
  52. data/lib/command_kit/printing/indent.rb +78 -0
  53. data/lib/command_kit/program_name.rb +57 -0
  54. data/lib/command_kit/stdio.rb +138 -0
  55. data/lib/command_kit/usage.rb +102 -0
  56. data/lib/command_kit/version.rb +4 -0
  57. data/lib/command_kit/xdg.rb +138 -0
  58. data/spec/arguments/argument_spec.rb +169 -0
  59. data/spec/arguments/argument_value_spec.rb +126 -0
  60. data/spec/arguments_spec.rb +213 -0
  61. data/spec/colors_spec.rb +470 -0
  62. data/spec/command_kit_spec.rb +8 -0
  63. data/spec/command_name_spec.rb +130 -0
  64. data/spec/command_spec.rb +49 -0
  65. data/spec/commands/auto_load/subcommand_spec.rb +82 -0
  66. data/spec/commands/auto_load_spec.rb +128 -0
  67. data/spec/commands/auto_require_spec.rb +142 -0
  68. data/spec/commands/fixtures/test_auto_load/cli/commands/test1.rb +10 -0
  69. data/spec/commands/fixtures/test_auto_load/cli/commands/test2.rb +10 -0
  70. data/spec/commands/fixtures/test_auto_require/lib/test_auto_require/cli/commands/test1.rb +10 -0
  71. data/spec/commands/help_spec.rb +66 -0
  72. data/spec/commands/parent_command_spec.rb +40 -0
  73. data/spec/commands/subcommand_spec.rb +99 -0
  74. data/spec/commands_spec.rb +767 -0
  75. data/spec/console_spec.rb +201 -0
  76. data/spec/description_spec.rb +203 -0
  77. data/spec/env/home_spec.rb +46 -0
  78. data/spec/env/path_spec.rb +78 -0
  79. data/spec/env_spec.rb +123 -0
  80. data/spec/examples_spec.rb +235 -0
  81. data/spec/exception_handler_spec.rb +103 -0
  82. data/spec/help_spec.rb +119 -0
  83. data/spec/inflector_spec.rb +104 -0
  84. data/spec/main_spec.rb +179 -0
  85. data/spec/options/option_spec.rb +258 -0
  86. data/spec/options/option_value_spec.rb +67 -0
  87. data/spec/options/parser_spec.rb +265 -0
  88. data/spec/options_spec.rb +137 -0
  89. data/spec/os_spec.rb +46 -0
  90. data/spec/pager_spec.rb +154 -0
  91. data/spec/printing/indent_spec.rb +130 -0
  92. data/spec/printing_spec.rb +76 -0
  93. data/spec/program_name_spec.rb +62 -0
  94. data/spec/spec_helper.rb +6 -0
  95. data/spec/stdio_spec.rb +264 -0
  96. data/spec/usage_spec.rb +237 -0
  97. data/spec/xdg_spec.rb +191 -0
  98. metadata +156 -0
@@ -0,0 +1,171 @@
1
+ require 'command_kit/options/option_value'
2
+
3
+ require 'optparse'
4
+ require 'date'
5
+ require 'time'
6
+ require 'uri'
7
+ require 'shellwords'
8
+
9
+ module CommandKit
10
+ module Options
11
+ #
12
+ # Represents a defined option.
13
+ #
14
+ class Option
15
+
16
+ # @return [Symbol]
17
+ attr_reader :name
18
+
19
+ # @return [String, nil]
20
+ attr_reader :short
21
+
22
+ # @return [String]
23
+ attr_reader :long
24
+
25
+ # @return [Boolean]
26
+ attr_reader :equals
27
+
28
+ # @return [OptionValue, nil]
29
+ attr_reader :value
30
+
31
+ # @return [String]
32
+ attr_reader :desc
33
+
34
+ # @return [Proc, nil]
35
+ attr_reader :block
36
+
37
+ #
38
+ # Initializes the option.
39
+ #
40
+ # @param [Symbol] name
41
+ #
42
+ # @param [String, nil] short
43
+ #
44
+ # @param [String, nil] long
45
+ #
46
+ # @param [Boolean] equals
47
+ #
48
+ # @param [Hash{Symbol => Object}, nil] value
49
+ # Keyword arguments for {OptionValue#initialize}, or `nil` if the option
50
+ # has no additional value.
51
+ #
52
+ # @option value [Class, Hash, Array, Regexp] type
53
+ #
54
+ # @option value [String, nil] usage
55
+ #
56
+ # @param [String] desc
57
+ #
58
+ # @yield [(value)]
59
+ #
60
+ # @yieldparam [Object, nil] value
61
+ #
62
+ def initialize(name, short: nil,
63
+ long: self.class.default_long_opt(name),
64
+ equals: false,
65
+ value: nil,
66
+ desc: ,
67
+ &block)
68
+ @name = name
69
+ @short = short
70
+ @long = long
71
+ @equals = equals
72
+ @value = OptionValue.new(**value) if value
73
+ @desc = desc
74
+ @block = block
75
+ end
76
+
77
+ #
78
+ # The default long option (ex: `--long-opt`) for the given option name
79
+ # (ex: `:long_opt`).
80
+ #
81
+ # @param [Symbol] name
82
+ #
83
+ # @return [String]
84
+ #
85
+ def self.default_long_opt(name)
86
+ "--#{Inflector.dasherize(name)}"
87
+ end
88
+
89
+ #
90
+ # Specifies if the option is of the form `--opt=VALUE`.
91
+ #
92
+ # @return [Boolean]
93
+ #
94
+ def equals?
95
+ @equals
96
+ end
97
+
98
+ #
99
+ # The separator characer between the option and option value.
100
+ #
101
+ # @return ['=', ' ', nil]
102
+ #
103
+ def separator
104
+ if @value
105
+ if equals? then '='
106
+ else ' '
107
+ end
108
+ end
109
+ end
110
+
111
+ #
112
+ # Usage strings for the option.
113
+ #
114
+ # @return [Array<String>]
115
+ # The usage strings.
116
+ #
117
+ def usage
118
+ [*@short, "#{@long}#{separator}#{@value && @value.usage}"]
119
+ end
120
+
121
+ #
122
+ # Determines if the option has a value.
123
+ #
124
+ # @return [Boolean]
125
+ #
126
+ def value?
127
+ !@value.nil?
128
+ end
129
+
130
+ #
131
+ # The option value's type.
132
+ #
133
+ # @return [Class, nil]
134
+ #
135
+ # @see OptionValue#type
136
+ #
137
+ def type
138
+ @value && @value.type
139
+ end
140
+
141
+ #
142
+ # The option value's default value.
143
+ #
144
+ # @return [Object, nil]
145
+ #
146
+ # @see OptionValue#default_value
147
+ #
148
+ def default_value
149
+ @value && @value.default_value
150
+ end
151
+
152
+ #
153
+ # The option description.
154
+ #
155
+ # @return [String]
156
+ #
157
+ # @note
158
+ # If {#default_value} returns a value, the description will contain the
159
+ # `Default:` value the option will be initialized with.
160
+ #
161
+ def desc
162
+ if (value = default_value)
163
+ "#{@desc} (Default: #{value})"
164
+ else
165
+ @desc
166
+ end
167
+ end
168
+
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'command_kit/arguments/argument_value'
4
+ require 'command_kit/inflector'
5
+
6
+ require 'optparse'
7
+ require 'date'
8
+ require 'time'
9
+ require 'uri'
10
+ require 'shellwords'
11
+
12
+ module CommandKit
13
+ module Options
14
+ #
15
+ # Represents an additional argument associated with an option flag.
16
+ #
17
+ class OptionValue < Arguments::ArgumentValue
18
+
19
+ USAGES = {
20
+ # NOTE: NilClass and Object are intentionally omitted
21
+ Date => 'DATE',
22
+ DateTime => 'DATE_TIME',
23
+ Time => 'TIME',
24
+ URI => 'URI',
25
+ Shellwords => 'STR',
26
+ String => 'STR',
27
+ Integer => 'INT',
28
+ Float => 'DEC',
29
+ Numeric => 'NUM',
30
+ OptionParser::DecimalInteger => 'INT',
31
+ OptionParser::OctalInteger => 'OCT',
32
+ OptionParser::DecimalNumeric => 'NUM|DEC',
33
+ TrueClass => 'BOOL',
34
+ FalseClass => 'BOOL',
35
+ Array => 'LIST[,...]',
36
+ Regexp => '/REGEXP/'
37
+ }
38
+
39
+ #
40
+ # Initializes the option value.
41
+ #
42
+ # @param [Class, Hash, Array, Regexp] type
43
+ #
44
+ # @param [String, nil] usage
45
+ #
46
+ # @param [Hash{Symbol => Object}] kwargs
47
+ #
48
+ def initialize(type: String,
49
+ usage: self.class.default_usage(type),
50
+ **kwargs)
51
+ super(type: type, usage: usage, **kwargs)
52
+ end
53
+
54
+ #
55
+ # Returns the default option value usage for the given type.
56
+ #
57
+ # @param [Class, Hash, Array, Regexp] type
58
+ #
59
+ # @return [String, nil]
60
+ #
61
+ # @raise [TypeError]
62
+ # The given type was not a Class, Hash, Array, or Regexp.
63
+ #
64
+ def self.default_usage(type)
65
+ USAGES.fetch(type) do
66
+ case type
67
+ when Class then Inflector.underscore(type.name).upcase
68
+ when Hash then type.keys.join('|')
69
+ when Array then type.join('|')
70
+ when Regexp then type.source
71
+ else
72
+ raise(TypeError,"unsupported option type: #{type.inspect}")
73
+ end
74
+ end
75
+ end
76
+
77
+ #
78
+ # The usage string for the argument.
79
+ #
80
+ # @return [String, nil]
81
+ #
82
+ def usage
83
+ string = @usage
84
+ string = "[#{string}]" if optional?
85
+ string
86
+ end
87
+
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'command_kit/main'
4
+ require 'command_kit/usage'
5
+ require 'command_kit/printing'
6
+
7
+ require 'optparse'
8
+
9
+ module CommandKit
10
+ module Options
11
+ #
12
+ # Adds an [OptionParser] to the command class and automatically parses options
13
+ # before calling `main`.
14
+ #
15
+ # [OptionParser]: https://rubydoc.info/stdlib/optparse/OptionParser
16
+ #
17
+ # include CommandKit::OptParser
18
+ #
19
+ # def initialize
20
+ # @opts.on('-c','--custom','Custom option') do
21
+ # @custom = true
22
+ # end
23
+ # end
24
+ #
25
+ # def main(*argv)
26
+ # if @custom
27
+ # puts "Custom mode enabled"
28
+ # end
29
+ # end
30
+ #
31
+ module Parser
32
+ include Usage
33
+ include Main
34
+ include Printing
35
+
36
+ module ModuleMethods
37
+ #
38
+ # Sets {CommandKit::Usage::ClassMethods#usage .usage} or extends
39
+ # {ModuleMethods}, depending on whether {Options::Parser} is being
40
+ # included into a class or a module.
41
+ #
42
+ # @param [Class, Module] context
43
+ # The class or module including {Parser}.
44
+ #
45
+ def included(context)
46
+ super
47
+
48
+ if context.class == Module
49
+ context.extend ModuleMethods
50
+ else
51
+ context.usage '[options]'
52
+ end
53
+ end
54
+ end
55
+
56
+ extend ModuleMethods
57
+
58
+ # The option parser.
59
+ #
60
+ # @return [OptionParser]
61
+ attr_reader :option_parser
62
+
63
+ #
64
+ # The option parser.
65
+ #
66
+ # @return [OptionParser]
67
+ #
68
+ def initialize(**kwargs)
69
+ super(**kwargs)
70
+
71
+ @option_parser = OptionParser.new do |opts|
72
+ opts.banner = "Usage: #{usage}"
73
+
74
+ opts.separator ''
75
+ opts.separator 'Options:'
76
+
77
+ opts.on_tail('-h','--help','Print help information') do
78
+ help
79
+ exit(0)
80
+ end
81
+ end
82
+ end
83
+
84
+ #
85
+ # Parses the options and passes any additional non-option arguments
86
+ # to the superclass'es `#main` method.
87
+ #
88
+ # @param [Array<String>] argv
89
+ # The given arguments Array.
90
+ #
91
+ # @return [Integer]
92
+ # The exit status code.
93
+ #
94
+ def main(argv=[])
95
+ super(parse_options(argv))
96
+ rescue SystemExit => system_exit
97
+ system_exit.status
98
+ end
99
+
100
+ #
101
+ # Parses the given options.
102
+ #
103
+ # @param [Array<String>] argv
104
+ # The given command-line arguments.
105
+ #
106
+ # @return [Array<String>]
107
+ # The remaining non-option arguments.
108
+ #
109
+ 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
127
+ end
128
+
129
+ #
130
+ # Prints an option parsing error.
131
+ #
132
+ # @param [OptionParser::ParseError] error
133
+ # The error from `OptionParser`.
134
+ #
135
+ def on_parse_error(error)
136
+ print_error("#{command_name}: #{error.message}")
137
+ print_error("Try '#{command_name} --help' for more information.")
138
+ exit(1)
139
+ end
140
+
141
+ #
142
+ # Place-holder method for handling `OptionParser::InvalidOption` exceptions.
143
+ #
144
+ # @param [OptionParser::InvalidOption] error
145
+ #
146
+ # @see on_parse_error
147
+ #
148
+ def on_invalid_option(error)
149
+ on_parse_error(error)
150
+ end
151
+
152
+ #
153
+ # Place-holder method for handling `OptionParser::AmbiguousOption`
154
+ # exceptions.
155
+ #
156
+ # @param [OptionParser::AmbiguousOption] error
157
+ #
158
+ # @see on_parse_error
159
+ #
160
+ def on_ambiguous_option(error)
161
+ on_parse_error(error)
162
+ end
163
+
164
+ #
165
+ # Place-holder method for handling `OptionParser::InvalidArgument`
166
+ # exceptions.
167
+ #
168
+ # @param [OptionParser::InvalidArgument] error
169
+ #
170
+ # @see on_parse_error
171
+ #
172
+ def on_invalid_argument(error)
173
+ on_parse_error(error)
174
+ end
175
+
176
+ #
177
+ # Place-holder method for handling `OptionParser::MissingArgument`
178
+ # exceptions.
179
+ #
180
+ # @param [OptionParser::MissingArgument] error
181
+ #
182
+ # @see on_parse_error
183
+ #
184
+ def on_missing_argument(error)
185
+ on_parse_error(error)
186
+ end
187
+
188
+ #
189
+ # Place-holder method for handling `OptionParser::NeedlessArgument`
190
+ # exceptions.
191
+ #
192
+ # @param [OptionParser::NeedlessArgument] error
193
+ #
194
+ # @see on_parse_error
195
+ #
196
+ def on_needless_argument(error)
197
+ on_parse_error(error)
198
+ end
199
+
200
+ #
201
+ # Place-holder method for handling `OptionParser::AmbiguousArgument`
202
+ # exceptions.
203
+ #
204
+ # @param [OptionParser::AmbiguousArgument] error
205
+ #
206
+ # @see on_parse_error
207
+ #
208
+ def on_ambiguous_argument(error)
209
+ on_parse_error(error)
210
+ end
211
+
212
+ #
213
+ # Prints the `--help` output.
214
+ #
215
+ def help_options
216
+ puts option_parser
217
+ end
218
+
219
+ #
220
+ # @see #help_options
221
+ #
222
+ def help
223
+ help_options
224
+ end
225
+ end
226
+ end
227
+ end