command_kit 0.1.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
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