class Addressable::Template
Experimental RBS support (using type sampling data from the type_fusion
project).
# sig/addressable/template.rbs class Addressable::Template def initialize: (String pattern) -> void def normalize_keys: (Hash mapping) -> untyped def ordered_variable_defaults: () -> untyped def parse_new_template_pattern: (String pattern, ?nil processor) -> untyped def parse_template_pattern: (String pattern, ?nil processor) -> untyped def partial_expand: (Hash mapping, ?nil processor, ?true normalize_values) -> untyped def variables: () -> untyped end
RFC 6570 (tools.ietf.org/html/rfc6570).
This is an implementation of a URI template based on
#
def ==(template)
-
(TrueClass, FalseClass)
-
Parameters:
-
template
(Object
) -- The Template to compare.
def ==(template) return false unless template.kind_of?(Template) return self.pattern == template.pattern end
def expand(mapping, processor=nil, normalize_values=true)
-
(Addressable::URI)
- The expanded URI template.
Parameters:
-
normalize_values
(Boolean
) -- -
processor
(#validate, #transform
) -- -
mapping
(Hash
) -- The mapping that corresponds to the pattern.
def expand(mapping, processor=nil, normalize_values=true) result = self.pattern.dup mapping = normalize_keys(mapping) result.gsub!( EXPRESSION ) do |capture| transform_capture(mapping, capture, processor, normalize_values) end return Addressable::URI.parse(result) end
def extract(uri, processor=nil)
-
(Hash, NilClass)
-
Parameters:
-
processor
(#restore, #match
) -- -
uri
(Addressable::URI, #to_str
) --
def extract(uri, processor=nil) match_data = self.match(uri, processor) return (match_data ? match_data.mapping : nil) end
def freeze
-
(Addressable::URI)
- The frozen URI object.
def freeze self.variables self.variable_defaults self.named_captures super end
def initialize(pattern)
Experimental RBS support (using type sampling data from the type_fusion
project).
def initialize: (String pattern) -> void
This signature was generated using 5 samples from 2 applications.
-
(Addressable::Template)
- The initialized Template object.
Parameters:
-
pattern
(#to_str
) -- The URI Template pattern.
def initialize(pattern) if !pattern.respond_to?(:to_str) raise TypeError, "Can't convert #{pattern.class} into String." end @pattern = pattern.to_str.dup.freeze end
def inspect
-
(String)
- The Template object's state, as a String.
def inspect sprintf("#<%s:%#0x PATTERN:%s>", self.class.to_s, self.object_id, self.pattern) end
def join_values(operator, return_value)
-
(String)
- The transformed mapped value
Parameters:
-
return_value
(Array
) -- -
operator
(String, Nil
) -- One of the operators from the set
def join_values(operator, return_value) leader = LEADERS.fetch(operator, '') joiner = JOINERS.fetch(operator, ',') case operator when '&', '?' leader + return_value.map{|k,v| if v.is_a?(Array) && v.first =~ /=/ v.join(joiner) elsif v.is_a?(Array) v.map{|inner_value| "#{k}=#{inner_value}"}.join(joiner) else "#{k}=#{v}" end }.join(joiner) when ';' return_value.map{|k,v| if v.is_a?(Array) && v.first =~ /=/ ';' + v.join(";") elsif v.is_a?(Array) ';' + v.map{|inner_value| "#{k}=#{inner_value}"}.join(";") else v && v != '' ? ";#{k}=#{v}" : ";#{k}" end }.join else leader + return_value.map{|k,v| v}.join(joiner) end end
def match(uri, processor=nil)
-
(Hash, NilClass)
-
Parameters:
-
processor
(#restore, #match
) -- -
uri
(Addressable::URI, #to_str
) --
def match(uri, processor=nil) uri = Addressable::URI.parse(uri) unless uri.is_a?(Addressable::URI) mapping = {} # First, we need to process the pattern, and extract the values. expansions, expansion_regexp = parse_template_pattern(pattern, processor) return nil unless uri.to_str.match(expansion_regexp) unparsed_values = uri.to_str.scan(expansion_regexp).flatten if uri.to_str == pattern return Addressable::Template::MatchData.new(uri, self, mapping) elsif expansions.size > 0 index = 0 expansions.each do |expansion| _, operator, varlist = *expansion.match(EXPRESSION) varlist.split(',').each do |varspec| _, name, modifier = *varspec.match(VARSPEC) mapping[name] ||= nil case operator when nil, '+', '#', '/', '.' unparsed_value = unparsed_values[index] name = varspec[VARSPEC, 1] value = unparsed_value value = value.split(JOINERS[operator]) if value && modifier == '*' when ';', '?', '&' if modifier == '*' if unparsed_values[index] value = unparsed_values[index].split(JOINERS[operator]) value = value.inject({}) do |acc, v| key, val = v.split('=') val = "" if val.nil? acc[key] = val acc end end else if (unparsed_values[index]) name, value = unparsed_values[index].split('=') value = "" if value.nil? end end end if processor != nil && processor.respond_to?(:restore) value = processor.restore(name, value) end if processor == nil if value.is_a?(Hash) value = value.inject({}){|acc, (k, v)| acc[Addressable::URI.unencode_component(k)] = Addressable::URI.unencode_component(v) acc } elsif value.is_a?(Array) value = value.map{|v| Addressable::URI.unencode_component(v) } else value = Addressable::URI.unencode_component(value) end end if !mapping.has_key?(name) || mapping[name].nil? # Doesn't exist, set to value (even if value is nil) mapping[name] = value end index = index + 1 end end return Addressable::Template::MatchData.new(uri, self, mapping) else return nil end end
def named_captures
- Api: - private
Returns:
-
(Hash)
- The named captures of the `Regexp` given by {#to_regexp}.
def named_captures self.to_regexp.named_captures end
def normalize_keys(mapping)
Experimental RBS support (using type sampling data from the type_fusion
project).
def normalize_keys: (sample | gem_name | String | gem_version | String | receiver | String | method_name | String | application_name | String | location | String | type_fusion_version | String | parameters | | return_value | Array | String | String mapping) -> untyped
This signature was generated using 1 sample from 1 application.
-
(Hash)
-
Parameters:
-
mapping
(Hash
) -- A mapping hash to normalize
def normalize_keys(mapping) return mapping.inject({}) do |accu, pair| name, value = pair if Symbol === name name = name.to_s elsif name.respond_to?(:to_str) name = name.to_str else raise TypeError, "Can't convert #{name.class} into String." end accu[name] = value accu end end
def normalize_value(value)
-
(Hash, Array, String)
- The normalized values
Parameters:
-
value
(Hash, Array, String
) --
def normalize_value(value) # Handle unicode normalization if value.respond_to?(:to_ary) value.to_ary.map! { |val| normalize_value(val) } elsif value.kind_of?(Hash) value = value.inject({}) { |acc, (k, v)| acc[normalize_value(k)] = normalize_value(v) acc } else value = value.to_s if !value.kind_of?(String) if value.encoding != Encoding::UTF_8 value = value.dup.force_encoding(Encoding::UTF_8) end value = value.unicode_normalize(:nfc) end value end
def ordered_variable_defaults
Experimental RBS support (using type sampling data from the type_fusion
project).
def ordered_variable_defaults: () -> untyped
This signature was generated using 4 samples from 1 application.
def ordered_variable_defaults @ordered_variable_defaults ||= begin expansions, _ = parse_template_pattern(pattern) expansions.flat_map do |capture| _, _, varlist = *capture.match(EXPRESSION) varlist.split(',').map do |varspec| varspec[VARSPEC, 1] end end end end
def parse_new_template_pattern(pattern, processor = nil)
Experimental RBS support (using type sampling data from the type_fusion
project).
def parse_new_template_pattern: (String pattern, ?nil processor) -> untyped
This signature was generated using 6 samples from 2 applications.
-
(Array, Regexp)
-
Parameters:
-
processor
(#match
) -- The template processor to use. -
pattern
(String
) -- The URI template pattern.
def parse_new_template_pattern(pattern, processor = nil) # Escape the pattern. The two gsubs restore the escaped curly braces # back to their original form. Basically, escape everything that isn't # within an expansion. escaped_pattern = Regexp.escape( pattern ).gsub(/\\\{(.*?)\\\}/) do |escaped| escaped.gsub(/\\(.)/, "\\1") end expansions = [] # Create a regular expression that captures the values of the # variables in the URI. regexp_string = escaped_pattern.gsub( EXPRESSION ) do |expansion| expansions << expansion _, operator, varlist = *expansion.match(EXPRESSION) leader = Regexp.escape(LEADERS.fetch(operator, '')) joiner = Regexp.escape(JOINERS.fetch(operator, ',')) combined = varlist.split(',').map do |varspec| _, name, modifier = *varspec.match(VARSPEC) result = processor && processor.respond_to?(:match) ? processor.match(name) : nil if result "(?<#{name}>#{ result })" else group = case operator when '+' "#{ RESERVED }*?" when '#' "#{ RESERVED }*?" when '/' "#{ UNRESERVED }*?" when '.' "#{ UNRESERVED.gsub('\.', '') }*?" when ';' "#{ UNRESERVED }*=?#{ UNRESERVED }*?" when '?' "#{ UNRESERVED }*=#{ UNRESERVED }*?" when '&' "#{ UNRESERVED }*=#{ UNRESERVED }*?" else "#{ UNRESERVED }*?" end if modifier == '*' "(?<#{name}>#{group}(?:#{joiner}?#{group})*)?" else "(?<#{name}>#{group})?" end end end.join("#{joiner}?") "(?:|#{leader}#{combined})" end # Ensure that the regular expression matches the whole URI. regexp_string = "\\A#{regexp_string}\\z" return expansions, Regexp.new(regexp_string) end
def parse_template_pattern(pattern, processor = nil)
Experimental RBS support (using type sampling data from the type_fusion
project).
def parse_template_pattern: (String pattern, ?nil processor) -> untyped
This signature was generated using 5 samples from 2 applications.
-
(Array, Regexp)
-
Parameters:
-
processor
(#match
) -- The template processor to use. -
pattern
(String
) -- The URI template pattern.
def parse_template_pattern(pattern, processor = nil) if processor.nil? && pattern == @pattern @cached_template_parse ||= parse_new_template_pattern(pattern, processor) else parse_new_template_pattern(pattern, processor) end end
def partial_expand(mapping, processor=nil, normalize_values=true)
Experimental RBS support (using type sampling data from the type_fusion
project).
def partial_expand: (sample | gem_name | String | gem_version | String | receiver | String | method_name | String | application_name | String | location | String | type_fusion_version | String | parameters | mapping, ?nil processor, ?true normalize_values) -> untyped
This signature was generated using 2 samples from 1 application.
-
(Addressable::Template)
- The partially expanded URI template.
Parameters:
-
normalize_values
(Boolean
) -- -
processor
(#validate, #transform
) -- -
mapping
(Hash
) -- The mapping that corresponds to the pattern.
def partial_expand(mapping, processor=nil, normalize_values=true) result = self.pattern.dup mapping = normalize_keys(mapping) result.gsub!( EXPRESSION ) do |capture| transform_partial_capture(mapping, capture, processor, normalize_values) end return Addressable::Template.new(result) end
def source
- Api: - private
Returns:
-
(String)
- The source of the `Regexp` given by {#to_regexp}.
def source self.to_regexp.source end
def to_regexp
-
(Regexp)
- A regular expression which should match the template.
def to_regexp _, source = parse_template_pattern(pattern) Regexp.new(source) end
def transform_capture(mapping, capture, processor=nil,
-
(String)
- The expanded expression
Parameters:
-
normalize_values
(Boolean
) -- -
processor
(#validate, #transform
) -- -
capture
(String
) -- -
mapping
(Hash
) -- The mapping to replace captures
def transform_capture(mapping, capture, processor=nil, normalize_values=true) _, operator, varlist = *capture.match(EXPRESSION) return_value = varlist.split(',').inject([]) do |acc, varspec| _, name, modifier = *varspec.match(VARSPEC) value = mapping[name] unless value == nil || value == {} allow_reserved = %w(+ #).include?(operator) # Common primitives where the .to_s output is well-defined if Numeric === value || Symbol === value || value == true || value == false value = value.to_s end length = modifier.gsub(':', '').to_i if modifier =~ /^:\d+/ unless (Hash === value) || value.respond_to?(:to_ary) || value.respond_to?(:to_str) raise TypeError, "Can't convert #{value.class} into String or Array." end value = normalize_value(value) if normalize_values if processor == nil || !processor.respond_to?(:transform) # Handle percent escaping if allow_reserved encode_map = Addressable::URI::CharacterClasses::RESERVED + Addressable::URI::CharacterClasses::UNRESERVED else encode_map = Addressable::URI::CharacterClasses::UNRESERVED end if value.kind_of?(Array) transformed_value = value.map do |val| if length Addressable::URI.encode_component(val[0...length], encode_map) else Addressable::URI.encode_component(val, encode_map) end end unless modifier == "*" transformed_value = transformed_value.join(',') end elsif value.kind_of?(Hash) transformed_value = value.map do |key, val| if modifier == "*" "#{ Addressable::URI.encode_component( key, encode_map) }=#{ Addressable::URI.encode_component( val, encode_map) }" else "#{ Addressable::URI.encode_component( key, encode_map) },#{ Addressable::URI.encode_component( val, encode_map) }" end end unless modifier == "*" transformed_value = transformed_value.join(',') end else if length transformed_value = Addressable::URI.encode_component( value[0...length], encode_map) else transformed_value = Addressable::URI.encode_component( value, encode_map) end end end # Process, if we've got a processor 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 normalize_values transformed_value = normalize_value(transformed_value) end end end acc << [name, transformed_value] end acc end return "" if return_value.empty? join_values(operator, return_value) end
def transform_partial_capture(mapping, capture, processor = nil,
-
(String)
- The expanded expression
Parameters:
-
normalize_values
(Boolean
) -- -
processor
(#validate, #transform
) -- -
capture
(String
) -- -
mapping
(Hash
) --
def transform_partial_capture(mapping, capture, processor = nil, normalize_values = true) _, operator, varlist = *capture.match(EXPRESSION) vars = varlist.split(",") if operator == "?" # partial expansion of form style query variables sometimes requires a # slight reordering of the variables to produce a valid url. first_to_expand = vars.find { |varspec| _, name, _ = *varspec.match(VARSPEC) mapping.key?(name) && !mapping[name].nil? } vars = [first_to_expand] + vars.reject {|varspec| varspec == first_to_expand} if first_to_expand end vars. inject("".dup) do |acc, varspec| _, name, _ = *varspec.match(VARSPEC) next_val = if mapping.key? name transform_capture(mapping, "{#{operator}#{varspec}}", processor, normalize_values) else "{#{operator}#{varspec}}" end # If we've already expanded at least one '?' operator with non-empty # value, change to '&' operator = "&" if (operator == "?") && (next_val != "") acc << next_val end end
def variable_defaults
-
(Hash)
- Mapping of template variables to their defaults
def variable_defaults @variable_defaults ||= Hash[*ordered_variable_defaults.reject { |k, v| v.nil? }.flatten] end
def variables
Experimental RBS support (using type sampling data from the type_fusion
project).
def variables: () -> untyped
This signature was generated using 4 samples from 1 application.
-
(Array)
- The variables present in the template's pattern.
def variables @variables ||= ordered_variable_defaults.map { |var, val| var }.uniq end