Class: ImageOptim::Runner::OptionParser

Inherits:
OptionParser
  • Object
show all
Defined in:
lib/image_optim/runner/option_parser.rb

Overview

Parse options from arguments to image_optim binary

Constant Summary collapse

DEFINE =
proc do |op, options|
  unless op.is_a?(OptionParser)
    fail ArgumentError, "expected instance of OptionParser, got #{op.inspect}"
  end
  unless options.is_a?(Hash)
    fail ArgumentError, "expected instance of Hash, got #{options.inspect}"
  end

  ImageOptim::TrueFalseNil.add_to_option_parser(op)
  ImageOptim::NonNegativeIntegerRange.add_to_option_parser(op)

  op.banner = <<-TEXT.gsub(/^\s*\|/, '')
    |#{ImageOptim.full_version}
    |
    |Usage:
    |  #{op.program_name} [options] image_path …
    |
    |Configuration will be read and prepended to options from two paths:
    |  #{ImageOptim::Config::GLOBAL_PATH}
    |  #{ImageOptim::Config::LOCAL_PATH}
    |
  TEXT

  op.on('--config-paths PATH1,PATH2', Array, 'Config paths to use instead of default ones') do |paths|
    options[:config_paths] = paths
  end

  op.separator nil

  op.on('-r', '-R', '--recursive', 'Recursively scan directories for images') do |recursive|
    options[:recursive] = recursive
  end

  op.on("--exclude-dir 'GLOB'", 'Glob for excluding directories (defaults to .*)') do |glob|
    options[:exclude_dir_glob] = glob
  end

  op.on("--exclude-file 'GLOB'", 'Glob for excluding files (defaults to .*)') do |glob|
    options[:exclude_file_glob] = glob
  end

  op.on("--exclude 'GLOB'", 'Set glob for excluding both directories and files') do |glob|
    options[:exclude_file_glob] = options[:exclude_dir_glob] = glob
  end

  op.separator nil

  op.on('--no-progress', 'Disable showing progress') do |show_progress|
    options[:show_progress] = show_progress
  end

  op.on('--[no-]threads N', Integer, 'Number of threads or disable (defaults to number of processors)') do |threads|
    options[:threads] = threads
  end

  op.on(
    '--[no-]nice N',
    Integer,
    'Nice level, priority of all used tools with higher value meaning lower priority, in range -20..19, negative ' \
    'values can be set only if run by root user (defaults to 10)'
  ) do |nice|
    options[:nice] = nice
  end

  op.on(
    '--[no-]pack',
    'Require image_optim_pack or disable it, by default image_optim_pack will be used if available, will turn on ' \
    'skip-missing-workers unless explicitly disabled'
  ) do |pack|
    options[:pack] = pack
  end

  op.separator nil
  op.separator '  Caching:'

  op.on('--cache-dir DIR', 'Cache optimized images into the specified directory') do |cache_dir|
    options[:cache_dir] = cache_dir
  end

  op.on('--cache-worker-digests', 'Cache worker digests (updating workers invalidates cache)') do |cache_worker_digests|
    options[:cache_worker_digests] = cache_worker_digests
  end

  op.separator nil
  op.separator '  Disabling workers:'

  op.on('--[no-]skip-missing-workers', 'Skip workers with missing or problematic binaries') do |skip|
    options[:skip_missing_workers] = skip
  end

  ImageOptim::Worker.klasses.each do |klass|
    bin = klass.bin_sym
    op.on("--no-#{bin}", "disable #{bin} worker") do |enable|
      options[bin] = enable
    end
  end

  op.separator nil
  op.separator '  Worker options:'

  op.on('--allow-lossy', 'Allow lossy workers and optimizations') do |allow_lossy|
    options[:allow_lossy] = allow_lossy
  end

  op.on('--timeout N', Float, 'Maximum time in seconds to spend on one image') do |timeout|
    options[:timeout] = timeout
  end

  op.separator nil

  ImageOptim::Worker.klasses.each_with_index do |klass, i|
    next if klass.option_definitions.empty?

    op.separator nil unless i == 0

    bin = klass.bin_sym
    klass.option_definitions.each do |option_definition|
      name = option_definition.name.to_s.tr('_', '-')
      default = option_definition.default_description
      type = option_definition.type

      type, marking = case
      when [TrueClass, FalseClass, ImageOptim::TrueFalseNil].include?(type)
        [type, 'B']
      when type <= Integer
        [Integer, 'N']
      when type <= Array
        [Array, 'a,b,c']
      when type <= String
        [String, 'S']
      when type == ImageOptim::NonNegativeIntegerRange
        [type, 'M-N']
      else
        fail "Unknown type #{type}"
      end

      description = option_definition.description.gsub(' - ', ' - ')
      unless description['(defaults']
        description << " (defaults to #{default})"
      end

      op.on("--#{bin}-#{name} #{marking}", type, description) do |value|
        options[bin] = {} unless options[bin].is_a?(Hash)
        options[bin][option_definition.name.to_sym] = value
      end
    end
  end

  op.separator nil
  op.separator '  Common options:'

  op.on_tail(
    '-v',
    '--verbose',
    'Verbose output (show global and worker config, binary resolution log, information about each tool invocation, ' \
    'backtrace of exception)'
  ) do
    options[:verbose] = true
  end

  op.on_tail('-h', '--help', 'Show help and exit') do
    puts op.help
    exit
  end

  op.on_tail('--version', 'Show version and exit') do
    puts ImageOptim.version
    exit
  end

  op.on_tail('--info', 'Show environment info and exit') do
    options[:verbose] = true
    options[:only_info] = true
  end
end

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ OptionParser

After initialization passes self and options to DEFINE



28
29
30
31
# File 'lib/image_optim/runner/option_parser.rb', line 28

def initialize(options)
  super
  DEFINE.call(self, options)
end

Class Method Details

.parse!(args) ⇒ Object

Parse and remove options from args, return options Hash Calls abort in case of parse error



15
16
17
18
19
20
21
22
23
24
25
# File 'lib/image_optim/runner/option_parser.rb', line 15

def self.parse!(args)
  # assume -v to be a request to print version if it is the only argument
  args = %w[--version] if args == %w[-v]

  options = {}
  parser = new(options)
  parser.parse!(args)
  options
rescue OptionParser::ParseError => e
  abort "#{e}\n\n#{parser.help}"
end

Instance Method Details

#helpObject

Wraps and indents lines of overriden method



34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/image_optim/runner/option_parser.rb', line 34

def help
  text = super

  # reserve one column and limit to 120
  columns = [terminal_columns - 1, 120].min
  # 1 for distance between summary and description
  # 2 for additional indent
  wrapped_indent = summary_indent + (' ' * (summary_width + 1 + 2))
  wrapped_width = columns - wrapped_indent.length
  # don't try to wrap if there is too little space for description
  return text if wrapped_width < 20

  wrapped_text(text, wrapped_width, wrapped_indent, columns)
end