Module: URITemplate

Included in:
Colon, RFC6570
Defined in:
lib/uri_template.rb,
lib/uri_template/utils.rb,
lib/uri_template/colon.rb

Overview

This program is free software: you can redistribute it and/or modify

it under the terms of the Affero GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

(c) 2011 - 2012 by Hannes Georg

Defined Under Namespace

Modules: Expression, Invalid, InvalidValue, Literal, Token, Utils Classes: Colon, RFC6570, RegexpEnumerator, Unconvertable

Constant Summary

SCHEME_REGEX =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

/\A[a-z]+:/i.freeze
HOST_REGEX =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

/\A(?:[a-z]+:)?\/\/[^\/]+/i.freeze
URI_SPLIT =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

/\A(?:([a-z]+):)?(?:\/\/)?([^\/]+)?/i.freeze
VERSIONS =

A hash with all available implementations.

See Also:

{
  :rfc6570 => :RFC6570,
  :default => :RFC6570,
  :colon => :Colon,
  :latest => :RFC6570
}

Class Method Summary (collapse)

Instance Method Summary (collapse)

Class Method Details

+ (Object) apply(a, method, b, *args)

Applies a method to a URITemplate with another URITemplate as argument. This is a useful shorthand since both URITemplates are automatically coerced.

Examples:

tpl = URITemplate.new('foo')
URITemplate.apply( tpl, :/, 'bar' ).pattern #=> 'foo/bar'
URITemplate.apply( 'baz', :/, tpl ).pattern #=> 'baz/foo'
URITemplate.apply( 'bla', :/, 'blub' ).pattern #=> 'bla/blub'


151
152
153
154
# File 'lib/uri_template.rb', line 151

def self.apply(a, method, b, *args)
  a,b,_,_ = self.coerce(a,b)
  a.send(method,b,*args)
end

+ (Tuple<URITemplate,URITemplate,Bool,Bool>) coerce(a, b)

Tries to coerce two URITemplates into a common representation. Returns an array with two URITemplates and two booleans indicating which of the two were converted or raises an ArgumentError.

Examples:

URITemplate.coerce( URITemplate.new(:rfc6570,'{x}'), '{y}' ) #=> [URITemplate.new(:rfc6570,'{x}'), URITemplate.new(:rfc6570,'{y}'), false, true]
URITemplate.coerce( '{y}', URITemplate.new(:rfc6570,'{x}') ) #=> [URITemplate.new(:rfc6570,'{y}'), URITemplate.new(:rfc6570,'{x}'), true, false]

Returns:

Raises:

  • (ArgumentError)


115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/uri_template.rb', line 115

def self.coerce(a,b)
  if a.kind_of? URITemplate
    if a.class == b.class
      return [a,b,false,false]
    end
    b_as_a = a.class.try_convert(b)
    if b_as_a
      return [a,b_as_a,false,true]
    end
  end
  if b.kind_of? URITemplate
    a_as_b = b.class.try_convert(a)
    if a_as_b
      return [a_as_b, b, true, false]
    end
  end
  bc = self.try_convert(b)
  if bc.kind_of? URITemplate
    a_as_b = bc.class.try_convert(a)
    if a_as_b
      return [a_as_b, bc, true, true]
    end
  end
  raise ArgumentError, "Expected at least on URITemplate, but got #{a.inspect} and #{b.inspect}" unless a.kind_of? URITemplate or b.kind_of? URITemplate
  raise ArgumentError, "Cannot coerce #{a.inspect} and #{b.inspect} into a common representation."
end

+ (Object) coerce_first_arg(meth)



156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/uri_template.rb', line 156

def self.coerce_first_arg(meth)
  alias_method( (meth.to_s + '_without_coercion').to_sym , meth )
  class_eval(<<-RUBY)
def #{meth}(other, *args, &block)
this, other, this_converted, _ = URITemplate.coerce( self, other )
if this_converted
  return this.#{meth}(other,*args, &block)
end
return #{meth}_without_coercion(other,*args, &block)
end
RUBY
end

+ (URITemplate) convert(x)

Same as try_convert but raises an ArgumentError when the given argument could not be converted.

Returns:

Raises:

  • ArgumentError if the argument is unconvertable



99
100
101
102
103
104
105
# File 'lib/uri_template.rb', line 99

def self.convert(x)
  o = self.try_convert(x)
  if o.nil?
    raise ArgumentError, "Expected to receive something that can be converted to an URITemplate, but got #{x.inspect}"
  end
  return o
end

+ (Object) new(*args)

Creates an uri template using an implementation. The args should at least contain a pattern string. Symbols in the args are used to determine the actual implementation.

Examples:

tpl = URITemplate.new('{x}') # a new template using the default implementation
tpl.expand('x'=>'y') #=> 'y'
tpl = URITemplate.new(:colon,'/:x') # a new template using the colon implementation


76
77
78
79
# File 'lib/uri_template.rb', line 76

def self.new(*args)
  klass, rest = resolve_class(*args)
  return klass.new(*rest)
end

+ (Object) resolve_class(*args)

Looks up which implementation to use. Extracts all symbols from args and looks up the first in VERSIONS.

Examples:

URITemplate.resolve_class() #=> [ URITemplate::RFC6570, [] ]
URITemplate.resolve_class(:colon) #=> [ URITemplate::Colon, [] ]
URITemplate.resolve_class("template",:rfc6570) #=> [ URITemplate::RFC6570, ["template"] ]

Returns:

  • Array an array of the class to use and the unused parameters.

Raises:

  • ArgumentError when no class was found.



58
59
60
61
62
63
# File 'lib/uri_template.rb', line 58

def self.resolve_class(*args)
  symbols, rest = args.partition{|x| x.kind_of? Symbol }
  version = symbols.fetch(0, :default)
  raise ArgumentError, "Unknown template version #{version.inspect}, defined versions: #{VERSIONS.keys.inspect}" unless VERSIONS.key?(version)
  return self.const_get(VERSIONS[version]), rest
end

+ (nil|{URITemplate}) try_convert(x)

Tries to convert the given argument into an URITemplate. Returns nil if this fails.

Returns:



85
86
87
88
89
90
91
92
93
# File 'lib/uri_template.rb', line 85

def self.try_convert(x)
  if x.kind_of? URITemplate
    return x
  elsif x.kind_of? String
    return URITemplate.new(x)
  else
    return nil
  end
end

Instance Method Details

- (Object) concat(other) Also known as: +, >>

Concatenate two template with conversion.

Examples:

tpl = URITemplate::RFC6570.new('foo')
(tpl + '{bar}' ).pattern #=> 'foo{bar}'

Parameters:

Returns:

  • URITemplate



338
339
340
341
342
343
344
345
346
# File 'lib/uri_template.rb', line 338

def concat(other)
  if other.host? or other.scheme?
    raise ArgumentError, "Expected to receive a relative template but got an absoulte one: #{other.inspect}. If you think this is a bug, please report it."
  end

  return self if other.tokens.none?
  return other if self.tokens.none?
  return self.class.new( self.to_s + other.to_s )
end

- (Object) eq(other) Also known as: ==

Compares two template patterns.



285
286
287
# File 'lib/uri_template.rb', line 285

def eq(other)
  return self.pattern == other.pattern
end

- (Object) expand(variables = {})

Expands this uri template with the given variables. The variables should be converted to strings using URITemplate::Utils#object_to_param.

The keys in the variables hash are converted to strings in order to support the Ruby 1.9 hash syntax.

Parameters:

  • variables (#map) (defaults to: {})

Returns:

  • String

Raises:

  • (Unconvertable)

    if a variable could not be converted to a string.

  • (InvalidValue)

    if a value is not suiteable for a certain variable ( e.g. a string when a list is expected ).



189
190
191
192
193
194
195
196
197
198
# File 'lib/uri_template.rb', line 189

def expand(variables = {})
  raise ArgumentError, "Expected something that returns to :map, but got: #{variables.inspect}" unless variables.respond_to? :map

  # Stringify variables
  variables = Hash[variables.map{ |k, v| [k.to_s, v] }]

  tokens.map{|part|
    part.expand(variables)
  }.join
end

- (Boolean) host? Also known as: absolute?

Returns whether this uri-template includes a host name

This method is usefull to check wheter this template will generate or match a uri with a host.

Examples:

URITemplate.new('/foo').host? #=> false
URITemplate.new('//example.com/foo').host? #=> true
URITemplate.new('//{host}/foo').host? #=> true
URITemplate.new('http://example.com/foo').host? #=> true
URITemplate.new('{scheme}://example.com/foo').host? #=> true

Returns:

  • (Boolean)

See Also:



254
255
256
# File 'lib/uri_template.rb', line 254

def host?
  return scheme_and_host[1]
end

- (Object) path_concat(other) Also known as: /

Tries to concatenate two templates, as if they were path segments. Removes double slashes or insert one if they are missing.

Examples:

tpl = URITemplate::RFC6570.new('/xy/')
(tpl / '/z/' ).pattern #=> '/xy/z/'
(tpl / 'z/' ).pattern #=> '/xy/z/'
(tpl / '{/z}' ).pattern #=> '/xy{/z}'
(tpl / 'a' / 'b' ).pattern #=> '/xy/a/b'

Parameters:

Returns:

  • URITemplate



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/uri_template.rb', line 305

def path_concat(other)
  if other.host? or other.scheme?
    raise ArgumentError, "Expected to receive a relative template but got an absoulte one: #{other.inspect}. If you think this is a bug, please report it."
  end

  return self if other.tokens.none?
  return other if self.tokens.none?

  tail = self.tokens.last
  head = other.tokens.first

  if tail.ends_with_slash?
    if head.starts_with_slash?
      return self.class.new( remove_double_slash(self.tokens, other.tokens) )
    end
  elsif !head.starts_with_slash?
    return self.class.new( (self.tokens + ['/'] + other.tokens).join)
  end
  return self.class.new( (self.tokens + other.tokens).join )
end

- (Object) pattern Also known as: to_s

Returns the pattern for this template.

Returns:

  • String



278
279
280
# File 'lib/uri_template.rb', line 278

def pattern
  @pattern ||= tokens.map(&:to_s).join
end

- (Boolean) relative?

Opposite of #absolute?

Returns:

  • (Boolean)


402
403
404
# File 'lib/uri_template.rb', line 402

def relative?
  !absolute?
end

- (Boolean) scheme?

Returns whether this uri-template includes a scheme

This method is usefull to check wheter this template will generate or match a uri with a scheme.

Examples:

URITemplate.new('/foo').scheme? #=> false
URITemplate.new('//example.com/foo').scheme? #=> false
URITemplate.new('http://example.com/foo').scheme? #=> true
URITemplate.new('{scheme}://example.com/foo').scheme? #=> true

Returns:

  • (Boolean)

See Also:



271
272
273
# File 'lib/uri_template.rb', line 271

def scheme?
  return scheme_and_host[0]
end

- (Object) static_characters

Returns the number of static characters in this template. This method is useful for routing, since it's often pointful to use the url with fewer variable characters. For example 'static' and 'sta{var\}' both match 'static', but in most cases 'static' should be prefered over 'sta{var\}' since it's more specific.

Examples:

URITemplate.new('/xy/').static_characters #=> 4
URITemplate.new('{foo}').static_characters #=> 0
URITemplate.new('a{foo}b').static_characters #=> 2

Returns:

  • Numeric



236
237
238
# File 'lib/uri_template.rb', line 236

def static_characters
  @static_characters ||= tokens.select(&:literal?).map{|t| t.string.size }.inject(0,:+)
end

- (Array<URITemplate::Token>) tokens

This method is abstract.

Returns the tokens of this templates. Tokens should include either Literal or Expression.

Returns:



212
213
214
# File 'lib/uri_template.rb', line 212

def tokens
  raise "Please implement #tokens on #{self.class.inspect}."
end

- (Symbol) type

This method is abstract.

Returns the type of this template. The type is a symbol which can be used in resolve_class to resolve the type of this template.

Returns:

  • (Symbol)


204
205
206
# File 'lib/uri_template.rb', line 204

def type
  raise "Please implement #type on #{self.class.inspect}."
end

- (Array<String>) variables

Returns an array containing all variables. Repeated variables are ignored. The concrete order of the variables may change.

Examples:

URITemplate.new('{foo}{bar}{baz}').variables #=> ['foo','bar','baz']
URITemplate.new('{a}{c}{a}{b}').variables #=> ['a','c','b']

Returns:

  • (Array<String>)


222
223
224
# File 'lib/uri_template.rb', line 222

def variables
  @variables ||= tokens.map(&:variables).flatten.uniq.freeze
end