class Aws::Templates::Utils::Options

Options hash-like class

Implements the core mechanism of hash lookup, merge, and transformation.

It supports nested hash lookup with index function and wildcard hash definitions on any level of nested hierarchy so you can define fallback values right in the input hash. The algorithm will try to select the best combination if it doesn't see the exact match during lookup.

Public Class Methods

new(*structures) click to toggle source

Initialize Options with list of recursive structures (See Options#recursive?)

# File lib/aws/templates/utils/options.rb, line 225
def initialize(*structures)
  structures.reject(&:nil?).each { |container| merge!(container) }
end

Public Instance Methods

[](*path) click to toggle source

Get a parameter from resulting hash or any nested part of it

The method can access resulting hash as a tree performing traverse as needed. Also, it handles nil-pointer situations correctly so you will get no exception but just 'nil' even when the whole branch you're trying to access don't exist or contains non-hash value somewhere in the middle. Also, the method recognizes asterisk (*) hash records which is an analog of match-all or default values for some sub-branch.

  • path - an array representing path of the value in the nested

    hash
    

Example

opts = Options.new(
  'a' => {
    'b' => 'c',
    '*' => { '*' => 2 }
  },
  'd' => 1
)
opts.to_hash # => { 'a' => { 'b' => 'c', '*' => { '*' => 2 } }, 'd' => 1 }
opts['a'] # => Options.new('b' => 'c', '*' => { '*' => 2 })
opts['a', 'b'] # => 'c'
opts['d', 'e'] # => nil
# multi-level wildcard match
opts['a', 'z', 'r'] # => 2
# File lib/aws/templates/utils/options.rb, line 58
def [](*path)
  structures.reverse_each.inject(nil) do |memo, container|
    ret = begin
      Utils.lookup(container, path.dup)
    rescue Exception::OptionValueDeleted, Exception::OptionScalarOnTheWay
      # we discovered that this layer either have value deleted or parent was overriden
      # by a scalar. Either way we just return what we have in the memo
      break memo
    end

    # if current container doesn't have this value - let's go to the next iteration
    next memo if ret.nil?

    # if found value is a scalar then either we return it as is or return memo
    # if memo is not nil it means that we've found hierarchical objects before
    break(memo.nil? ? ret : memo) unless Utils.recursive?(ret)

    # value is not a scalar. it means we need to keep merging them
    memo.nil? ? Options.new(ret) : memo.unshift_layer!(ret)
  end
end
[]=(*path_and_value) click to toggle source

Set the parameter with the path to the value

The method can access resulting hash as a tree performing traverse as needed. When stubled uponAlso, it handles non-existent keys correctly creating sub-branches as necessary. If a non-hash and non-nil value discovered in the middle of the path, an exception will be thrown. The method doesn't give any special meaning to wildcards keys so you can set wildcard parameters also.

  • path - an array representing path of the value in the nested

    hash
    
  • value - value to set the parameter to

Example

opts = Options.new({})
opts.to_hash # => {}
opts['a', 'b'] = 'c'
opts['a', '*', '*'] = 2
opts['d'] = 1
opts.to_hash # => { 'a' => { 'b' => 'c', '*' => { '*' => 2 } }, 'd' => 1 }
# File lib/aws/templates/utils/options.rb, line 120
def []=(*path_and_value)
  value = path_and_value.pop
  path = path_and_value
  dirty!.cow! # mooo
  Utils.set_recursively(structures.last, value, path)
end
compact!() click to toggle source

Squash all layers into one

Options is designed with very specific goal to be memory-friendly and re-use merged objects as immutable layers. However, after some particular threshold of layer's stack size, performance of index operations can suffer significantly. To mitigate this user can use the method to squash all layers into one aggregated hash.

The method performs in-place stack modification

# File lib/aws/templates/utils/options.rb, line 247
def compact!
  @structures = [to_hash]
  self
end
cow!() click to toggle source
# File lib/aws/templates/utils/options.rb, line 270
def cow!
  unless @is_cowed
    structures << {}
    @is_cowed = true
  end

  self
end
delete(*path) click to toggle source

Delete a branch

Delete a branch in the options. Rather than deleting it from hash, the path is assigned with special marker that it was deleted. It helps avoid hash recalculation leading to memory thrashing simultaneously maintaining semantics close to Hash#delete

# File lib/aws/templates/utils/options.rb, line 133
def delete(*path)
  self[*path] = Utils::DELETED_MARKER
end
dependencies() click to toggle source
# File lib/aws/templates/utils/options.rb, line 84
def dependencies
  # rubocop:disable Style/SymbolProc
  # Refinements don't support dynamic dispatch yet. So, symbolic methods don't work
  memoize(:dependencies) do
    select_recursively { |obj| obj.dependency? }
      .inject(::Set.new) { |acc, elem| acc.merge(elem.dependencies) }
  end
  # rubocop:enable Style/SymbolProc
end
dependency?() click to toggle source
# File lib/aws/templates/utils/options.rb, line 80
def dependency?
  !dependencies.empty?
end
dup() click to toggle source

Duplicate the options

Duplicates the object itself and puts another layer of hash map. All original hash maps are not touched if the duplicate is modified.

# File lib/aws/templates/utils/options.rb, line 234
def dup
  Options.new(*structures)
end
filter() { |self)| ... } click to toggle source

Filter options

Filter options with provided Proc. The proc should accept one parameter satisfying “recursive” contract. See Utils.recursive

# File lib/aws/templates/utils/options.rb, line 219
def filter
  Options.new(yield self)
end
include?(k) click to toggle source

If top-level key exists

Checks if top-level key exists. Deleted branches are excluded.

# File lib/aws/templates/utils/options.rb, line 181
def include?(k)
  found = structures.reverse_each.find { |container| container.include?(k) }
  !found.nil? && (found[k] != Utils::DELETED_MARKER)
end
keys() click to toggle source

Top-level keys

Produces a list of top-level keys from all layers. Deleted branches are not included.

# File lib/aws/templates/utils/options.rb, line 165
def keys
  memoize(:keys) do
    structures
      .each_with_object(::Set.new) do |container, keyset|
        container.keys.each do |k|
          container[k] == Utils::DELETED_MARKER ? keyset.delete(k) : keyset.add(k)
        end
      end
      .to_a
  end
end
merge(other) click to toggle source

Merge Options with object

Create new Options object which is a merge of the target Options instance with an object. The object must be “recusrsive” meaning it should satisfy minimum contract for “recursive”. See Utils::recursive? for details

# File lib/aws/templates/utils/options.rb, line 192
def merge(other)
  self.class.new(*structures, other)
end
merge!(other) click to toggle source

Merge Options with object in-place

Put the passed object as the top layer of the current instance. The object must be “recursive” meaning it should satisfy minimum contract for “recursive”. See Utils::recursive? for details

# File lib/aws/templates/utils/options.rb, line 202
def merge!(other)
  raise Exception::OptionShouldBeRecursive.new(other) unless Utils.recursive?(other)

  if other.is_a?(self.class)
    structures.concat(other.structures)
  else
    structures << other
  end

  dirty!
end
select_recursively(&blk) click to toggle source
# File lib/aws/templates/utils/options.rb, line 94
def select_recursively(&blk)
  Utils.select_recursively(self, &blk)
end
structures() click to toggle source
# File lib/aws/templates/utils/options.rb, line 25
def structures
  @structures ||= []
end
to_filter() click to toggle source

Create filter

Gets hash representstion of the Options instance and transforms it to filter

# File lib/aws/templates/utils/options.rb, line 157
def to_filter
  to_hash.to_filter
end
to_hash() click to toggle source

Transforms to hash object

Produces a hash out of Options object merging COW layers iteratively and calculating them recursively.

# File lib/aws/templates/utils/options.rb, line 142
def to_hash
  memoize(:to_hash) do
    _process_hashed(structures.inject({}) { |acc, elem| Utils.merge(acc, elem) })
  end
end
to_recursive() click to toggle source

The class already supports recursive concept so return self

# File lib/aws/templates/utils/options.rb, line 149
def to_recursive
  self
end
unshift_layer!(layer) click to toggle source

Put the layer to the bottom of the stack

However it doesn't resemble exact semantics, the method is similar to reverse_merge! from ActiveSupport. It puts the “recursive” object passed to the bottom of the layer stack of the Options instance effectively making it the least prioritized layer.

# File lib/aws/templates/utils/options.rb, line 258
def unshift_layer!(layer)
  raise Exception::OptionShouldBeRecursive.new(layer) unless Utils.recursive?(layer)

  if layer.is_a?(self.class)
    layer.structures.dup.concat(structures)
  else
    structures.unshift(layer)
  end

  dirty!
end