rvm/extoptparse

View on GitHub
lib/group_option_parser.rb

Summary

Maintainability
A
0 mins
Test Coverage
=begin
Copyright 2017 Michal Papis <mpapis@gmail.com>

This file is part of extoptparse.

Extoptparse is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Extoptparse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with extoptparse.  If not, see <http://www.gnu.org/licenses/>.
=end

require "optparse"

# Add grouping to Ruby's `OptionParser`
module GroupOptionParser

  # changes the target of all functions to be set by `group`
  def top
    @stack[current_top]
  end

  # @overload remove(name)
  #   allows removing last or group by `name`
  # @param name STRING name of the group to remove,
  #                    if no name provided removes last added list
  def remove(name=nil)
    if name
      remove_group(name)
    else
      remove_last
    end
  end

  # run block in context of a group,
  # if group does not exist it's created
  # @param name STRING name of the group to change context to, if the group is missing it's created
  # @yield self all the operations on instance now refer to group
  # TODO: not thread safe, might need to do it in a thread safe way if we ever need threads to build parser
  def group(name, &_block)
    self.current_top = group_index(name)
    yield self
    self.current_top = TOP_INDEX
  end

private

  TOP_INDEX = -1

  attr_writer :current_top

  def current_top
    @current_top ||= TOP_INDEX
  end

  def groups
    @groups ||= {
      "tail" => 1,
      "top"  => TOP_INDEX,
    }
  end

  def group_index(name)
    name = name.to_s
    groups[name] || new_group(name)
  end

  def new_group(name)
    index = last_index
    groups[name] = index
    @stack.insert(index, OptionParser::List.new)
    index
  end

  def remove_group(name)
    index = groups.delete(name.to_str)
    @stack.delete_at(index) if index
  end

  def remove_last
    name, _ = groups.rassoc(last_index)
    groups.delete(name) if name
    @stack.pop
  end

  def last_index
    @stack.size - 1
  end
end