cartodb-4.42/lib/carto/connector/parameters.rb

161 lines
4.3 KiB
Ruby
Raw Normal View History

2024-04-06 13:25:13 +08:00
module Carto
class Connector
# Connector parameters: behaves like a Hash, but:
#
# * keys are case-insensitive for [], slice, except, merge!, etc.
# * original key case is retained by slice, merge, each, map
# * has validation (errors) based on required and optional keys
#
class Parameters
# Parameters can be defined by a Hash or an array of [key, value] pairs
# If required or optional arrays of parameter names are passed,
# then #errors, #valid? use them to check valid parameters
def initialize(params, required: [], optional: [])
params ||= {}
params = if params.respond_to?(:parameters)
params.parameters
else
Hash[params]
end
@params = params.symbolize_keys
@required_parameters = normalized_array(required || [])
@optional_parameters = normalized_array(optional || [])
# TODO: validate definition (no dups...)
@accepted_parameters = @required_parameters + @optional_parameters
end
def [](name)
_k, v = fetch(name)
v
end
def delete(name)
k, _v = fetch(name)
@params.delete(k)
end
def blank?
@params.blank?
end
def present?
@params.present?
end
def slice(*param_names)
normalized_names = normalized_array(param_names)
Parameters.new(@params.select { |name| normalized_key(name).in?(normalized_names) })
end
def except(*param_names)
normalized_names = normalized_array(param_names)
Parameters.new(@params.reject { |name| normalized_key(name).in?(normalized_names) })
end
def merge!(params)
params.each do |new_name, new_value|
old_name, _old_value = fetch(new_name)
@params.delete(old_name) if old_name
@params[new_name.to_sym] = new_value
end
self
end
def reverse_merge!(params)
params.each do |new_name, new_value|
old_name, _old_value = fetch(new_name)
unless old_name
@params[new_name.to_sym] = new_value
end
end
self
end
def parameters
@params
end
def each(&blk)
parameters.map &blk
end
def map(&blk)
# Parameters.new parameters.map(&blk)
# Parameters.new Hash[parameters.map(&blk)].stringify_keys
Parameters.new parameters.map { |k, v|
k, v = blk[k, v]
[k.to_sym, v] if k
}.compact
end
def has?(name)
k, _v = fetch(name)
!!k
end
def normalized_parameters
normalized_hash @params
end
def normalized_names
@params.keys.map { |name| normalized_key(name) }
end
def normalize_parameter_names(names)
names ||= @accepted_parameters
normalized_array(Array(names))
end
def errors(only_for: nil, parameters_term: 'parameters')
errors = []
if @accepted_parameters.present?
invalid_params = normalized_names - @accepted_parameters
missing_parameters = @required_parameters - normalized_names
missing_parameters &= normalize_parameter_names(only_for)
if missing_parameters.present?
errors << "Missing required #{parameters_term} #{missing_parameters * ','}"
end
errors << "Invalid #{parameters_term}: #{invalid_params * ', '}" if invalid_params.present?
end
errors
end
def valid?
errors.empty?
end
def dup
Parameters.new(@params, required: @required_parameters, optional: @optional_parameters)
end
def merge(params)
dup.merge!(params)
end
def reverse_merge(params)
dup.reverse_merge!(params)
end
private
def normalized_key(name)
# we keep the keys as downcase symbols
name && name.to_s.downcase.to_sym
end
def normalized_hash(hash)
Hash[hash.map { |name, value| [normalized_key(name), value] }]
end
def normalized_array(array)
array.map { |name| normalized_key(name) }
end
# returns [actual_name, value]
def fetch(name)
@params.find { |internal_name, _value| internal_name.to_s.casecmp(name.to_s).zero? }
end
end
end
end