Module: URITemplate

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

Overview

The MIT License (MIT)

Copyright © 2011-2014 Hannes Georg

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Defined Under Namespace

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

Constant Summary collapse

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

Methods included from ClassMethods

convert, included, try_convert

Class Method Details

.apply(a, method, b, *args) ⇒ Object

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'


167
168
169
170
# File 'lib/uri_template.rb', line 167

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

.coerce(a, b) ⇒ Tuple<URITemplate,URITemplate,Bool,Bool>

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)


131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/uri_template.rb', line 131

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

.coerce_first_arg(meth) ⇒ Object

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



173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/uri_template.rb', line 173

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

.new(*args) ⇒ Object

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


118
119
120
121
# File 'lib/uri_template.rb', line 118

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

.resolve_class(*args) ⇒ Object

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.



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

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

Instance Method Details

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

Concatenate two template with conversion.

Examples:

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

Parameters:

Returns:

  • URITemplate



384
385
386
387
388
389
390
391
392
# File 'lib/uri_template.rb', line 384

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

#eq(other) ⇒ Object Also known as: ==

Compares two template patterns.



331
332
333
# File 'lib/uri_template.rb', line 331

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

#expand(variables = {}) ⇒ Object

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.

If the variables are given as an array, they will be matched against the variables in the template based on their order.

Parameters:

  • variables (#map, Array) (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 ).



208
209
210
211
212
213
# File 'lib/uri_template.rb', line 208

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

#expand_partial(variables = {}) ⇒ URITemplate

Works like #expand with two differences:

- the result is a uri template instead of string
- undefined variables are left in the template

Parameters:

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

Returns:

See Also:

  • URITemplate.{{#expand}


223
224
225
226
227
228
# File 'lib/uri_template.rb', line 223

def expand_partial(variables = {})
  variables = normalize_variables(variables)
  self.class.new(tokens.map{|part|
    part.expand_partial(variables)
  }.flatten(1))
end

#host?Boolean 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:



300
301
302
# File 'lib/uri_template.rb', line 300

def host?
  return scheme_and_host[1]
end

#path_concat(other) ⇒ Object 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



351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/uri_template.rb', line 351

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).join )
    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

#patternObject Also known as: to_s

Returns the pattern for this template.

Returns:

  • String



324
325
326
# File 'lib/uri_template.rb', line 324

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

#relative?Boolean

Opposite of #absolute?

Returns:

  • (Boolean)


448
449
450
# File 'lib/uri_template.rb', line 448

def relative?
  !absolute?
end

#scheme?Boolean

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:



317
318
319
# File 'lib/uri_template.rb', line 317

def scheme?
  return scheme_and_host[0]
end

#static_charactersObject

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



282
283
284
# File 'lib/uri_template.rb', line 282

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

#tokensArray<URITemplate::Token>

This method is abstract.

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

Returns:



258
259
260
# File 'lib/uri_template.rb', line 258

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

#typeSymbol

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)


250
251
252
# File 'lib/uri_template.rb', line 250

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

#variablesArray<String>

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>)


268
269
270
# File 'lib/uri_template.rb', line 268

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