Class: Cisco::CmdRef

Inherits:
Object
  • Object
show all
Defined in:
lib/cisco_node_utils/command_reference.rb

Overview

Control a reference for an attribute.

Direct Known Subclasses

UnsupportedCmdRef

Constant Summary collapse

KEYS =
%w(default_value default_only
data_format context value
get_data_format get_command get_context get_value
set_data_format set_context set_value
auto_default multiple kind os_version)
KINDS =
%w(boolean int string symbol)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(feature, name, values, file) ⇒ CmdRef

Construct a CmdRef describing the given (feature, name) pair. Param “values” is a hash with keys as described in KEYS. Param “file” is for debugging purposes only.


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
# File 'lib/cisco_node_utils/command_reference.rb', line 42

def initialize(feature, name, values, file)
  fail ArgumentError, "'#{values}' is not a hash." unless values.is_a? Hash

  @feature = feature
  @name = name
  @auto_default = true
  @default_only = false
  @multiple = false
  @kind = nil
  @os_version = nil

  values_to_hash(values, file)

  if @hash['get_value'] || @hash['get_command']
    define_helper('getter',
                  data_format: @hash['get_data_format'] || :cli,
                  command:     @hash['get_command'],
                  context:     @hash['get_context'] || [],
                  value:       @hash['get_value'])
  end
  if @hash['set_value'] # rubocop:disable Style/GuardClause
    define_helper('setter',
                  data_format: @hash['set_data_format'] || :cli,
                  context:     @hash['set_context'] || [],
                  values:      @hash['set_value'])
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &block) ⇒ Object


300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/cisco_node_utils/command_reference.rb', line 300

def method_missing(method_name, *args, &block)
  if KEYS.include?(method_name.to_s)
    # ref.foo -> return @hash[foo] or fail IndexError
    method_name = method_name.to_s
    unless @hash.include?(method_name)
      if @default_only
        fail UnsupportedError.new(@feature, @name, method_name)
      end
      fail IndexError, "No #{method_name} defined for #{@feature}, #{@name}"
    end
    # puts("get #{method_name}: '#{@hash[method_name]}'")
    @hash[method_name]
  elsif method_name.to_s[-1] == '?' && \
        KEYS.include?(method_name.to_s[0..-2])
    # ref.foo? -> return true if @hash[foo], else false
    method_name = method_name.to_s[0..-2]
    @hash.include?(method_name)
  else
    super(method_name, *args, &block)
  end
end

Instance Attribute Details

#auto_defaultObject (readonly) Also known as: auto_default?

Returns the value of attribute auto_default


22
23
24
# File 'lib/cisco_node_utils/command_reference.rb', line 22

def auto_default
  @auto_default
end

#default_onlyObject (readonly) Also known as: default_only?

Returns the value of attribute default_only


22
23
24
# File 'lib/cisco_node_utils/command_reference.rb', line 22

def default_only
  @default_only
end

#featureObject (readonly)

Returns the value of attribute feature


21
22
23
# File 'lib/cisco_node_utils/command_reference.rb', line 21

def feature
  @feature
end

#hashObject (readonly)

Returns the value of attribute hash


21
22
23
# File 'lib/cisco_node_utils/command_reference.rb', line 21

def hash
  @hash
end

#kindObject (readonly)

Returns the value of attribute kind


22
23
24
# File 'lib/cisco_node_utils/command_reference.rb', line 22

def kind
  @kind
end

#multipleObject (readonly) Also known as: multiple?

Returns the value of attribute multiple


22
23
24
# File 'lib/cisco_node_utils/command_reference.rb', line 22

def multiple
  @multiple
end

#nameObject (readonly)

Returns the value of attribute name


21
22
23
# File 'lib/cisco_node_utils/command_reference.rb', line 21

def name
  @name
end

#os_versionObject (readonly)

Returns the value of attribute os_version


22
23
24
# File 'lib/cisco_node_utils/command_reference.rb', line 22

def os_version
  @os_version
end

Class Method Details

.keysObject


33
34
35
# File 'lib/cisco_node_utils/command_reference.rb', line 33

def self.keys
  KEYS
end

Instance Method Details

#boolean_default_true(value) ⇒ Object

Property with an implicit value of 'true' if no value is given


160
161
162
# File 'lib/cisco_node_utils/command_reference.rb', line 160

def boolean_default_true(value)
  value.nil? || value
end

#convert_to_constant(value) ⇒ Object


279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/cisco_node_utils/command_reference.rb', line 279

def convert_to_constant(value)
  # NOTE: This method is now deprecated and should not be used for future
  #       development.
  #
  # If value is a string and it is empty OR the first letter is lower case
  # then leave value untouched.
  # If value is a string and the first letter is uppercase this indicates
  # that it could be a constant in Ruby, so attempt to convert it
  # to a Constant.
  if value.is_a?(String) && !value.empty?
    if value[0].chr == value[0].chr.upcase
      begin
        value = Object.const_get(value) if Object.const_defined?(value)
      rescue NameError
        debug("'#{value}' is not a constant")
      end
    end
  end
  value
end

#define_helper(method_name, base_hash) ⇒ Object

Create a helper method for generating the getter/setter values. This method will automatically handle wildcard arguments.


182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/cisco_node_utils/command_reference.rb', line 182

def define_helper(method_name, base_hash)
  # Which kind of wildcards (if any) do we need to support?
  combined = []
  base_hash.each_value do |v|
    combined += v if v.is_a?(Array)
    combined << v if v.is_a?(String)
  end
  key_value = combined.any? { |i| i.is_a?(String) && /<\S+>/ =~ i }
  printf = combined.any? { |i| i.is_a?(String) && /%/ =~ i }

  if key_value && printf
    fail 'Invalid mixture of key-value and printf wildcards ' \
         "in #{method_name}: #{combined}"
  elsif key_value
    define_key_value_helper(method_name, base_hash)
  elsif printf
    arg_count = combined.join.scan(/%/).length
    define_printf_helper(method_name, base_hash, arg_count)
  else
    # simple static token(s)
    define_static_helper(method_name, base_hash)
  end
  @hash[method_name] = true
end

#define_key_value_helper(method_name, base_hash) ⇒ Object


207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/cisco_node_utils/command_reference.rb', line 207

def define_key_value_helper(method_name, base_hash)
  # Key-value substitution
  define_singleton_method method_name.to_sym do |*args, **kwargs|
    unless args.empty?
      fail ArgumentError, "#{method_name} requires keyword args, not "\
        'positional args'
    end
    result = {}
    base_hash.each do |k, v|
      if v.is_a?(String)
        v = key_substitutor(v, kwargs)
      elsif v.is_a?(Array)
        output = []
        v.each do |line|
          # Check for (?) flag indicating optional param
          optional_line = line[/^\(\?\)(.*)/, 1]
          if optional_line
            begin
              line = key_substitutor(optional_line, kwargs)
            rescue ArgumentError # Unsubstituted key - OK to skip this line
              next
            end
          else
            line = key_substitutor(line, kwargs)
          end
          output.push(line)
        end
        v = output
      end
      result[k] = v
    end
    result
  end
end

#define_printf_helper(method_name, base_hash, arg_count) ⇒ Object


242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/cisco_node_utils/command_reference.rb', line 242

def define_printf_helper(method_name, base_hash, arg_count)
  define_singleton_method method_name.to_sym do |*args, **kwargs|
    unless kwargs.empty?
      fail ArgumentError, "#{method_name} requires positional args, not " \
        'keyword args'
    end
    unless args.length == arg_count
      fail ArgumentError, 'wrong number of arguments ' \
        "(#{args.length} for #{arg_count})"
    end

    result = {}
    base_hash.each do |k, v|
      if v.is_a?(String)
        v, args = printf_substitutor(v, args)
      elsif v.is_a?(Array)
        output = []
        v.each do |line|
          line, args = printf_substitutor(line, args)
          output.push(line)
        end
        v = output
      end
      result[k] = v
    end
    result
  end
end

#define_static_helper(method_name, base_hash) ⇒ Object


271
272
273
274
275
276
277
# File 'lib/cisco_node_utils/command_reference.rb', line 271

def define_static_helper(method_name, base_hash)
  # rubocop:disable Lint/UnusedBlockArgument
  define_singleton_method method_name.to_sym do |*args, **kwargs|
    base_hash
  end
  # rubocop:enable Lint/UnusedBlockArgument
end

#getter(*args, **kwargs) ⇒ Object

Default getter method. Will be overridden at initialization if the relevant parameters are set.

A non-trivial implementation of this method will take args or kwargs, and will return a hash of the form:

data_format: :cli,
command:     string or nil,
context:     array<string> or array<regexp>, perhaps empty
value:       string or regexp,


141
142
143
# File 'lib/cisco_node_utils/command_reference.rb', line 141

def getter(*args, **kwargs) # rubocop:disable Lint/UnusedMethodArgument
  fail UnsupportedError.new(@feature, @name, 'getter')
end

#getter?Boolean

Does this instance have a valid getter() function? Will be overridden at initialization if so.


120
121
122
# File 'lib/cisco_node_utils/command_reference.rb', line 120

def getter?
  !@hash['getter'].nil?
end

#key_substitutor(item, kwargs) ⇒ Object


164
165
166
167
168
169
170
171
172
173
# File 'lib/cisco_node_utils/command_reference.rb', line 164

def key_substitutor(item, kwargs)
  result = item
  kwargs.each do |key, value|
    result = result.sub("<#{key}>", value.to_s)
  end
  unsub = result[/<(\S+)>/, 1]
  fail ArgumentError, \
       "No value specified for '#{unsub}' in '#{result}'" if unsub
  result
end

#printf_substitutor(item, args) ⇒ Object


175
176
177
178
# File 'lib/cisco_node_utils/command_reference.rb', line 175

def printf_substitutor(item, args)
  item = sprintf(item, *args.shift(item.scan(/%/).length))
  [item, args]
end

#setter(*args, **kwargs) ⇒ Object

Default setter method. Will be overridden at initialization if the relevant parameters are set.

A non-trivial implementation of this method will take args or kwargs, and will return a hash of the form:

data_format: :cli,
context:     array<string>, perhaps empty
values:      array<string>,


155
156
157
# File 'lib/cisco_node_utils/command_reference.rb', line 155

def setter(*args, **kwargs) # rubocop:disable Lint/UnusedMethodArgument
  fail UnsupportedError.new(@feature, @name, 'setter')
end

#setter?Boolean

Does this instance have a valid setter() function? Will be overridden at initialization if so.


126
127
128
# File 'lib/cisco_node_utils/command_reference.rb', line 126

def setter?
  !@hash['setter'].nil?
end

#to_sObject

Print useful debugging information about the object.


323
324
325
326
327
328
# File 'lib/cisco_node_utils/command_reference.rb', line 323

def to_s
  str = ''
  str << "Command: #{@feature} #{@name}\n"
  @hash.each { |key, value| str << "  #{key}: #{value}\n" }
  str
end

#values_to_hash(values, file) ⇒ Object


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
# File 'lib/cisco_node_utils/command_reference.rb', line 70

def values_to_hash(values, file)
  @hash = {}
  values.each do |key, value|
    unless KEYS.include?(key)
      fail "Unrecognized key #{key} for #{feature}, #{name} in #{file}"
    end
    case key
    when 'auto_default'
      @auto_default = value ? true : false
    when 'data_format', 'get_data_format', 'set_data_format'
      @hash[key] = value.to_sym
    when 'default_only'
      @default_only = true
      # default_value overrides default_only
      @hash['default_value'] ||= value
    when 'multiple'
      @multiple = boolean_default_true(value)
    when 'kind'
      fail "Unknown 'kind': '#{value}'" unless KINDS.include?(value)
      @kind = value.to_sym
    when 'os_version'
      @os_version = value
    else
      # default_value overrides default_only
      @default_only = false if key == 'default_value'
      @hash[key] = value
    end
  end

  # Inherit general to specific if needed
  if @hash.key?('data_format')
    @hash['get_data_format'] = @hash['data_format'] \
      unless @hash.key?('get_data_format')
    @hash['set_data_format'] = @hash['data_format'] \
      unless @hash.key?('set_data_format')
  end
  if @hash.key?('context')
    @hash['get_context'] = @hash['context'] unless @hash.key?('get_context')
    @hash['set_context'] = @hash['context'] unless @hash.key?('set_context')
  end
  if @hash.key?('value')
    @hash['get_value'] = @hash['value'] unless @hash.key?('get_value')
    @hash['set_value'] = @hash['value'] unless @hash.key?('set_value')
  end

  @hash.delete_if { |key, _| key != 'default_value' } if @default_only
end