class Addressable::Template

This is an implementation of a URI template based on <a href="URI">tinyurl.com/uritemplatedraft03">URI Template draft 03</a>.

Constants

OPERATOR_EXPANSION
VARIABLE_EXPANSION

Attributes

pattern[R]

@return [String] The Template object's pattern.

Public Class Methods

new(pattern) click to toggle source

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

Public Instance Methods

expand(mapping, processor=nil) click to toggle source

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
extract(uri, processor=nil) click to toggle source

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
inspect() click to toggle source

Returns a String representation of the Template object's state.

@return [String] The Template object's state, as a String.

# File lib/addressable/template.rb, line 137
def inspect
  sprintf("#<%s:%#0x PATTERN:%s>",
    self.class.to_s, self.object_id, self.pattern)
end
keys() click to toggle source
Alias for: variables
match(uri, processor=nil) click to toggle source

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
partial_expand(mapping, processor=nil) click to toggle source

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
variable_defaults() click to toggle source

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
variables() click to toggle source

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
Also aliased as: keys