162 lines
4.4 KiB
Ruby
162 lines
4.4 KiB
Ruby
require_relative './providers/generic_odbc'
|
|
require_relative './providers/mysql'
|
|
require_relative './providers/postgresql'
|
|
require_relative './providers/sqlserver'
|
|
require_relative './providers/hive'
|
|
require_relative './providers/pg_fdw'
|
|
|
|
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 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 errors(only: nil, parameters_term: 'parameters')
|
|
errors = []
|
|
if @accepted_parameters.present?
|
|
invalid_params = normalized_names - @accepted_parameters
|
|
missing_parameters = @required_parameters - normalized_names
|
|
if only.present?
|
|
only = normalized_array(Array(only))
|
|
missing_parameters &= only
|
|
end
|
|
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_attributes, optional: @optional_attributes)
|
|
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
|