161 lines
4.3 KiB
Ruby
161 lines
4.3 KiB
Ruby
|
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
|