command_kit 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +18 -3
  3. data/.rubocop.yml +141 -0
  4. data/ChangeLog.md +165 -0
  5. data/Gemfile +3 -0
  6. data/README.md +186 -118
  7. data/Rakefile +3 -2
  8. data/command_kit.gemspec +4 -4
  9. data/examples/command.rb +1 -1
  10. data/gemspec.yml +7 -0
  11. data/lib/command_kit/arguments/argument.rb +2 -2
  12. data/lib/command_kit/arguments.rb +36 -7
  13. data/lib/command_kit/colors.rb +702 -53
  14. data/lib/command_kit/command.rb +2 -3
  15. data/lib/command_kit/commands/auto_load.rb +8 -1
  16. data/lib/command_kit/commands/help.rb +3 -2
  17. data/lib/command_kit/commands/subcommand.rb +1 -1
  18. data/lib/command_kit/commands.rb +24 -9
  19. data/lib/command_kit/env/path.rb +1 -1
  20. data/lib/command_kit/file_utils.rb +46 -0
  21. data/lib/command_kit/help/man.rb +17 -33
  22. data/lib/command_kit/inflector.rb +47 -17
  23. data/lib/command_kit/interactive.rb +9 -0
  24. data/lib/command_kit/main.rb +7 -9
  25. data/lib/command_kit/man.rb +44 -0
  26. data/lib/command_kit/open_app.rb +69 -0
  27. data/lib/command_kit/options/option.rb +41 -27
  28. data/lib/command_kit/options/option_value.rb +3 -2
  29. data/lib/command_kit/options/parser.rb +17 -22
  30. data/lib/command_kit/options.rb +102 -14
  31. data/lib/command_kit/os/linux.rb +157 -0
  32. data/lib/command_kit/os.rb +159 -11
  33. data/lib/command_kit/package_manager.rb +200 -0
  34. data/lib/command_kit/pager.rb +46 -4
  35. data/lib/command_kit/printing/indent.rb +4 -4
  36. data/lib/command_kit/printing.rb +14 -3
  37. data/lib/command_kit/program_name.rb +9 -0
  38. data/lib/command_kit/sudo.rb +40 -0
  39. data/lib/command_kit/terminal.rb +5 -0
  40. data/lib/command_kit/version.rb +1 -1
  41. data/spec/arguments/argument_spec.rb +1 -1
  42. data/spec/arguments_spec.rb +84 -1
  43. data/spec/colors_spec.rb +357 -70
  44. data/spec/command_spec.rb +77 -6
  45. data/spec/commands/auto_load_spec.rb +33 -2
  46. data/spec/commands_spec.rb +101 -29
  47. data/spec/env/path_spec.rb +6 -0
  48. data/spec/exception_handler_spec.rb +1 -1
  49. data/spec/file_utils_spec.rb +59 -0
  50. data/spec/fixtures/template.erb +5 -0
  51. data/spec/help/man_spec.rb +54 -57
  52. data/spec/inflector_spec.rb +70 -8
  53. data/spec/man_spec.rb +46 -0
  54. data/spec/open_app_spec.rb +85 -0
  55. data/spec/options/option_spec.rb +38 -2
  56. data/spec/options/option_value_spec.rb +55 -0
  57. data/spec/options/parser_spec.rb +0 -10
  58. data/spec/options_spec.rb +328 -0
  59. data/spec/os/linux_spec.rb +164 -0
  60. data/spec/os_spec.rb +200 -13
  61. data/spec/package_manager_spec.rb +806 -0
  62. data/spec/pager_spec.rb +71 -6
  63. data/spec/printing/indent_spec.rb +7 -5
  64. data/spec/printing_spec.rb +23 -1
  65. data/spec/program_name_spec.rb +8 -0
  66. data/spec/sudo_spec.rb +51 -0
  67. data/spec/terminal_spec.rb +30 -0
  68. data/spec/usage_spec.rb +1 -1
  69. metadata +23 -4
@@ -78,9 +78,6 @@ module CommandKit
78
78
  @option_parser = OptionParser.new do |opts|
79
79
  opts.banner = "Usage: #{usage}"
80
80
 
81
- opts.separator ''
82
- opts.separator 'Options:'
83
-
84
81
  opts.on_tail('-h','--help','Print help information') do
85
82
  help
86
83
  exit(0)
@@ -118,23 +115,21 @@ module CommandKit
118
115
  # @api semipublic
119
116
  #
120
117
  def parse_options(argv)
121
- begin
122
- option_parser.parse(argv)
123
- rescue OptionParser::InvalidOption => error
124
- on_invalid_option(error)
125
- rescue OptionParser::AmbiguousOption => error
126
- on_ambiguous_option(error)
127
- rescue OptionParser::InvalidArgument => error
128
- on_invalid_argument(error)
129
- rescue OptionParser::MissingArgument => error
130
- on_missing_argument(error)
131
- rescue OptionParser::NeedlessArgument => error
132
- on_needless_argument(error)
133
- rescue OptionParser::AmbiguousArgument => error
134
- on_ambiguous_argument(error)
135
- rescue OptionParser::ParseError => error
136
- on_parse_error(error)
137
- end
118
+ option_parser.parse(argv)
119
+ rescue OptionParser::InvalidOption => error
120
+ on_invalid_option(error)
121
+ rescue OptionParser::AmbiguousOption => error
122
+ on_ambiguous_option(error)
123
+ rescue OptionParser::InvalidArgument => error
124
+ on_invalid_argument(error)
125
+ rescue OptionParser::MissingArgument => error
126
+ on_missing_argument(error)
127
+ rescue OptionParser::NeedlessArgument => error
128
+ on_needless_argument(error)
129
+ rescue OptionParser::AmbiguousArgument => error
130
+ on_ambiguous_argument(error)
131
+ rescue OptionParser::ParseError => error
132
+ on_parse_error(error)
138
133
  end
139
134
 
140
135
  #
@@ -146,8 +141,8 @@ module CommandKit
146
141
  # @api semipublic
147
142
  #
148
143
  def on_parse_error(error)
149
- print_error("#{command_name}: #{error.message}")
150
- print_error("Try '#{command_name} --help' for more information.")
144
+ print_error(error.message)
145
+ stderr.puts("Try '#{command_name} --help' for more information.")
151
146
  exit(1)
152
147
  end
153
148
 
@@ -1,3 +1,4 @@
1
+ require 'command_kit/arguments'
1
2
  require 'command_kit/options/option'
2
3
  require 'command_kit/options/parser'
3
4
 
@@ -22,7 +23,26 @@ module CommandKit
22
23
  # @bar = arg.split(':')
23
24
  # end
24
25
  #
25
- # ### initialize and using ivars
26
+ # ### Multi-line Descriptions
27
+ #
28
+ # option :opt, value: {type: String},
29
+ # desc: [
30
+ # 'line1',
31
+ # 'line2',
32
+ # '...'
33
+ # ]
34
+ #
35
+ # ### Option Categories
36
+ #
37
+ # option :opt1, value: {type: String},
38
+ # category: 'Foo Category',
39
+ # desc: 'Option 1'
40
+ #
41
+ # option :opt2, value: {type: String},
42
+ # category: 'Foo Category',
43
+ # desc: 'Option 2'
44
+ #
45
+ # ### initialize and using instance variables
26
46
  #
27
47
  # option :number, value: {type: Integer},
28
48
  # desc: 'Numbers' do |num|
@@ -36,6 +56,7 @@ module CommandKit
36
56
  # end
37
57
  #
38
58
  module Options
59
+ include Arguments
39
60
  include Parser
40
61
 
41
62
  #
@@ -109,9 +130,12 @@ module CommandKit
109
130
  # @option value [String, nil] usage
110
131
  # The usage string for the option's value.
111
132
  #
112
- # @option kwargs [String] desc
133
+ # @option kwargs [String, Array<String>] desc
113
134
  # The description for the option.
114
135
  #
136
+ # @option kwargs [String, nil] category
137
+ # The optional category to group the option under.
138
+ #
115
139
  # @yield [(value)]
116
140
  # If a block is given, it will be passed the parsed option value.
117
141
  #
@@ -127,6 +151,19 @@ module CommandKit
127
151
  # @example Define an option:
128
152
  # option :foo, desc: "Foo option"
129
153
  #
154
+ # @example Define an option with a multi-line description:
155
+ # option :foo, desc: [
156
+ # "Line 1",
157
+ # "Line 2"
158
+ # ]
159
+ #
160
+ # @example Defines multiple options within a category:
161
+ # option :foo, desc: "Foo option",
162
+ # category: 'Other Options'
163
+ #
164
+ # option :bar, desc: "Bar option",
165
+ # category: 'Other Options'
166
+ #
130
167
  # @example With a custom short option:
131
168
  # option :foo, short: '-f',
132
169
  # desc: "Foo option"
@@ -197,7 +234,7 @@ module CommandKit
197
234
  # {Parser#option_parser option parser}.
198
235
  #
199
236
  # @param [Hash{Symbol => Object}] options
200
- # Optional pre-populated options hash.
237
+ # Optional prepopulated options hash.
201
238
  #
202
239
  # @note
203
240
  # The {#option_parser} will populate {#options} and also call any
@@ -210,17 +247,68 @@ module CommandKit
210
247
 
211
248
  super(**kwargs)
212
249
 
213
- self.class.options.each_value do |option|
214
- option_parser.on(*option.usage,option.type,option.desc) do |arg,*captures|
215
- @options[option.name] = if arg.nil?
216
- option.default_value
217
- else
218
- arg
219
- end
220
-
221
- if option.block
222
- instance_exec(*arg,*captures,&option.block)
223
- end
250
+ define_option_categories
251
+ end
252
+
253
+ #
254
+ # Overrides the default {Usage#help help} method and calls {#help_options}
255
+ # and {#help_arguments}.
256
+ #
257
+ # @api public
258
+ #
259
+ def help
260
+ help_options
261
+ help_arguments
262
+ end
263
+
264
+ private
265
+
266
+ #
267
+ # Defines all of the options, grouped by category.
268
+ #
269
+ def define_option_categories
270
+ categories = self.class.options.values.group_by(&:category)
271
+
272
+ categories.each do |category,options|
273
+ if category
274
+ define_options_category(category,options)
275
+ end
276
+ end
277
+
278
+ define_options_category('Options',categories.fetch(nil,[]))
279
+ end
280
+
281
+ #
282
+ # Defines a new category of options with a header.
283
+ #
284
+ # @param [String] category
285
+ # The category name.
286
+ #
287
+ # @param [Array<Option>, nil] options
288
+ # The options to define.
289
+ #
290
+ def define_options_category(category,options)
291
+ option_parser.separator ''
292
+ option_parser.separator "#{category}:"
293
+
294
+ options.each(&method(:define_option))
295
+ end
296
+
297
+ #
298
+ # Defines an individual option.
299
+ #
300
+ # @param [Option] option
301
+ #
302
+ def define_option(option)
303
+ default_value = option.default_value
304
+
305
+ @options[option.name] = default_value unless default_value.nil?
306
+
307
+ option_parser.on(*option.usage,option.type,option.desc) do |arg,*captures|
308
+ @options[option.name] = arg
309
+
310
+ if option.block
311
+ instance_exec(*arg,*captures,&option.block)
224
312
  end
225
313
  end
226
314
  end
@@ -0,0 +1,157 @@
1
+ require 'command_kit/os'
2
+
3
+ module CommandKit
4
+ module OS
5
+ #
6
+ # Provides methods for determining the specific type of Linux.
7
+ #
8
+ # ## Example
9
+ #
10
+ # require 'command_kit/command'
11
+ # require 'command_kit/os/linux'
12
+ #
13
+ # class Command < CommandKit::Command
14
+ #
15
+ # include CommandKit::OS::Linux
16
+ #
17
+ # def run
18
+ # if debian_linux?
19
+ # # ...
20
+ # elsif redhat_linux?
21
+ # # ...
22
+ # elsif suse_linux?
23
+ # # ...
24
+ # elsif arch_linux?
25
+ # # ...
26
+ # end
27
+ # end
28
+ # end
29
+ #
30
+ # @since 0.2.0
31
+ #
32
+ module Linux
33
+ #
34
+ # @api private
35
+ #
36
+ module ModuleMethods
37
+ #
38
+ # Extends {ClassMethods} or {ModuleMethods}, depending on whether
39
+ # {OS} is being included into a class or a module..
40
+ #
41
+ # @param [Class, Module] context
42
+ # The class or module which is including {OS}.
43
+ #
44
+ def included(context)
45
+ super(context)
46
+
47
+ if context.class == Module
48
+ context.extend ModuleMethods
49
+ else
50
+ context.extend ClassMethods
51
+ end
52
+ end
53
+ end
54
+
55
+ extend ModuleMethods
56
+
57
+ module ClassMethods
58
+ #
59
+ # Determines the specific Linux distro.
60
+ #
61
+ # @return [:fedora, :redhat, :debian, :suse, :arch, nil]
62
+ # Returns the type of Linux distro or `nil` if the Linux distro could
63
+ # not be determined.
64
+ #
65
+ # @api semipublic
66
+ #
67
+ def linux_distro
68
+ if File.file?('/etc/fedora-release') then :fedora
69
+ elsif File.file?('/etc/redhat-release') then :redhat
70
+ elsif File.file?('/etc/debian_version') then :debian
71
+ elsif File.file?('/etc/SuSE-release') then :suse
72
+ elsif File.file?('/etc/arch-release') then :arch
73
+ end
74
+ end
75
+ end
76
+
77
+ # The Linux distro.
78
+ #
79
+ # @return [:fedora, :redhat, :debian, :suse, :arch, nil]
80
+ #
81
+ # @api public
82
+ attr_reader :linux_distro
83
+
84
+ #
85
+ # Initializes the command.
86
+ #
87
+ # @param [:fedora, :redhat, :debian, :suse, :arch, nil] linux_distro
88
+ # Overrides the default detected Linux distro.
89
+ #
90
+ # @param [Hash{Symbol => Object}] kwargs
91
+ # Additional keyword arguments.
92
+ #
93
+ # @api public
94
+ #
95
+ def initialize(linux_distro: self.class.linux_distro, **kwargs)
96
+ super(**kwargs)
97
+
98
+ @linux_distro = linux_distro
99
+ end
100
+
101
+ #
102
+ # Determines if the current OS is RedHat Linux based distro.
103
+ #
104
+ # @return [Boolean]
105
+ #
106
+ # @api public
107
+ #
108
+ def redhat_linux?
109
+ @linux_distro == :redhat
110
+ end
111
+
112
+ #
113
+ # Determines if the current OS is Fedora Linux based distro.
114
+ #
115
+ # @return [Boolean]
116
+ #
117
+ # @api public
118
+ #
119
+ def fedora_linux?
120
+ @linux_distro == :fedora
121
+ end
122
+
123
+ #
124
+ # Determines if the current OS is Debian Linux based distro.
125
+ #
126
+ # @return [Boolean]
127
+ #
128
+ # @api public
129
+ #
130
+ def debian_linux?
131
+ @linux_distro == :debian
132
+ end
133
+
134
+ #
135
+ # Determines if the current OS is SUSE Linux based distro.
136
+ #
137
+ # @return [Boolean]
138
+ #
139
+ # @api public
140
+ #
141
+ def suse_linux?
142
+ @linux_distro == :suse
143
+ end
144
+
145
+ #
146
+ # Determines if the current OS is Arch Linux based distro.
147
+ #
148
+ # @return [Boolean]
149
+ #
150
+ # @api public
151
+ #
152
+ def arch_linux?
153
+ @linux_distro == :arch
154
+ end
155
+ end
156
+ end
157
+ end
@@ -4,19 +4,102 @@ module CommandKit
4
4
  #
5
5
  # ## Examples
6
6
  #
7
- # include CommandKit::OS
7
+ # require 'command_kit/command'
8
+ # require 'command_kit/os'
8
9
  #
9
- # def main(*argv)
10
- # if linux?
11
- # # ...
12
- # elsif macos?
13
- # # ...
14
- # elsif windows?
15
- # # ...
10
+ # class Command < CommandKit::Command
11
+ #
12
+ # include CommandKit::OS
13
+ #
14
+ # def main(*argv)
15
+ # if linux?
16
+ # # ...
17
+ # elsif macos?
18
+ # # ...
19
+ # elsif freebsd?
20
+ # # ...
21
+ # elsif windows?
22
+ # # ...
23
+ # end
16
24
  # end
25
+ #
17
26
  # end
18
27
  #
19
28
  module OS
29
+ #
30
+ # @api private
31
+ #
32
+ module ModuleMethods
33
+ #
34
+ # Extends {ClassMethods} or {ModuleMethods}, depending on whether
35
+ # {OS} is being included into a class or a module..
36
+ #
37
+ # @param [Class, Module] context
38
+ # The class or module which is including {OS}.
39
+ #
40
+ def included(context)
41
+ super(context)
42
+
43
+ if context.class == Module
44
+ context.extend ModuleMethods
45
+ else
46
+ context.extend ClassMethods
47
+ end
48
+ end
49
+ end
50
+
51
+ extend ModuleMethods
52
+
53
+ module ClassMethods
54
+ #
55
+ # Determines the current OS.
56
+ #
57
+ # @return [:linux, :macos, :freebsd, :openbsd, :netbsd, :windows, nil]
58
+ # The OS type or `nil` if the OS could not be determined.
59
+ #
60
+ # @api semipublic
61
+ #
62
+ # @since 0.2.0
63
+ #
64
+ def os
65
+ if RUBY_PLATFORM.include?('linux') then :linux
66
+ elsif RUBY_PLATFORM.include?('darwin') then :macos
67
+ elsif RUBY_PLATFORM.include?('freebsd') then :freebsd
68
+ elsif RUBY_PLATFORM.include?('openbsd') then :openbsd
69
+ elsif RUBY_PLATFORM.include?('netbsd') then :netbsd
70
+ elsif Gem.win_platform? then :windows
71
+ end
72
+ end
73
+ end
74
+
75
+ # The current OS.
76
+ #
77
+ # @return [:linux, :macos, :freebsd, :openbsd, :netbsd, :windows, nil]
78
+ #
79
+ # @api public
80
+ #
81
+ # @since 0.2.0
82
+ attr_reader :os
83
+
84
+ #
85
+ # Initializes the command.
86
+ #
87
+ # @param [:linux, :macos, :freebsd, :openbsd, :netbsd, :windows, nil] os
88
+ # Overrides the default OS.
89
+ #
90
+ # @param [Hash{Symbol => Object}] kwargs
91
+ # Additional keyword arguments.
92
+ #
93
+ # @api public
94
+ #
95
+ # @since 0.2.0
96
+ #
97
+ def initialize(os: self.class.os, **kwargs)
98
+ super(**kwargs)
99
+
100
+ @os = os
101
+ end
102
+
20
103
  #
21
104
  # Determines if the current OS is Linux.
22
105
  #
@@ -25,7 +108,7 @@ module CommandKit
25
108
  # @api public
26
109
  #
27
110
  def linux?
28
- RUBY_PLATFORM.include?('linux')
111
+ @os == :linux
29
112
  end
30
113
 
31
114
  #
@@ -36,7 +119,72 @@ module CommandKit
36
119
  # @api public
37
120
  #
38
121
  def macos?
39
- RUBY_PLATFORM.include?('darwin')
122
+ @os == :macos
123
+ end
124
+
125
+ #
126
+ # Determines if the current OS is FreeBSD.
127
+ #
128
+ # @return [Boolean]
129
+ #
130
+ # @api public
131
+ #
132
+ # @since 0.2.0
133
+ #
134
+ def freebsd?
135
+ @os == :freebsd
136
+ end
137
+
138
+ #
139
+ # Determines if the current OS is OpenBSD.
140
+ #
141
+ # @return [Boolean]
142
+ #
143
+ # @api public
144
+ #
145
+ # @since 0.2.0
146
+ #
147
+ def openbsd?
148
+ @os == :openbsd
149
+ end
150
+
151
+ #
152
+ # Determines if the current OS is NetBSD.
153
+ #
154
+ # @return [Boolean]
155
+ #
156
+ # @api public
157
+ #
158
+ # @since 0.2.0
159
+ #
160
+ def netbsd?
161
+ @os == :netbsd
162
+ end
163
+
164
+ #
165
+ # Determines if the current OS is BSD based.
166
+ #
167
+ # @return [Boolean]
168
+ #
169
+ # @since 0.2.0
170
+ #
171
+ # @api public
172
+ #
173
+ def bsd?
174
+ freebsd? || openbsd? || netbsd?
175
+ end
176
+
177
+ #
178
+ # Determines if the current OS is UNIX based.
179
+ #
180
+ # @return [Boolean]
181
+ #
182
+ # @since 0.2.0
183
+ #
184
+ # @api public
185
+ #
186
+ def unix?
187
+ linux? || macos? || bsd?
40
188
  end
41
189
 
42
190
  #
@@ -47,7 +195,7 @@ module CommandKit
47
195
  # @api public
48
196
  #
49
197
  def windows?
50
- Gem.win_platform?
198
+ @os == :windows
51
199
  end
52
200
  end
53
201
  end