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
data/gemspec.yml ADDED
@@ -0,0 +1,14 @@
1
+ name: command_kit
2
+ summary: A toolkit for building Ruby CLI commands
3
+ description:
4
+ A Ruby toolkit for building clean, correct, and robust CLI commands as
5
+ Ruby classes.
6
+ license: MIT
7
+ authors: Postmodern
8
+ email: postmodern.mod3@gmail.com
9
+ homepage: https://github.com/postmodern/command_kit#readme
10
+
11
+ required_ruby_version: ">= 2.7.0"
12
+
13
+ development_dependencies:
14
+ bundler: ~> 2.0
@@ -0,0 +1 @@
1
+ require 'command_kit/version'
@@ -0,0 +1,161 @@
1
+ require 'command_kit/help'
2
+ require 'command_kit/arguments/argument'
3
+
4
+ module CommandKit
5
+ #
6
+ # Provides a thin DSL for defining arguments as attributes.
7
+ #
8
+ # ## Examples
9
+ #
10
+ # include CommandKit::Arguments
11
+ #
12
+ # argument :output, type: String,
13
+ # desc: 'The output file'
14
+ #
15
+ # argument :input, type: Array,
16
+ # desc: 'The input file(s)'
17
+ #
18
+ module Arguments
19
+ include Help
20
+
21
+ module ModuleMethods
22
+ #
23
+ # Extends {ClassMethods} or {ModuleMethods}, depending on whether
24
+ # {Arguments} is being included into a class or module.
25
+ #
26
+ # @param [Class, Module] context
27
+ # The class or module which is including {Arguments}.
28
+ #
29
+ def included(context)
30
+ super(context)
31
+
32
+ if context.class == Module
33
+ context.extend ModuleMethods
34
+ else
35
+ context.extend ClassMethods
36
+ end
37
+ end
38
+ end
39
+
40
+ extend ModuleMethods
41
+
42
+ #
43
+ # Defines class-level methods.
44
+ #
45
+ module ClassMethods
46
+ #
47
+ # All defined arguments for the class.
48
+ #
49
+ # @return [Hash{Symbol => Argument}]
50
+ # The defined argument for the class and it's superclass.
51
+ #
52
+ def arguments
53
+ @arguments ||= if superclass.kind_of?(ClassMethods)
54
+ superclass.arguments.dup
55
+ else
56
+ {}
57
+ end
58
+ end
59
+
60
+ #
61
+ # Defines an argument for the class.
62
+ #
63
+ # @param [Symbol] name
64
+ # The argument name.
65
+ #
66
+ # @yield [(arg)]
67
+ # If a block is given, it will be passed the parsed argument.
68
+ #
69
+ # @yieldparam [Object, nil] arg
70
+ # The parsed argument.
71
+ #
72
+ # @return [Argument]
73
+ # The newly defined argument.
74
+ #
75
+ # @example Define an argument:
76
+ # argument :bar, desc: "Bar argument"
77
+ #
78
+ # @example With a custom usage string:
79
+ # option :bar, usage: 'BAR',
80
+ # desc: "Bar argument"
81
+ #
82
+ # @example With a custom block:
83
+ # argument :bar, desc: "Bar argument" do |bar|
84
+ # # ...
85
+ # end
86
+ #
87
+ # @example With a custom type:
88
+ # argument :bar, type: Integer,
89
+ # desc: "Bar argument"
90
+ #
91
+ # @example With a default value:
92
+ # argument :bar, default: "bar.txt",
93
+ # desc: "Bar argument"
94
+ #
95
+ # @example An optional argument:
96
+ # argument :bar, required: true,
97
+ # desc: "Bar argument"
98
+ #
99
+ # @example A repeating argument:
100
+ # argument :bar, repeats: true,
101
+ # desc: "Bar argument"
102
+ #
103
+ def argument(name,**kwargs,&block)
104
+ arguments[name] = Argument.new(name,**kwargs,&block)
105
+ end
106
+ end
107
+
108
+ #
109
+ # Checks the minimum/maximum number of arguments, then calls the
110
+ # superclass'es `#main`.
111
+ #
112
+ # @param [Array<String>] argv
113
+ # The arguments passed to the program.
114
+ #
115
+ # @return [Integer]
116
+ # The exit status code. If too few or too many arguments are given, then
117
+ # an error message is printed and `1` is returned.
118
+ #
119
+ def main(argv=[])
120
+ required_args = self.class.arguments.each_value.count(&:required?)
121
+ optional_args = self.class.arguments.each_value.count(&:optional?)
122
+ has_repeats_arg = self.class.arguments.each_value.any?(&:repeats?)
123
+
124
+ if argv.length < required_args
125
+ print_error("insufficient number of arguments.")
126
+ help_usage
127
+ return 1
128
+ elsif argv.length > (required_args + optional_args) && !has_repeats_arg
129
+ print_error("too many arguments given")
130
+ help_usage
131
+ return 1
132
+ end
133
+
134
+ super(argv)
135
+ end
136
+
137
+ #
138
+ # Prints any defined arguments, along with the usual `--help` information.
139
+ #
140
+ def help_arguments
141
+ unless (arguments = self.class.arguments).empty?
142
+ puts
143
+ puts 'Arguments:'
144
+
145
+ self.class.arguments.each_value do |arg|
146
+ puts " #{arg.usage.ljust(33)}#{arg.desc}"
147
+ end
148
+ end
149
+ end
150
+
151
+ #
152
+ # Calls the superclass'es `#help` method, if it's defined, then calls
153
+ # {#help_arguments}.
154
+ #
155
+ def help
156
+ super if defined?(super)
157
+
158
+ help_arguments
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,111 @@
1
+ require 'command_kit/arguments/argument_value'
2
+
3
+ module CommandKit
4
+ module Arguments
5
+ #
6
+ # Represents a defined argument.
7
+ #
8
+ class Argument < ArgumentValue
9
+
10
+ # @return [Symbol]
11
+ attr_reader :name
12
+
13
+ # @return [Boolean]
14
+ attr_reader :repeats
15
+
16
+ # @return [String, nil]
17
+ attr_reader :desc
18
+
19
+ # @return [Regexp, nil]
20
+ attr_reader :pattern
21
+
22
+ # @return [Proc, nil]
23
+ attr_reader :parser
24
+
25
+ # @return [Proc, nil]
26
+ attr_reader :block
27
+
28
+ #
29
+ # Initializes the argument.
30
+ #
31
+ # @param [Symbol] name
32
+ #
33
+ # @param [Class, Hash, Array, Regexp] type
34
+ #
35
+ # @param [String, nil] usage
36
+ #
37
+ # @param [Object, Proc, nil] default
38
+ #
39
+ # @param [Boolean] required
40
+ #
41
+ # @param [Boolean] repeats
42
+ #
43
+ # @param [String] desc
44
+ #
45
+ # @note `usage` will be assigned a default value based on `type` and
46
+ # `name`.
47
+ #
48
+ # @yield [(value)]
49
+ #
50
+ # @yieldparam [Object, nil] value
51
+ #
52
+ def initialize(name, type: String,
53
+ usage: name.to_s.upcase,
54
+ default: nil,
55
+ required: true,
56
+ repeats: false,
57
+ desc: ,
58
+ &block)
59
+ super(
60
+ type: type,
61
+ usage: usage,
62
+ default: default,
63
+ required: required
64
+ )
65
+
66
+ @name = name
67
+ @repeats = repeats
68
+ @desc = desc
69
+
70
+ @pattern, @parser = self.class.parser(@type)
71
+
72
+ @block = block
73
+ end
74
+
75
+ #
76
+ # Looks up the option parser for the given `OptionParser` type.
77
+ #
78
+ # @param [Class] type
79
+ # The `OptionParser` type class.
80
+ #
81
+ # @return [(Regexp, Proc), nil]
82
+ # The matching pattern and converter proc.
83
+ #
84
+ def self.parser(type)
85
+ OptionParser::DefaultList.search(:atype,type)
86
+ end
87
+
88
+ #
89
+ # Specifies whether the argument can be repeated repeat times.
90
+ #
91
+ # @return [Boolean]
92
+ #
93
+ def repeats?
94
+ @repeats
95
+ end
96
+
97
+ #
98
+ # The usage string for the argument.
99
+ #
100
+ # @return [String]
101
+ #
102
+ def usage
103
+ string = @usage
104
+ string = "#{string} ..." if repeats?
105
+ string = "[#{string}]" if optional?
106
+ string
107
+ end
108
+
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,81 @@
1
+ module CommandKit
2
+ module Arguments
3
+ #
4
+ # Represents an individual argument value.
5
+ #
6
+ class ArgumentValue
7
+
8
+ # The desired type of the argument value.
9
+ #
10
+ # @return [Class, Hash, Array, Regexp, nil]
11
+ attr_reader :type
12
+
13
+ # The default parsed value for the argument value.
14
+ #
15
+ # @return [Object, Proc, nil]
16
+ attr_reader :default
17
+
18
+ # Specifies whether the argument value is required or optional.
19
+ #
20
+ # @return [Boolean]
21
+ attr_reader :required
22
+
23
+ # The usage string to describe the argument value.
24
+ #
25
+ # @return [String]
26
+ attr_reader :usage
27
+
28
+ #
29
+ # Initializes the argument value.
30
+ #
31
+ # @param [Class, Hash, Array, Regexp] type
32
+ # The type of the argument value.
33
+ #
34
+ # @param [Boolean] required
35
+ # Specifies whether the argument value is required or optional.
36
+ #
37
+ # @param [String] usage
38
+ # The usage string to represent the argument value.
39
+ #
40
+ # @param [Object, Proc, nil] default
41
+ # The default parsed value for the argument value.
42
+ #
43
+ def initialize(type: nil, required: true, default: nil, usage: )
44
+ @type = type
45
+ @required = required
46
+ @default = default
47
+ @usage = usage
48
+ end
49
+
50
+ #
51
+ # Determines if the argument is required or not.
52
+ #
53
+ # @return [Boolean]
54
+ #
55
+ def required?
56
+ @required
57
+ end
58
+
59
+ #
60
+ # Determines whether the argument can be omitted.
61
+ #
62
+ # @return [Boolean]
63
+ #
64
+ def optional?
65
+ !@required
66
+ end
67
+
68
+ #
69
+ # Returns a new default value.
70
+ #
71
+ # @return [Object]
72
+ #
73
+ def default_value
74
+ if @default.respond_to?(:call) then @default.call
75
+ else @default.dup
76
+ end
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,6 @@
1
+ module CommandKit
2
+ module Arguments
3
+ module Usage
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,355 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'command_kit/stdio'
4
+ require 'command_kit/env'
5
+
6
+ module CommandKit
7
+ #
8
+ # Defines ANSI color codes.
9
+ #
10
+ # ## Examples
11
+ #
12
+ # include CommandKit::Colors
13
+ #
14
+ # def run
15
+ # puts colors.green("hello world")
16
+ # end
17
+ #
18
+ # ### Printing color error messages
19
+ #
20
+ # stderr.puts colors(stderr).red("error!")
21
+ #
22
+ # ## Environment Variables
23
+ #
24
+ # * `TERM` - Specifies the type of terminal. When set to `DUMB`, it will
25
+ # disable color output.
26
+ #
27
+ # ## Alternatives
28
+ #
29
+ # * [ansi](http://rubyworks.github.io/ansi/)
30
+ # * [colorize](https://github.com/fazibear/colorize#readme)
31
+ #
32
+ # @see https://en.wikipedia.org/wiki/ANSI_escape_code
33
+ #
34
+ module Colors
35
+
36
+ include Stdio
37
+ include Env
38
+
39
+ #
40
+ # Applies ANSI formatting to text.
41
+ #
42
+ module ANSI
43
+ # ANSI reset code
44
+ RESET = "\e[0m"
45
+
46
+ # @see RESET
47
+ CLEAR = RESET
48
+
49
+ # ANSI code for bold text
50
+ BOLD = "\e[1m"
51
+
52
+ # ANSI code to disable boldness
53
+ RESET_INTENSITY = "\e[22m"
54
+
55
+ # ANSI color code for black
56
+ BLACK = "\e[30m"
57
+
58
+ # ANSI color code for red
59
+ RED = "\e[31m"
60
+
61
+ # ANSI color code for green
62
+ GREEN = "\e[32m"
63
+
64
+ # ANSI color code for yellow
65
+ YELLOW = "\e[33m"
66
+
67
+ # ANSI color code for blue
68
+ BLUE = "\e[34m"
69
+
70
+ # ANSI color code for megenta
71
+ MAGENTA = "\e[35m"
72
+
73
+ # ANSI color code for cyan
74
+ CYAN = "\e[36m"
75
+
76
+ # ANSI color code for white
77
+ WHITE = "\e[37m"
78
+
79
+ # ANSI color for the default foreground color
80
+ RESET_COLOR = "\e[39m"
81
+
82
+ module_function
83
+
84
+ #
85
+ # Resets text formatting.
86
+ #
87
+ # @return [RESET]
88
+ #
89
+ # @see RESET
90
+ #
91
+ def reset
92
+ RESET
93
+ end
94
+
95
+ #
96
+ # @see reset
97
+ #
98
+ def clear
99
+ reset
100
+ end
101
+
102
+ #
103
+ # Bolds the text.
104
+ #
105
+ # @param [String, nil] string
106
+ # An optional string.
107
+ #
108
+ # @return [String, BOLD]
109
+ # The bolded string or just {BOLD} if no arguments were given.
110
+ #
111
+ # @see BOLD
112
+ #
113
+ def bold(string=nil)
114
+ if string then "#{BOLD}#{string}#{RESET_INTENSITY}"
115
+ else BOLD
116
+ end
117
+ end
118
+
119
+ #
120
+ # Sets the text color to black.
121
+ #
122
+ # @param [String, nil] string
123
+ # An optional string.
124
+ #
125
+ # @return [String, BLACK]
126
+ # The colorized string or just {BLACK} if no arguments were given.
127
+ #
128
+ # @see BLACK
129
+ #
130
+ def black(string=nil)
131
+ if string then "#{BLACK}#{string}#{RESET_COLOR}"
132
+ else BLACK
133
+ end
134
+ end
135
+
136
+ #
137
+ # Sets the text color to red.
138
+ #
139
+ # @param [String, nil] string
140
+ # An optional string.
141
+ #
142
+ # @return [String, RED]
143
+ # The colorized string or just {RED} if no arguments were given.
144
+ #
145
+ # @see RED
146
+ #
147
+ def red(string=nil)
148
+ if string then "#{RED}#{string}#{RESET_COLOR}"
149
+ else RED
150
+ end
151
+ end
152
+
153
+ #
154
+ # Sets the text color to green.
155
+ #
156
+ # @param [String, nil] string
157
+ # An optional string.
158
+ #
159
+ # @return [String, GREEN]
160
+ # The colorized string or just {GREEN} if no arguments were given.
161
+ #
162
+ # @see GREEN
163
+ #
164
+ def green(string=nil)
165
+ if string then "#{GREEN}#{string}#{RESET_COLOR}"
166
+ else GREEN
167
+ end
168
+ end
169
+
170
+ #
171
+ # Sets the text color to yellow.
172
+ #
173
+ # @param [String, nil] string
174
+ # An optional string.
175
+ #
176
+ # @return [String, YELLOW]
177
+ # The colorized string or just {YELLOW} if no arguments were given.
178
+ #
179
+ # @see YELLOW
180
+ #
181
+ def yellow(string=nil)
182
+ if string then "#{YELLOW}#{string}#{RESET_COLOR}"
183
+ else YELLOW
184
+ end
185
+ end
186
+
187
+ #
188
+ # Sets the text color to blue.
189
+ #
190
+ # @param [String, nil] string
191
+ # An optional string.
192
+ #
193
+ # @return [String, BLUE]
194
+ # The colorized string or just {BLUE} if no arguments were given.
195
+ #
196
+ # @see BLUE
197
+ #
198
+ def blue(string=nil)
199
+ if string then "#{BLUE}#{string}#{RESET_COLOR}"
200
+ else BLUE
201
+ end
202
+ end
203
+
204
+ #
205
+ # Sets the text color to magenta.
206
+ #
207
+ # @param [String, nil] string
208
+ # An optional string.
209
+ #
210
+ # @return [String, MAGENTA]
211
+ # The colorized string or just {MAGENTA} if no arguments were given.
212
+ #
213
+ # @see MAGENTA
214
+ #
215
+ def magenta(string=nil)
216
+ if string then "#{MAGENTA}#{string}#{RESET_COLOR}"
217
+ else MAGENTA
218
+ end
219
+ end
220
+
221
+ #
222
+ # Sets the text color to cyan.
223
+ #
224
+ # @param [String, nil] string
225
+ # An optional string.
226
+ #
227
+ # @return [String, CYAN]
228
+ # The colorized string or just {CYAN} if no arguments were given.
229
+ #
230
+ # @see CYAN
231
+ #
232
+ def cyan(string=nil)
233
+ if string then "#{CYAN}#{string}#{RESET_COLOR}"
234
+ else CYAN
235
+ end
236
+ end
237
+
238
+ #
239
+ # Sets the text color to white.
240
+ #
241
+ # @param [String, nil] string
242
+ # An optional string.
243
+ #
244
+ # @return [String, WHITE]
245
+ # The colorized string or just {WHITE} if no arguments were given.
246
+ #
247
+ # @see WHITE
248
+ #
249
+ def white(string=nil)
250
+ if string then "#{WHITE}#{string}#{RESET_COLOR}"
251
+ else WHITE
252
+ end
253
+ end
254
+ end
255
+
256
+ #
257
+ # Dummy module with the same interface as {ANSI}, but for when ANSI is not
258
+ # supported.
259
+ #
260
+ module PlainText
261
+ RESET = \
262
+ CLEAR = \
263
+ BOLD = \
264
+ RESET_INTENSITY = \
265
+ BLACK = \
266
+ RED = \
267
+ GREEN = \
268
+ YELLOW = \
269
+ BLUE = \
270
+ MAGENTA = \
271
+ CYAN = \
272
+ WHITE = \
273
+ RESET_COLOR = ''
274
+
275
+ module_function
276
+
277
+ def reset
278
+ RESET
279
+ end
280
+
281
+ def clear
282
+ reset
283
+ end
284
+
285
+ def bold(string=nil)
286
+ string || ''
287
+ end
288
+
289
+ def black(string=nil)
290
+ string || ''
291
+ end
292
+
293
+ def red(string=nil)
294
+ string || ''
295
+ end
296
+
297
+ def green(string=nil)
298
+ string || ''
299
+ end
300
+
301
+ def yellow(string=nil)
302
+ string || ''
303
+ end
304
+
305
+ def blue(string=nil)
306
+ string || ''
307
+ end
308
+
309
+ def magenta(string=nil)
310
+ string || ''
311
+ end
312
+
313
+ def cyan(string=nil)
314
+ string || ''
315
+ end
316
+
317
+ def white(string=nil)
318
+ string || ''
319
+ end
320
+ end
321
+
322
+ #
323
+ # Checks if the stream supports ANSI output.
324
+ #
325
+ # @param [IO] stream
326
+ #
327
+ # @return [Boolean]
328
+ #
329
+ # @note
330
+ # When the env variable `TERM` is set to `dumb`, it will disable color
331
+ # output. Color output will also be disabled if the given stream is not
332
+ # a TTY.
333
+ #
334
+ def ansi?(stream=stdout)
335
+ env['TERM'] != 'dumb' && stream.tty?
336
+ end
337
+
338
+ #
339
+ # Returns the colors available for the given stream.
340
+ #
341
+ # @param [IO] stream
342
+ #
343
+ # @return [ANSI, PlainText]
344
+ # The ANSI module or PlainText dummy module.
345
+ #
346
+ def colors(stream=stdout)
347
+ color = if ansi?(stream) then ANSI
348
+ else PlainText
349
+ end
350
+
351
+ yield color if block_given?
352
+ return color
353
+ end
354
+ end
355
+ end