File: option.rb

package info (click to toggle)
puppet-agent 8.10.0-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 27,404 kB
  • sloc: ruby: 286,820; sh: 492; xml: 116; makefile: 88; cs: 68
file content (184 lines) | stat: -rw-r--r-- 6,255 bytes parent folder | download | duplicates (2)
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