Class: NeverBounce::CLI::Script::Meaningful Abstract
- Defined in:
- lib/never_bounce/cli/script/meaningful.rb
Overview
A meaningful base script class. Features:
-
Handle command-line options.
-
Handle envars.
-
Handle boilerplate actions like printing usage on ‘–help`.
Direct Known Subclasses
Instance Attribute Summary collapse
- #banner_text ⇒ String
- #envar_text ⇒ String
- #help_text ⇒ Object
-
#manifest ⇒ Manicest
abstract
Program manifest object.
- #options_text ⇒ Object
Attributes inherited from Base
Class Method Summary collapse
-
.error_klasses ⇒ Array
Exception classes which are rescued fromc in #main and printed to user.
-
.format_envar_examples(envar) ⇒ String
Format an envar examples string.
Instance Method Summary collapse
-
#call_slim_main(from_level = 3) ⇒ Integer
private
Invoke one of the slim_main methods available in self.
-
#handle_help_and_options ⇒ Integer?
Handle help request and invalid options.
-
#help? ⇒ Boolean
true
if help has been requested via command-line options. -
#main ⇒ Integer
Main routine which handles most boilerplate.
-
#option_parser ⇒ Object
Our
OptionParser
object. -
#options ⇒ Hash
Parse command-line options with
OptionParser
. -
#slim_main ⇒ Integer
abstract
Slim or “real” main routine of the successor class.
Methods inherited from Base
#env_falsey?, #env_truthy?, env_value_truthy?, #system, #verbose?
Instance Attribute Details
#banner_text ⇒ String
27 28 29 |
# File 'lib/never_bounce/cli/script/meaningful.rb', line 27 def @banner_text ||= "#{manifest.name} - #{manifest.function}" end |
#envar_text ⇒ String
32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/never_bounce/cli/script/meaningful.rb', line 32 def envar_text @envar_text ||= begin max_width = (envars = self.class.envars).map(&:name).map(&:size).max envars.sort_by { |_| [_.mandatory?? 0 : 1, _.name] }.map do |r| "%s %-#{max_width}s - %s%s" % [ (r.mandatory?? "*" : "-"), r.name, r.comment, (s = self.class.format_envar_examples(r)) ? " (#{s})" : "", ] end.join("\n") end end |
#help_text ⇒ Object
58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/never_bounce/cli/script/meaningful.rb', line 58 def help_text @help_text ||= begin [ , "", "USAGE: #{manifest.name} #{manifest.cmdline}", "", , "", "Environment variables:", envar_text, ].join("\n") end end |
#manifest ⇒ Manicest
Program manifest object. Successors should return it.
191 192 193 |
# File 'lib/never_bounce/cli/script/meaningful.rb', line 191 def manifest raise NotImplementedError, "Redefine `manifest` in your class: #{self.class}" end |
#options_text ⇒ Object
123 124 125 |
# File 'lib/never_bounce/cli/script/meaningful.rb', line 123 def @options_text ||= option_parser.help.strip end |
Class Method Details
.error_klasses ⇒ Array
Should be a few very high-level excaptions which you fully control.
Exception classes which are rescued fromc in #main and printed to user.
49 50 51 |
# File 'lib/never_bounce/cli/script/meaningful.rb', line 49 def self.error_klasses [Error] end |
.format_envar_examples(envar) ⇒ String
Format an envar examples string.
format_examples_string(envar) # => ""a", *"B""
153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/never_bounce/cli/script/meaningful.rb', line 153 def self.format_envar_examples(envar) return nil if envar.examples.empty? envar.examples.map do |v| if envar.default and v == envar.default "[" + v.inspect + "]" else v.inspect end end.join(", ") end |
Instance Method Details
#call_slim_main(from_level = 3) ⇒ Integer (private)
Invoke one of the slim_main methods available in self.
call_slim_main(3) # Try <tt>slim_main3</tt>, then <tt>slim_main2</tt> down to <tt>slim_main</tt>.
134 135 136 137 138 139 140 141 142 143 |
# File 'lib/never_bounce/cli/script/meaningful.rb', line 134 def call_slim_main(from_level = 3) from_level.downto(0) do |i| if respond_to?(m = "slim_main#{i > 0 ? i : ''}") return send(m) end end # This is in theory possible, stay sane. raise "No `slim_main` responded, check your class hierarchy" end |
#handle_help_and_options ⇒ Integer?
See method source for important details.
Handle help request and invalid options.
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/never_bounce/cli/script/meaningful.rb', line 169 def # NOTES: # # * Due to `OptionParser` specifics we don't use on-the-fly handling of options. # We do it procedurally instead, to avoid running into a circular dependency. # Thus, it's okay to call this method directly in specs as long as `argv` is concerned. # * The method has internal protection to ensure it's called just ones to make speccing a bit easier. igetset(:handle_help_and_options) do if help? # We ignore errors if there's a help request. stdout.puts help_text 0 elsif (ar = [:errors]) stderr.puts ar 1 end end end |
#help? ⇒ Boolean
true
if help has been requested via command-line options.
54 55 56 |
# File 'lib/never_bounce/cli/script/meaningful.rb', line 54 def help? !![:help] end |
#main ⇒ Integer
Main routine which handles most boilerplate.
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/never_bounce/cli/script/meaningful.rb', line 198 def main # Handle top-level errors like `UsageError`. begin if (res = ) return res end # Invoke dump'n'exit, if `def dx` defined. # NOTE: This code is production-compatible. It's active only if final script responds to `dx`, which is strictly debug-time. if respond_to? :dx and env_truthy? "DX" dx return 1 end # Do it. result = call_slim_main # Help us stay sane. raise "Unknown `slim_main` result: #{result.inspect}" if not result.is_a? Integer result rescue *self.class.error_klasses => e stderr.puts "#{e.class.to_s.split('::').last}: #{e.}" # Like "UsageError: this and that". 1 end end |
#option_parser ⇒ Object
Our OptionParser
object.
118 119 120 121 |
# File 'lib/never_bounce/cli/script/meaningful.rb', line 118 def option_parser if not @options @option_parser end |
#options ⇒ Hash
This isn’t an attribute and there’s no writer for it. For simulation and testing we use Base#argv=.
Parse command-line options with OptionParser
.
# => {:help => true}
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/never_bounce/cli/script/meaningful.rb', line 80 def @options ||= begin h = {} @option_parser = OptionParser.new do |opts| opts. = "" # A hack to remove Ruby's "-e". opts.on("-h", "--help", "Show help information") do h[:help] = true end end = begin @option_parser.parse!(argv) rescue OptionParser::ParseError => e (h[:errors] ||= []) << "Error: #{e.}" retry end # Parse and add "KEY=value" options to environment. .reject! do |s| if s =~ /^(\w+)=(.*)$/m # NOTE: `/m` allows for multiline options. env[$1] = $2 true end end # Treat remaining options as errors. # If this behaviour changes, we should provide access to `rmn_options` via method. .each do |s| (h[:errors] ||= []) << "Error: unexpected option: #{s}" end h end end |
#slim_main ⇒ Integer
Slim or “real” main routine of the successor class. Called by #main considering all boilerplate has been taken care of.
229 230 231 |
# File 'lib/never_bounce/cli/script/meaningful.rb', line 229 def slim_main raise NotImplementedError, "Redefine `slim_main` in your class: #{self.class}" end |