module Puppet::ResourceApi::DataTypeHandling
This module is used to handle data inside types, contains methods for munging and validation of the type values.
Public Class Methods
Source
# File lib/puppet/resource_api/data_type_handling.rb, line 147 def self.ambiguous_error_msg(error_msg_prefix, value, type) "#{error_msg_prefix} #{value.inspect} is not unabiguously convertable to " \ "#{type}" end
Returns ambiguous error message based on ‘error_msg_prefix`, `value` and `type`. @param type the type to check against @param value the value to clean @param error_msg_prefix a prefix for the error messages @private
Source
# File lib/puppet/resource_api/data_type_handling.rb, line 130 def self.boolean_munge(value) case value when 'true', :true # rubocop:disable Lint/BooleanSymbol true when 'false', :false # rubocop:disable Lint/BooleanSymbol false else value end end
Returns correct boolean ‘value` based on one specified in `type`. @param value the value to boolean munge @private
Source
# File lib/puppet/resource_api/data_type_handling.rb, line 23 def self.mungify(type, value, error_msg_prefix, unpack_strings = false) cleaned_value = mungify_core( type, value, error_msg_prefix, unpack_strings, ) validate(type, cleaned_value, error_msg_prefix) cleaned_value end
This method handles translating values from the runtime environment to the expected types for the provider with validation. When being called from ‘puppet resource`, it tries to transform the strings from the command line into their expected ruby representations, e.g. `“2”` (a string), will be transformed to `2` (the number) if (and only if) the target `type` is `Integer`. Additionally this function also validates that the passed in (and optionally transformed) value matches the specified type. @param type the type to check/clean against. @param value the value to clean @param error_msg_prefix a prefix for the error messages @param unpack_strings unpacking of strings for migrating off legacy type @return [type] the cleaned value
Source
# File lib/puppet/resource_api/data_type_handling.rb, line 45 def self.mungify_core(type, value, error_msg_prefix, unpack_strings = false) if unpack_strings # When the provider is exercised from the `puppet resource` CLI, we need # to unpack strings into the correct types, e.g. "1" (a string) # to 1 (an integer) cleaned_value, error_msg = try_mungify(type, value, error_msg_prefix) raise Puppet::ResourceError, error_msg if error_msg cleaned_value elsif value == :false # rubocop:disable Lint/BooleanSymbol # work around https://tickets.puppetlabs.com/browse/PUP-2368 false elsif value == :true # rubocop:disable Lint/BooleanSymbol # work around https://tickets.puppetlabs.com/browse/PUP-2368 true else # Every other time, we can use the values as is value end end
This is core method used in mungify which handles translating values to expected cleaned type values, result is not validated. @param type the type to check/clean against. @param value the value to clean @param error_msg_prefix a prefix for the error messages @param unpack_strings unpacking of strings for migrating off legacy type @return [type] the cleaned value @raise [Puppet::ResourceError] if ‘value` could not be parsed into `type` @private
Source
# File lib/puppet/resource_api/data_type_handling.rb, line 198 def self.parse_puppet_type(attr_name, type) Puppet::Pops::Types::TypeParser.singleton.parse(type) rescue Puppet::ParseErrorWithIssue => e raise Puppet::DevError, "The type of the `#{attr_name}` attribute " \ "`#{type}` could not be parsed: #{e.message}" rescue Puppet::ParseError => e raise Puppet::DevError, "The type of the `#{attr_name}` attribute " \ "`#{type}` is not recognised: #{e.message}" end
Source
# File lib/puppet/resource_api/data_type_handling.rb, line 71 def self.try_mungify(type, value, error_msg_prefix) case type when Puppet::Pops::Types::PArrayType if value.is_a? Array conversions = value.map do |v| try_mungify(type.element_type, v, error_msg_prefix) end # only convert the values if none failed. otherwise fall through and # rely on puppet to render a proper error if conversions.all? { |c| c[1].nil? } value = conversions.map { |c| c[0] } end end when Puppet::Pops::Types::PBooleanType value = boolean_munge(value) when Puppet::Pops::Types::PIntegerType, Puppet::Pops::Types::PFloatType, Puppet::Pops::Types::PNumericType if value.is_a?(String) && (value.match?(%r{^-?\d+$}) || value.match?(Puppet::Pops::Patterns::NUMERIC)) value = Puppet::Pops::Utils.to_n(value) end when Puppet::Pops::Types::PEnumType, Puppet::Pops::Types::PStringType, Puppet::Pops::Types::PPatternType value = value.to_s if value.is_a? Symbol when Puppet::Pops::Types::POptionalType return value.nil? ? [nil, nil] : try_mungify(type.type, value, error_msg_prefix) when Puppet::Pops::Types::PVariantType # try converting to anything except string first string_type = type.types.find { |t| t.is_a? Puppet::Pops::Types::PStringType } conversion_results = (type.types - [string_type]).map do |t| try_mungify(t, value, error_msg_prefix) end # only consider valid results conversion_results = conversion_results.select { |r| r[1].nil? }.to_a # use the conversion result if unambiguous return conversion_results[0] if conversion_results.length == 1 # return an error if ambiguous if conversion_results.length > 1 return [nil, ambiguous_error_msg(error_msg_prefix, value, type)] end # try to interpret as string return try_mungify(string_type, value, error_msg_prefix) if string_type # fall through to default handling end error_msg = try_validate(type, value, error_msg_prefix) return [nil, error_msg] if error_msg # an error [value, nil] # match end
Recursive implementation part of mungify_core. Uses a multi-valued return value to avoid excessive exception throwing for regular usage. @return [Array] if the mungify worked, the first element is the cleaned
value, and the second element is nil. If the mungify failed, the first element is nil, and the second element is an error message
@private
Source
# File lib/puppet/resource_api/data_type_handling.rb, line 169 def self.try_validate(type, value, error_msg_prefix) return nil if type.instance?(value) # an error :-( inferred_type = Puppet::Pops::Types::TypeCalculator.infer_set(value) error_msg = Puppet::Pops::Types::TypeMismatchDescriber.new.describe_mismatch( error_msg_prefix, type, inferred_type, ) error_msg end
Tries to validate the ‘value` against the specified `type`. @param type the type to check against @param value the value to clean @param error_msg_prefix a prefix for the error messages @return [String, nil] a error message indicating the problem, or `nil` if the value was valid. @private
Source
# File lib/puppet/resource_api/data_type_handling.rb, line 158 def self.validate(type, value, error_msg_prefix) error_msg = try_validate(type, value, error_msg_prefix) raise Puppet::ResourceError, error_msg if error_msg end
Validates the ‘value` against the specified `type`. @param type the type to check against @param value the value to clean @param error_msg_prefix a prefix for the error messages @raise [Puppet::ResourceError] if `value` is not of type `type` @private
Source
# File lib/puppet/resource_api/data_type_handling.rb, line 182 def self.validate_ensure(definition) return unless definition[:attributes].key? :ensure options = definition[:attributes][:ensure] type = parse_puppet_type(:ensure, options[:type]) # If the type is wrapped in optional, then check it unwrapped: type = type.type if type.is_a?(Puppet::Pops::Types::POptionalType) return if type.is_a?(Puppet::Pops::Types::PEnumType) && type.values.sort == %w[absent present].sort # If Variant was used instead, then construct one and use the built-in type comparison: variant_type = Puppet::Pops::Types::TypeParser.singleton.parse('Variant[Undef, Enum[present, absent]]') return if variant_type == type raise Puppet::DevError, '`:ensure` attribute must have a type of: `Enum[present, absent]` or `Optional[Enum[present, absent]]`' end