def self.expand_template(pattern, mapping, processor=nil)
result = pattern.dup
reserved = Addressable::URI::CharacterClasses::RESERVED
unreserved = Addressable::URI::CharacterClasses::UNRESERVED
anything = reserved + unreserved
operator_expansion =
/\{-([a-zA-Z]+)\|([#{anything}]+)\|([#{anything}]+)\}/
variable_expansion = /\{([#{anything}]+?)(=([#{anything}]+))?\}/
transformed_mapping = mapping.inject({}) do |accu, pair|
name, value = pair
unless value.respond_to?(:to_ary) || value.respond_to?(:to_str)
raise TypeError,
"Can't convert #{value.class} into String or Array."
end
value =
value.respond_to?(:to_ary) ? value.to_ary : value.to_str
if value.kind_of?(Array)
value.map! { |val| Addressable::IDNA.unicode_normalize_kc(val) }
else
value = Addressable::IDNA.unicode_normalize_kc(value)
end
if processor == nil || !processor.respond_to?(:transform)
if value.kind_of?(Array)
transformed_value = value.map do |val|
self.encode_component(
val, Addressable::URI::CharacterClasses::UNRESERVED)
end
else
transformed_value = self.encode_component(
value, Addressable::URI::CharacterClasses::UNRESERVED)
end
end
if processor != nil
if processor.respond_to?(:validate)
if !processor.validate(name, value)
display_value = value.kind_of?(Array) ? value.inspect : value
raise InvalidTemplateValueError,
"#{name}=#{display_value} is an invalid template value."
end
end
if processor.respond_to?(:transform)
transformed_value = processor.transform(name, value)
if transformed_value.kind_of?(Array)
transformed_value.map! do |val|
Addressable::IDNA.unicode_normalize_kc(val)
end
else
transformed_value =
Addressable::IDNA.unicode_normalize_kc(transformed_value)
end
end
end
accu[name] = transformed_value
accu
end
result.gsub!(
/#{operator_expansion}|#{variable_expansion}/
) do |capture|
if capture =~ operator_expansion
operator, argument, variables, default_mapping =
parse_template_expansion(capture, transformed_mapping)
expand_method = "expand_#{operator}_operator"
if ([expand_method, expand_method.to_sym] & private_methods).empty?
raise InvalidTemplateOperatorError,
"Invalid template operator: #{operator}"
else
send(expand_method.to_sym, argument, variables, default_mapping)
end
else
varname, _, vardefault = capture.scan(/^\{(.+?)(=(.*))?\}$/)[0]
transformed_mapping[varname] || vardefault
end
end
return Addressable::URI.parse(result)
end