Module: Aws::Templates::Utils

Includes:
Exception
Defined in:
lib/aws/templates/utils.rb,
lib/aws/templates/utils/default.rb,
lib/aws/templates/utils/guarded.rb,
lib/aws/templates/utils/options.rb,
lib/aws/templates/utils/as_named.rb,
lib/aws/templates/utils/autoload.rb,
lib/aws/templates/utils/memoized.rb,
lib/aws/templates/utils/dependent.rb,
lib/aws/templates/utils/recursive.rb,
lib/aws/templates/utils/dependency.rb,
lib/aws/templates/utils/late_bound.rb,
lib/aws/templates/utils/inheritable.rb,
lib/aws/templates/utils/inspectable.rb,
lib/aws/templates/utils/parametrized.rb,
lib/aws/templates/utils/contextualized.rb,
lib/aws/templates/utils/artifact_storage.rb,
lib/aws/templates/utils/parametrized/getter.rb,
lib/aws/templates/utils/parametrized/nested.rb,
lib/aws/templates/utils/contextualized/filter.rb,
lib/aws/templates/utils/dependency/refinements.rb,
lib/aws/templates/utils/parametrized/constraint.rb,
lib/aws/templates/utils/parametrized/getter/dsl.rb,
lib/aws/templates/utils/parametrized/getter/path.rb,
lib/aws/templates/utils/contextualized/filter/add.rb,
lib/aws/templates/utils/contextualized/filter/dsl.rb,
lib/aws/templates/utils/parametrized/getter/as_is.rb,
lib/aws/templates/utils/parametrized/getter/value.rb,
lib/aws/templates/utils/contextualized/filter/copy.rb,
lib/aws/templates/utils/contextualized/refinements.rb,
lib/aws/templates/utils/parametrized/getter/one_of.rb,
lib/aws/templates/utils/contextualized/filter/chain.rb,
lib/aws/templates/utils/contextualized/filter/proxy.rb,
lib/aws/templates/utils/parametrized/constraint/dsl.rb,
lib/aws/templates/utils/parametrized/transformation.rb,
lib/aws/templates/utils/contextualized/filter/remove.rb,
lib/aws/templates/utils/contextualized/filter/scoped.rb,
lib/aws/templates/utils/parametrized/constraint/enum.rb,
lib/aws/templates/utils/contextualized/filter/identity.rb,
lib/aws/templates/utils/contextualized/filter/override.rb,
lib/aws/templates/utils/parametrized/constraint/all_of.rb,
lib/aws/templates/utils/parametrized/constraint/matches.rb,
lib/aws/templates/utils/parametrized/constraint/not_nil.rb,
lib/aws/templates/utils/parametrized/transformation/dsl.rb,
lib/aws/templates/utils/parametrized/constraint/requires.rb,
lib/aws/templates/utils/parametrized/constraint/condition.rb,
lib/aws/templates/utils/parametrized/transformation/as_hash.rb,
lib/aws/templates/utils/parametrized/transformation/as_list.rb,
lib/aws/templates/utils/parametrized/transformation/as_chain.rb,
lib/aws/templates/utils/parametrized/transformation/as_module.rb,
lib/aws/templates/utils/parametrized/transformation/as_object.rb,
lib/aws/templates/utils/parametrized/transformation/as_string.rb,
lib/aws/templates/utils/parametrized/transformation/as_boolean.rb,
lib/aws/templates/utils/parametrized/transformation/as_integer.rb,
lib/aws/templates/utils/parametrized/transformation/as_rendered.rb,
lib/aws/templates/utils/parametrized/constraint/depends_on_value.rb,
lib/aws/templates/utils/parametrized/getter/as_instance_variable.rb,
lib/aws/templates/utils/parametrized/constraint/satisfies_condition.rb,
lib/aws/templates/utils/contextualized/filter/recursive_schema_filter.rb

Overview

Variable utility functions used through the code.

Defines auxiliary functions set and serves as the namespace for the framework modules.

Defined Under Namespace

Modules: AsNamed, Autoload, Contextualized, Default, Dependent, Guarded, Inheritable, Inspectable, LateBound, Memoized, Parametrized, Recursive Classes: ArtifactStorage, Dependency, Lazy, Options

Constant Summary collapse

RECURSIVE_METHODS =
%i[keys [] include?].freeze
PARAMETRIZED_METHODS =
[:parameter_names].freeze
DELETED_MARKER =

Deletion marker

Since Options use lazy merging (effectively keeping all hashes passed during initialization immutable) and iterates through all of them during value look-up, we need a way of marking some branch as deleted when deletion operation is invoked on an Options object. So, the algorithm marks branch with the object to stop iteration during look-up.

Object.new
PATH_REGEXP =
Regexp.compile('::|[.]|/')

Class Method Summary collapse

Class Method Details

._lookup_recursively(value, path) ⇒ Object



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/aws/templates/utils.rb', line 181

def self._lookup_recursively(value, path)
  current_key = path.shift

  # is there a value defined for the key in the current recursive structure?
  if value.include?(current_key)
    # yes - look-up the rest of the path
    return_value = lookup(value[current_key], path)
    # if value was still not found - resorting to wildcard path
    return_value.nil? ? lookup(value[:*], path) : return_value
  elsif value.include?(:*)
    # if there is no key but the recursive has a default value defined - dive into the
    # wildcard branch
    lookup(value[:*], path)
  end
end

._merge_back(result, b) ⇒ Object



119
120
121
122
123
# File 'lib/aws/templates/utils.rb', line 119

def self._merge_back(result, b)
  b.keys
   .reject { |k| result.include?(k) }
   .each_with_object(result) { |k, res| res[k] = hashify(b[k]) }
end

._merge_forward(a, b, blk) ⇒ Object



113
114
115
116
117
# File 'lib/aws/templates/utils.rb', line 113

def self._merge_forward(a, b, blk)
  a.keys.each_with_object({}) do |k, hsh|
    hsh[k] = b[k].nil? ? hashify(a[k]) : merge(a[k], b[k], &blk)
  end
end

.deep_dup(original) ⇒ Object

Duplicate hash recursively

Duplicate the hash and all nested hashes recursively



73
74
75
76
77
78
79
80
# File 'lib/aws/templates/utils.rb', line 73

def self.deep_dup(original)
  return original unless Utils.hashable?(original)

  duplicate = original.dup.to_hash
  duplicate.each_pair { |k, v| duplicate[k] = deep_dup(v) }

  duplicate
end

.hashable?(obj) ⇒ Boolean

If object is hashable

Checks if object can be transformed into Hash

Returns:

  • (Boolean)


46
47
48
# File 'lib/aws/templates/utils.rb', line 46

def self.hashable?(obj)
  obj.respond_to?(:to_hash)
end

.hashify(v) ⇒ Object



125
126
127
128
# File 'lib/aws/templates/utils.rb', line 125

def self.hashify(v)
  return v unless Utils.recursive?(v)
  v.keys.each_with_object({}) { |k, hsh| hsh[k] = hashify(v[k]) }
end

.list?(obj) ⇒ Boolean

If object is a list

Checks if object can be transformed into Array

Returns:

  • (Boolean)


65
66
67
# File 'lib/aws/templates/utils.rb', line 65

def self.list?(obj)
  obj.respond_to?(:to_ary)
end

.lookup(value, path) ⇒ Object

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


168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/aws/templates/utils.rb', line 168

def self.lookup(value, path)
  # we stop lookup and return nil if nil is encountered
  return if value.nil?
  # value was deleted in this layer
  raise Exception::OptionValueDeleted.new(path) if value == DELETED_MARKER
  # we reached our target! returning it
  return value if path.nil? || path.empty?
  # we still have some part of path to traverse but scalar was found
  raise Exception::OptionScalarOnTheWay.new(value, path) if Utils.scalar?(value)

  _lookup_recursively(value, path.dup)
end

.lookup_module(str) ⇒ Object



240
241
242
243
244
245
246
247
248
# File 'lib/aws/templates/utils.rb', line 240

def self.lookup_module(str)
  target = str.split(PATH_REGEXP)
              .inject(::Object.lazy) { |acc, elem| acc.const_get(elem.modulize) }
              .reduce

  raise "#{str} == #{target} which is not a module" unless target.is_a?(Module)

  target
end

.merge(a, b, &blk) ⇒ Object

Merges two nested hashes

The core element of the whole framework. The principle is simple: both arguments are transformed to hashes if they support :to_hash method, the resulting hashes are merged with the standard method with creating a new hash. Second element takes preference if the function reached bottom level of recursion with only scalars left.

Raises ArgumentError if a and b have incompatible types hence they can’t be merged

Example

Options.merge('a', 'b') # => 'b'
Options.merge({'1'=>'2'}, {'3'=>'4'}) # => {'1'=>'2', '3'=>'4'}
Options.merge(
  { '1' => { '2' => '3' } },
  { '1' => { '4' => '5' } }
) # => { '1' => { '2' => '3', '4'=>'5' } }

Recursively merge two “recursive” objects PS: Yes I know that there is “merge” method for hashes.



105
106
107
108
109
110
111
# File 'lib/aws/templates/utils.rb', line 105

def self.merge(a, b, &blk)
  unless Utils.recursive?(a) && Utils.recursive?(b)
    return hashify(blk.nil? ? b : blk.call(a, b))
  end

  _merge_back(_merge_forward(a, b, blk), b)
end

.parametrized?(obj) ⇒ Boolean

If the object is “parametrized”

Checks if object satisfies “parametrized” concept. See PARAMETRIZED_METHODS for the list of methods

Returns:

  • (Boolean)


57
58
59
# File 'lib/aws/templates/utils.rb', line 57

def self.parametrized?(obj)
  PARAMETRIZED_METHODS.all? { |m| obj.respond_to?(m) }
end

.recursive?(obj) ⇒ Boolean

If the object is “recursive”

Checks if object satisfies “recursive” concept. See RECURSIVE_METHODS for the list of methods

Returns:

  • (Boolean)


30
31
32
# File 'lib/aws/templates/utils.rb', line 30

def self.recursive?(obj)
  RECURSIVE_METHODS.all? { |m| obj.respond_to?(m) }
end

.scalar?(obj) ⇒ Boolean

If the object is “scalar”

Checks if object satisfies “scalar” concept.

Returns:

  • (Boolean)


38
39
40
# File 'lib/aws/templates/utils.rb', line 38

def self.scalar?(obj)
  !obj.respond_to?(:[])
end

.select_recursively(container, &blk) ⇒ Object

Select object recursively

Selects objects recursively from the specified container with specified block predicate.

  • container - container to recursively select items from

  • blk - the predicate



227
228
229
230
231
232
233
234
235
236
# File 'lib/aws/templates/utils.rb', line 227

def self.select_recursively(container, &blk)
  container.keys.each_with_object([]) do |k, collection|
    value = container[k]
    if blk.call(value)
      collection << value
    elsif Utils.recursive?(value)
      collection.concat(select_recursively(value, &blk))
    end
  end
end

.set_recursively(container, value, path) ⇒ Object

Sets a value in hierarchy

Sets a path in a nested recursive hash structure to the specified value

  • container - container with the specified path

  • value - the value to set the path to

  • path - path containing the target value



205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/aws/templates/utils.rb', line 205

def self.set_recursively(container, value, path)
  last_key = path.pop

  last_branch = path.inject(container) do |obj, current_key|
    raise Exception::OptionScalarOnTheWay.new(obj, path) unless Utils.recursive?(obj)
    if obj.include?(current_key)
      obj[current_key]
    else
      obj[current_key] = {}
    end
  end

  last_branch[last_key] = value
end