This is an implementation of a URI template based on <a href="URI">tinyurl.com/uritemplatedraft03">URI Template draft 03</a>.
@return [String] The Template object's pattern.
Creates a new Addressable::Template
object.
@param [to_str] pattern The URI Template pattern.
@return [Addressable::Template] The initialized Template object.
# File lib/addressable/template.rb, line 122 def initialize(pattern) if !pattern.respond_to?(:to_str) raise TypeError, "Can't convert #{pattern.class} into String." end @pattern = pattern.to_str.freeze end
Expands a URI template into a full URI.
@param [Hash] mapping The mapping that corresponds to the pattern. @param [validate, transform] processor
An optional processor object may be supplied.
The object should respond to either the validate
or
transform
messages or both. Both the validate
and
transform
methods should take two parameters:
name
and value
. The validate
method
should return true
or false
; true
if
the value of the variable is valid, false
otherwise. An
InvalidTemplateValueError
exception will be raised if the
value is invalid. The transform
method should return the
transformed variable value as a String
. If a
transform
method is used, the value will not be percent
encoded automatically. Unicode normalization will be performed both before
and after sending the value to the transform method.
@return [Addressable::URI] The expanded URI template.
@example
class ExampleProcessor def self.validate(name, value) return !!(value =~ %r^[\w ]+$/) if name == "query" return true end def self.transform(name, value) return value.gsub(%r /, "+") if name == "query" return value end end Addressable::Template.new( "http://example.com/search/{query}/" ).expand( {"query" => "an example search query"}, ExampleProcessor ).to_str #=> "http://example.com/search/an+example+search+query/" Addressable::Template.new( "http://example.com/search/{-list|+|query}/" ).expand( {"query" => "an example search query".split(" ")} ).to_str #=> "http://example.com/search/an+example+search+query/" Addressable::Template.new( "http://example.com/search/{query}/" ).expand( {"query" => "bogus!"}, ExampleProcessor ).to_str #=> Addressable::Template::InvalidTemplateValueError
# File lib/addressable/template.rb, line 453 def expand(mapping, processor=nil) result = self.pattern.dup transformed_mapping = transform_mapping(mapping, processor) result.gsub!( %r#{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(%r^\{(.+?)(=(.*))?\}$/)[0] transformed_mapping[varname] || vardefault end end return Addressable::URI.parse(result) end
Extracts a mapping from the URI using a URI Template pattern.
@param [Addressable::URI, to_str] uri
The URI to extract from.
@param [restore, match] processor
A template processor object may optionally be supplied. The object should respond to either the <tt>restore</tt> or <tt>match</tt> messages or both. The <tt>restore</tt> method should take two parameters: `[String] name` and `[String] value`. The <tt>restore</tt> method should reverse any transformations that have been performed on the value to ensure a valid URI. The <tt>match</tt> method should take a single parameter: `[String] name`. The <tt>match</tt> method should return a <tt>String</tt> containing a regular expression capture group for matching on that particular variable. The default value is `".*?"`. The <tt>match</tt> method has no effect on multivariate operator expansions.
@return [Hash, NilClass]
The <tt>Hash</tt> mapping that was extracted from the URI, or <tt>nil</tt> if the URI didn't match the template.
@example
class ExampleProcessor def self.restore(name, value) return value.gsub(%r\+/, " ") if name == "query" return value end def self.match(name) return ".*?" if name == "first" return ".*" end end uri = Addressable::URI.parse( "http://example.com/search/an+example+search+query/" ) Addressable::Template.new( "http://example.com/search/{query}/" ).extract(uri, ExampleProcessor) #=> {"query" => "an example search query"} uri = Addressable::URI.parse("http://example.com/a/b/c/") Addressable::Template.new( "http://example.com/{first}/{second}/" ).extract(uri, ExampleProcessor) #=> {"first" => "a", "second" => "b/c"} uri = Addressable::URI.parse("http://example.com/a/b/c/") Addressable::Template.new( "http://example.com/{first}/{-list|/|second}/" ).extract(uri) #=> {"first" => "a", "second" => ["b", "c"]}
# File lib/addressable/template.rb, line 199 def extract(uri, processor=nil) match_data = self.match(uri, processor) return (match_data ? match_data.mapping : nil) end
Extracts match data from the URI using a URI Template pattern.
@param [Addressable::URI, to_str] uri
The URI to extract from.
@param [restore, match] processor
A template processor object may optionally be supplied. The object should respond to either the <tt>restore</tt> or <tt>match</tt> messages or both. The <tt>restore</tt> method should take two parameters: `[String] name` and `[String] value`. The <tt>restore</tt> method should reverse any transformations that have been performed on the value to ensure a valid URI. The <tt>match</tt> method should take a single parameter: `[String] name`. The <tt>match</tt> method should return a <tt>String</tt> containing a regular expression capture group for matching on that particular variable. The default value is `".*?"`. The <tt>match</tt> method has no effect on multivariate operator expansions.
@return [Hash, NilClass]
The <tt>Hash</tt> mapping that was extracted from the URI, or <tt>nil</tt> if the URI didn't match the template.
@example
class ExampleProcessor def self.restore(name, value) return value.gsub(%r\+/, " ") if name == "query" return value end def self.match(name) return ".*?" if name == "first" return ".*" end end uri = Addressable::URI.parse( "http://example.com/search/an+example+search+query/" ) match = Addressable::Template.new( "http://example.com/search/{query}/" ).match(uri, ExampleProcessor) match.variables #=> ["query"] match.captures #=> ["an example search query"] uri = Addressable::URI.parse("http://example.com/a/b/c/") match = Addressable::Template.new( "http://example.com/{first}/{second}/" ).match(uri, ExampleProcessor) match.variables #=> ["first", "second"] match.captures #=> ["a", "b/c"] uri = Addressable::URI.parse("http://example.com/a/b/c/") match = Addressable::Template.new( "http://example.com/{first}/{-list|/|second}/" ).match(uri) match.variables #=> ["first", "second"] match.captures #=> ["a", ["b", "c"]]
# File lib/addressable/template.rb, line 270 def match(uri, processor=nil) uri = Addressable::URI.parse(uri) mapping = {} # First, we need to process the pattern, and extract the values. expansions, expansion_regexp = parse_template_pattern(pattern, processor) 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 && expansions.size == unparsed_values.size expansions.each_with_index do |expansion, index| unparsed_value = unparsed_values[index] if expansion =~ OPERATOR_EXPANSION operator, argument, variables = parse_template_expansion(expansion) extract_method = "extract_#{operator}_operator" if ([extract_method, extract_method.to_sym] & private_methods).empty? raise InvalidTemplateOperatorError, "Invalid template operator: #{operator}" else begin send( extract_method.to_sym, unparsed_value, processor, argument, variables, mapping ) rescue TemplateOperatorAbortedError return nil end end else name = expansion[VARIABLE_EXPANSION, 1] value = unparsed_value if processor != nil && processor.respond_to?(:restore) value = processor.restore(name, value) end if mapping[name] == nil || mapping[name] == value mapping[name] = value else return nil end end end return Addressable::Template::MatchData.new(uri, self, mapping) else return nil end end
Expands a URI template into another URI template.
@param [Hash] mapping The mapping that corresponds to the pattern. @param [validate, transform] processor
An optional processor object may be supplied.
The object should respond to either the validate
or
transform
messages or both. Both the validate
and
transform
methods should take two parameters:
name
and value
. The validate
method
should return true
or false
; true
if
the value of the variable is valid, false
otherwise. An
InvalidTemplateValueError
exception will be raised if the
value is invalid. The transform
method should return the
transformed variable value as a String
. If a
transform
method is used, the value will not be percent
encoded automatically. Unicode normalization will be performed both before
and after sending the value to the transform method.
@return [Addressable::Template] The partially expanded URI template.
@example
Addressable::Template.new( "http://example.com/{one}/{two}/" ).partial_expand({"one" => "1"}).pattern #=> "http://example.com/1/{two}/" Addressable::Template.new( "http://example.com/search/{-list|+|query}/" ).partial_expand( {"query" => "an example search query".split(" ")} ).pattern #=> "http://example.com/search/an+example+search+query/" Addressable::Template.new( "http://example.com/{-join|&|one,two}/" ).partial_expand({"one" => "1"}).pattern #=> "http://example.com/?one=1{-prefix|&two=|two}" Addressable::Template.new( "http://example.com/{-join|&|one,two,three}/" ).partial_expand({"one" => "1", "three" => 3}).pattern #=> "http://example.com/?one=1{-prefix|&two=|two}&three=3"
# File lib/addressable/template.rb, line 364 def partial_expand(mapping, processor=nil) result = self.pattern.dup transformed_mapping = transform_mapping(mapping, processor) result.gsub!( %r#{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, true ) end else varname, _, vardefault = capture.scan(%r^\{(.+?)(=(.*))?\}$/)[0] if transformed_mapping[varname] transformed_mapping[varname] elsif vardefault "{#{varname}=#{vardefault}}" else "{#{varname}}" end end end return Addressable::Template.new(result) end
Returns a mapping of variables to their default values specified in the template. Variables without defaults are not returned.
@return [Hash] Mapping of template variables to their defaults
# File lib/addressable/template.rb, line 494 def variable_defaults @variable_defaults ||= Hash[*ordered_variable_defaults.reject { |k, v| v.nil? }.flatten] end
Returns an Array of variables used within the template pattern. The variables are listed in the Array in the order they appear within the pattern. Multiple occurrences of a variable within a pattern are not represented in this Array.
@return [Array] The variables present in the template's pattern.
# File lib/addressable/template.rb, line 484 def variables @variables ||= ordered_variable_defaults.map { |var, val| var }.uniq end