1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
# frozen_string_literal: true
# This represents an option on an action or face (to be globally applied
# to its actions). Options should be constructed by calling
# {Puppet::Interface::OptionManager#option}, which is available on
# {Puppet::Interface}, and then calling methods of
# {Puppet::Interface::OptionBuilder} in the supplied block.
# @api public
class Puppet::Interface::Option
include Puppet::Interface::TinyDocs
# @api private
def initialize(parent, *declaration, &block)
@parent = parent
@optparse = []
@default = nil
# Collect and sort the arguments in the declaration.
dups = {}
declaration.each do |item|
if item.is_a? String and item.to_s =~ /^-/ then
unless item =~ /^-[a-z]\b/ or item =~ /^--[^-]/ then
raise ArgumentError, _("%{option}: long options need two dashes (--)") % { option: item.inspect }
end
@optparse << item
# Duplicate checking...
# for our duplicate checking purpose, we don't make a check with the
# translated '-' -> '_'. Right now, we do that on purpose because of
# a duplicated option made publicly available on certificate and ca
# faces for dns alt names. Puppet defines 'dns_alt_names', those
# faces include 'dns-alt-names'. We can't get rid of 'dns-alt-names'
# yet, so we need to do our duplicate checking on the untranslated
# version of the option.
# jeffweiss 17 april 2012
name = optparse_to_optionname(item)
if Puppet.settings.include? name then
raise ArgumentError, _("%{option}: already defined in puppet") % { option: item.inspect }
end
dup = dups[name]
if dup
raise ArgumentError, _("%{option}: duplicates existing alias %{duplicate} in %{parent}") %
{ option: item.inspect, duplicate: dup.inspect, parent: @parent }
else
dups[name] = item
end
else
raise ArgumentError, _("%{option} is not valid for an option argument") % { option: item.inspect }
end
end
if @optparse.empty? then
raise ArgumentError, _("No option declarations found while building")
end
# Now, infer the name from the options; we prefer the first long option as
# the name, rather than just the first option.
@name = optparse_to_name(@optparse.find do |a| a =~ /^--/ end || @optparse.first)
@aliases = @optparse.map { |o| optparse_to_name(o) }
# Do we take an argument? If so, are we consistent about it, because
# incoherence here makes our life super-difficult, and we can more easily
# relax this rule later if we find a valid use case for it. --daniel 2011-03-30
@argument = @optparse.any? { |o| o =~ /[ =]/ }
if @argument and !@optparse.all? { |o| o =~ /[ =]/ } then
raise ArgumentError, _("Option %{name} is inconsistent about taking an argument") % { name: @name }
end
# Is our argument optional? The rules about consistency apply here, also,
# just like they do to taking arguments at all. --daniel 2011-03-30
@optional_argument = @optparse.any? { |o| o =~ /[ =]\[/ }
if @optional_argument
raise ArgumentError, _("Options with optional arguments are not supported")
end
if @optional_argument and !@optparse.all? { |o| o =~ /[ =]\[/ } then
raise ArgumentError, _("Option %{name} is inconsistent about the argument being optional") % { name: @name }
end
end
# to_s and optparse_to_name are roughly mirrored, because they are used to
# transform options to name symbols, and vice-versa. This isn't a full
# bidirectional transformation though. --daniel 2011-04-07
def to_s
@name.to_s.tr('_', '-')
end
# @api private
def optparse_to_optionname(declaration)
found = declaration.match(/^-+(?:\[no-\])?([^ =]+)/)
unless found
raise ArgumentError, _("Can't find a name in the declaration %{declaration}") % { declaration: declaration.inspect }
end
found.captures.first
end
# @api private
def optparse_to_name(declaration)
name = optparse_to_optionname(declaration).tr('-', '_')
unless name.to_s =~ /^[a-z]\w*$/
raise _("%{name} is an invalid option name") % { name: name.inspect }
end
name.to_sym
end
def takes_argument?
!!@argument
end
def optional_argument?
!!@optional_argument
end
def required?
!!@required
end
def has_default?
!!@default
end
def default=(proc)
if required
raise ArgumentError, _("%{name} can't be optional and have a default value") % { name: self }
end
unless proc.is_a? Proc
# TRANSLATORS 'proc' is a Ruby block of code
raise ArgumentError, _("default value for %{name} is a %{class_name}, not a proc") %
{ name: self, class_name: proc.class.name.inspect }
end
@default = proc
end
def default
@default and @default.call
end
attr_reader :parent, :name, :aliases, :optparse, :required
def required=(value)
if has_default?
raise ArgumentError, _("%{name} can't be optional and have a default value") % { name: self }
end
@required = value
end
attr_reader :before_action
def before_action=(proc)
unless proc.is_a? Proc
# TRANSLATORS 'proc' is a Ruby block of code
raise ArgumentError, _("before action hook for %{name} is a %{class_name}, not a proc") %
{ name: self, class_name: proc.class.name.inspect }
end
@before_action =
@parent.__send__(:__add_method, __decoration_name(:before), proc)
end
attr_reader :after_action
def after_action=(proc)
unless proc.is_a? Proc
# TRANSLATORS 'proc' is a Ruby block of code
raise ArgumentError, _("after action hook for %{name} is a %{class_name}, not a proc") %
{ name: self, class_name: proc.class.name.inspect }
end
@after_action =
@parent.__send__(:__add_method, __decoration_name(:after), proc)
end
def __decoration_name(type)
if @parent.is_a? Puppet::Interface::Action then
:"option #{name} from #{parent.name} #{type} decoration"
else
:"option #{name} #{type} decoration"
end
end
end
|