class YamlLint::Linter::KeyOverlapDetector

Detects duplicate keys in Psych parsed data

Attributes

overlapping_keys[R]

Public Class Methods

new() click to toggle source

Setup class variables

# File lib/yamllint/linter.rb, line 133
def initialize
  @seen_keys = Set.new
  @key_components = []
  @last_key = ['']
  @overlapping_keys = Set.new
  @complex_type = []
  @array_positions = []
end

Public Instance Methods

parse(psych_parse_data) click to toggle source

Get the data and send it off for duplicate key validation

# File lib/yamllint/linter.rb, line 143
def parse(psych_parse_data)
  data_start = psych_parse_data.handler.root.children[0]
  parse_recurse(data_start)
end

Private Instance Methods

add_value(value, key) click to toggle source

Add a key / value pair

# File lib/yamllint/linter.rb, line 219
def add_value(value, key)
  YamlLint.logger.debug { "add_value: #{value.inspect}, #{key.inspect}" }

  case @complex_type.last
  when :hash
    @key_components.push(key)
    check_for_overlap!
    @key_components.pop
  when :array
    @key_components.push(@array_positions.last)
    check_for_overlap!
    @array_positions[-1] += 1
    @key_components.pop
  end
end
array_end(key) click to toggle source

Tear down the array

# File lib/yamllint/linter.rb, line 210
def array_end(key)
  YamlLint.logger.debug { "array_end: #{key.inspect}" }

  @key_components.pop
  @complex_type.pop
  @array_positions.pop
end
array_start(key) click to toggle source

Setup a new array

# File lib/yamllint/linter.rb, line 199
def array_start(key)
  YamlLint.logger.debug { "array_start: #{key.inspect}" }

  complex_type_start(key)

  @complex_type.push(:array)
  @array_positions.push(0)
  check_for_overlap!
end
check_for_overlap!() click to toggle source

Check for key overlap

# File lib/yamllint/linter.rb, line 236
def check_for_overlap!
  full_key = @key_components.dup
  YamlLint.logger.debug { "Checking #{full_key.join('.')} for overlap" }

  return if @seen_keys.add?(full_key)
  YamlLint.logger.debug { "Overlapping key #{full_key.join('.')}" }
  @overlapping_keys << full_key
end
complex_type_start(key) click to toggle source

Setup common hash and array elements

# File lib/yamllint/linter.rb, line 246
def complex_type_start(key)
  case @complex_type.last
  when :hash
    @key_components.push(key)
  when :array
    @key_components.push(@array_positions.last)
    @array_positions[-1] += 1
  end
end
hash_end(key) click to toggle source

Tear down a hash

# File lib/yamllint/linter.rb, line 191
def hash_end(key)
  YamlLint.logger.debug { "hash_end: #{key.inspect}" }

  @key_components.pop
  @complex_type.pop
end
hash_start(key) click to toggle source

Setup a new hash

# File lib/yamllint/linter.rb, line 181
def hash_start(key)
  YamlLint.logger.debug { "hash_start: #{key.inspect}" }

  complex_type_start(key)

  @complex_type.push(:hash)
  check_for_overlap!
end
parse_recurse(psych_parse_data, is_sequence = false) click to toggle source

Recusively check for duplicate keys

# File lib/yamllint/linter.rb, line 151
def parse_recurse(psych_parse_data, is_sequence = false)
  is_key = false
  psych_parse_data.children.each do |n|
    case n.class.to_s
    when 'Psych::Nodes::Scalar'
      is_key = !is_key unless is_sequence
      @last_key.push(n.value) if is_key
      add_value(n.value, @last_key.last) unless is_key
      msg = "Scalar: #{n.value}, key: #{is_key}, last_key: #{@last_key}"
      YamlLint.logger.debug { msg }
      @last_key.pop if !is_key && !is_sequence
    when 'Psych::Nodes::Sequence'
      YamlLint.logger.debug { "Sequence: #{n.children}" }
      array_start(@last_key.last)
      parse_recurse(n, true)
      array_end(@last_key.last)
      is_key = false
      @last_key.pop
    when 'Psych::Nodes::Mapping'
      YamlLint.logger.debug { "Mapping: #{n.children}" }
      hash_start(@last_key.last)
      parse_recurse(n)
      hash_end(@last_key.last)
      is_key = false
      @last_key.pop
    end
  end
end