class NeverBounce::CLI::Script::Meaningful

A meaningful base script class. Features:

@abstract @see CLI::Feature::Envars @see CLI::Feature::Igetset

Attributes

banner_text[W]
envar_text[W]
help_text[W]
manifest[W]
options_text[W]

Public Class Methods

error_klasses() click to toggle source

Exception classes which are rescued fromc in {#main} and printed to user. @note Should be a few very high-level excaptions which you fully control. @return [Array]

# File lib/never_bounce/cli/script/meaningful.rb, line 49
def self.error_klasses
  [Error]
end
format_envar_examples(envar) click to toggle source

Format an envar examples string.

format_examples_string(envar)   # => ""a", *"B""

@return [String] Insertion-ready string or nil if envar doesn't have examples.

# 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

Public Instance Methods

banner_text() click to toggle source

@return [String]

envar_text() click to toggle source

@return [String]

# 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
handle_help_and_options() click to toggle source

Handle help request and invalid options. @note See method source for important details. @return [Integer] Program exit code (0, 1) if the program should exit now. @return [nil] If all okay.

# File lib/never_bounce/cli/script/meaningful.rb, line 169
def handle_help_and_options
  # 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 = options[:errors])
      stderr.puts ar
      1
    end
  end
end
help?() click to toggle source

true if help has been requested via command-line options.

# File lib/never_bounce/cli/script/meaningful.rb, line 54
def help?
  !!options[:help]
end
help_text() click to toggle source
# File lib/never_bounce/cli/script/meaningful.rb, line 58
def help_text
  @help_text ||= begin
    [
      banner_text,
      "",
      "USAGE: #{manifest.name} #{manifest.cmdline}",
      "",
      options_text,
      "",
      "Environment variables:",
      envar_text,
    ].join("\n")
  end
end
main() click to toggle source

Main routine which handles most boilerplate. @return [Integer] @see slim_main

# File lib/never_bounce/cli/script/meaningful.rb, line 198
def main
  # Handle top-level errors like `UsageError`.
  begin
    if (res = handle_help_and_options)
      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.message}"    # Like "UsageError: this and that".
    1
  end
end
manifest() click to toggle source

Program manifest object. Successors should return it. @abstract @return [Manicest]

# File lib/never_bounce/cli/script/meaningful.rb, line 191
def manifest
  raise NotImplementedError, "Redefine `manifest` in your class: #{self.class}"
end
option_parser() click to toggle source

Our OptionParser object.

# File lib/never_bounce/cli/script/meaningful.rb, line 118
def option_parser
  options if not @options
  @option_parser
end
options() click to toggle source

Parse command-line options with OptionParser.

options   # => {:help => true}

@note This isn't an attribute and there's no writer for it. For simulation and testing

we use {#argv=}.

@return [Hash]

# File lib/never_bounce/cli/script/meaningful.rb, line 80
def options
  @options ||= begin
    h = {}

    @option_parser = OptionParser.new do |opts|
      opts.banner = ""    # A hack to remove Ruby's "-e".

      opts.on("-h", "--help", "Show help information") do
        h[:help] = true
      end
    end

    rmn_options = begin
      @option_parser.parse!(argv)
    rescue OptionParser::ParseError => e
      (h[:errors] ||= []) << "Error: #{e.message}"
      retry
    end

    # Parse and add "KEY=value" options to environment.
    rmn_options.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.
    rmn_options.each do |s|
      (h[:errors] ||= []) << "Error: unexpected option: #{s}"
    end

    h
  end
end
options_text() click to toggle source
# File lib/never_bounce/cli/script/meaningful.rb, line 123
def options_text
  @options_text ||= option_parser.help.strip
end
slim_main() click to toggle source

Slim or “real” main routine of the successor class. Called by {#main} considering all boilerplate has been taken care of. @abstract @return [Integer]

# File lib/never_bounce/cli/script/meaningful.rb, line 229
def slim_main
  raise NotImplementedError, "Redefine `slim_main` in your class: #{self.class}"
end

Private Instance Methods

call_slim_main(from_level = 3) click to toggle source

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>.

@return [Integer] Result of slim_main[N].

# 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