merge devops_options_parser

This commit is contained in:
Anton Chuchkalov 2015-04-13 17:44:30 +04:00
parent 13eed304b1
commit 9228193da4
3 changed files with 272 additions and 73 deletions

View File

@ -1,5 +1,5 @@
require "optparse"
require "devops-client/version"
require "devops-client/options/helpers/devops_options_parser"
class CommonOptions
@ -82,78 +82,15 @@ class CommonOptions
end
def options
o = {}
optparse = OptionParser.new do |opts|
opts.banner = "\n" + I18n.t("options.usage", :cmd => $0) + "\n\n" + I18n.t("options.commands") + ":\n"
if block_given?
opts.separator(I18n.t("options.options") + ":\n")
yield opts, o
end
opts.separator("\n" + I18n.t("options.common_options") + ":\n")
opts.on("-h", "--help", I18n.t("options.common.help")) do
opts.banner << "\n"
puts opts
exit
end
o[:no_ask] = false
opts.on("-y", "--assumeyes", I18n.t("options.common.confirmation")) do
o[:no_ask] = true;
end
#Not used, just for banner purposes. This should be fixed when we find how to deal with options separetely
opts.on("-c", "--config CONFIG", I18n.t("options.common.config", :file => DevopsClient.config_file)) do
puts "Not implemented yet"
exit
end
opts.on("-v", "--version", I18n.t("options.common.version")) do
puts I18n.t("options.common.version") + ": #{DevopsClient::VERSION}"
exit
end
opts.on("--host HOST", I18n.t("options.common.host", :host => default_options[:host])) do |h|
o[:host] = h
end
o[:api] = default_options[:api]
opts.on("--api VER", I18n.t("options.common.api", :api => o[:api])) do |a|
o[:api] = a
end
o[:prefix] = default_options[:prefix]
opts.on("--prefix PREFIX", I18n.t("options.common.prefix", :prefix => o[:prefix])) do |p|
o[:prefix] = p
end
o[:username] = default_options[:username]
opts.on("--user USERNAME", I18n.t("options.common.username", :username => o[:username])) do |u|
o[:username] = u.strip
print I18n.t("handler.user.password_for", :user => o[:username])
begin
system("stty -echo")
o[:password] = STDIN.gets.strip
ensure
system("stty echo")
end
puts
end
o[:format] = TABLE_FORMAT
opts.on("--format FORMAT", I18n.t("options.common.format", :formats => OUTPUT_FROMATS.join("', '"), :format => TABLE_FORMAT)) do |f|
o[:format] = f if OUTPUT_FROMATS.include?(f)
end
# should be handled in lib/devops-client.rb
opts.on("", "--completion", I18n.t("options.common.completion"))
end
optparse.parse!(self.args)
o
# You could use block to extend default functionality:
# self.options do |parser, options|
# parser.banner << 'banner'
# options[:any_option] = {a: 123}
# end
def options(&block)
parser = Options::Helpers::DevopsOptionsParser.new(default_options, &block)
parser.parse!(self.args)
parser.parsed_options
end
def invalid_command

View File

@ -0,0 +1,189 @@
require "optparse"
require 'forwardable'
require_relative 'option_value_recognizer'
module Options
module Helpers
class DevopsOptionsParser
extend Forwardable
attr_reader :parsed_options
# leave this duplication for a while
TABLE_FORMAT = "table"
JSON_FORMAT = "json"
CSV_FORMAT = "csv"
OUTPUT_FROMATS = [TABLE_FORMAT, JSON_FORMAT, CSV_FORMAT]
def_delegators :@parser, :banner, :separator, :on, :parse!
def initialize(default_options, &block)
@parser = OptionParser.new
@default_options = default_options
@parsed_options = {}
banner_usage
if block
@parser.separator(I18n.t("options.options") + ":\n")
block.call(self, @parsed_options)
end
common_options_separator
recognize_help
recognize_assume_yes
recognize_config
recognize_version
recognize_host
recognize_api
recognize_prefix
recognize_username
recognize_format
recognize_completion
@parser
end
# it is used to set options values without later quiz.
# Arguments description:
# option_name - name of option;
# resource_name - used for description lookup. Lookup path is "options.descriptions.#{resource_name}.#{option_name}".
# attrs - hash with following options:
# :type - could be one of following values:
# :required (default)
# :optional
# :switch
# :default - default value. nil by default
# :switch_value - set this value, if type is :switch and option've been recognized.
# :option_key - key in result_options hash. Default - option_name.to_sym
# :variable - default - option_name.upcase
# :description - default - I18n.t("options.descriptions.#{resource_name}.#{option_name}")
# :i18n_scope - if present, change I18n lookup path to "options.descriptions.#{resource_name}.#{i18n_scope}.#{option_name}"
# :short - short option name
#
# EXAMPLES:
# 1)
# parser.recognize_option_value(:provider, 'stack')
# is equal to
# opts.on("--provider provider", I18n.t("options.descriptions.stack.provider)) do |provider|
# options[:provider] = provider
# end
#
# 2)
# parser.recognize_option_value(:provider, 'stack', type: :optional, default: 'openstack')
# is equal to
# options[:provider] = 'openstack'
# opts.on("--provider [provider]", I18n.t("options.descriptions.stack.provider)) do |provider|
# options[:provider] = provider
# end
#
# 3)
# parser.recognize_option_value(:no_template, 'image', type: :switch, default: false, switch_value: true)
# is equal to
# options[:no_template] = false
# opts.on("--no_template", I18n.t("options.descriptions.image.no_template)) do
# options[:no_template] = true
# end
#
# 4)
# parser.recognize_option_value(:parameters, 'stack') do |parameters|
# options[:parameters] = JSON.parse(parameters)
# end
# is equal to
# opts.on("--parameters parameters", I18n.t("options.descriptions.stack.parameters)) do |parameters|
# options[:parameters] = JSON.parse(parameters)
# end
def recognize_option_value(option_name, resource_name, attrs={}, &block)
recognizer = OptionValueRecognizer.new(option_name, resource_name, attrs)
recognizer.recognize(@parser, @parsed_options, &block)
end
private
def banner_usage
@parser.banner = "\n" + I18n.t("options.usage", :cmd => $0) + "\n\n" + I18n.t("options.commands") + ":\n"
end
def common_options_separator
@parser.separator("\n" + I18n.t("options.common_options") + ":\n")
end
def recognize_help
@parser.on("-h", "--help", I18n.t("options.common.help")) do
@parser.banner << "\n"
puts @parser
exit
end
end
def recognize_assume_yes
@parsed_options[:no_ask] = false
@parser.on("-y", "--assumeyes", I18n.t("options.common.confirmation")) do
@parsed_options[:no_ask] = true;
end
end
def recognize_config
#Not used, just for banner purposes. This should be fixed when we find how to deal with options separetely
@parser.on("-c", "--config CONFIG", I18n.t("options.common.config", :file => DevopsClient.config_file)) do
puts "Not implemented yet"
exit
end
end
def recognize_version
@parser.on("-v", "--version", I18n.t("options.common.version")) do
puts I18n.t("options.common.version") + ": #{DevopsClient::VERSION}"
exit
end
end
def recognize_host
@parser.on("--host HOST", I18n.t("options.common.host", :host => @default_options[:host])) do |h|
@parsed_options[:host] = h
end
end
def recognize_api
@parsed_options[:api] = @default_options[:api]
@parser.on("--api VER", I18n.t("options.common.api", :api => @parsed_options[:api])) do |a|
@parsed_options[:api] = a
end
end
def recognize_prefix
@parsed_options[:prefix] = @default_options[:prefix]
@parser.on("--prefix PREFIX", I18n.t("options.common.prefix", :prefix => @parsed_options[:prefix])) do |p|
@parsed_options[:prefix] = p
end
end
def recognize_username
@parsed_options[:username] = @default_options[:username]
@parser.on("--user USERNAME", I18n.t("options.common.username", :username => @parsed_options[:username])) do |u|
@parsed_options[:username] = u.strip
print I18n.t("handler.user.password_for", :user => @parsed_options[:username])
begin
system("stty -echo")
@parsed_options[:password] = STDIN.gets.strip
ensure
system("stty echo")
end
puts
end
end
def recognize_format
@parsed_options[:format] = TABLE_FORMAT
@parser.on("--format FORMAT", I18n.t("options.common.format", :formats => OUTPUT_FROMATS.join("', '"), :format => TABLE_FORMAT)) do |f|
@parsed_options[:format] = f if OUTPUT_FROMATS.include?(f)
end
end
def recognize_completion
# should be handled in lib/devops-client.rb
@parser.on("", "--completion", I18n.t("options.common.completion"))
end
end
end
end

View File

@ -0,0 +1,73 @@
# This class is used only in devops_option_parser.
# It was extracted because #recognize_option_value method became too large.
# Description and examples of usage are in devops_option_parser.rb.
class OptionValueRecognizer
def initialize(option_name, resource_name, attrs={})
@option_name, @attrs = option_name, attrs
set_type(option_name, resource_name)
set_option_key(option_name, resource_name)
set_description(option_name, resource_name)
set_variable(option_name, resource_name)
set_options_to_recognize(option_name, resource_name)
end
def recognize(parser, parsed_options, &block)
parsed_options[@option_key] = @attrs[:default] if @attrs.keys.include?(:default)
parser.on(*@options_to_recognize, @description) do |value|
value = @attrs[:switch_value] if @type == :switch
if block
block.call(value)
else
parsed_options[@option_key] = value
end
end
end
private
def set_type(option_name, resource_name)
@type = @attrs[:type] || :required
raise "Illegal optional type: '#{@type}'" unless [:required, :optional, :switch].include?(@type)
if @type == :switch && !@attrs.keys.include?(:switch_value)
raise 'Missing switch value'
end
end
def set_option_key(option_name, resource_name)
@option_key = @attrs[:option_key] || option_name.to_sym
end
def set_description(option_name, resource_name)
if @attrs[:description]
@description = @attrs[:description]
else
lookup_path = [:options, :descriptions, resource_name]
lookup_path << @attrs[:i18n_scope] if @attrs[:i18n_scope]
lookup_path << option_name
@description = I18n.t(lookup_path.join('.'))
end
end
def set_variable(option_name, resource_name)
variable = @attrs[:variable] || option_name.upcase
@variable = case @type
when :optional
" [#{variable}]"
when :switch
''
else # required by default
" #{variable}"
end
end
def set_options_to_recognize(option_name, resource_name)
full = "--#{@option_name}#{@variable}"
@options_to_recognize = [full]
@options_to_recognize.unshift(@attrs[:short]) if @attrs[:short]
end
end