uri_template 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG CHANGED
@@ -1,3 +1,6 @@
1
+ # 0.2.1 - 30.12.2011
2
+ * Compatibility: Works now with MRI 1.8.7 and REE
3
+
1
4
  # 0.2.0 - 03.12.2011
2
5
  * Reworked the escaping mechanism
3
6
  + escape_utils can now be used to boost escape/unescape performance
@@ -18,66 +18,74 @@
18
18
  # A base module for all implementations of a uri template.
19
19
  module URITemplate
20
20
 
21
+ # @private
22
+ # Should we use \u.... or \x.. in regexps?
23
+ SUPPORTS_UNICODE_CHARS = begin
24
+ rx = eval('/\u0020/')
25
+ !!(rx =~ " ")
26
+ rescue SyntaxError
27
+ false
28
+ end
29
+
21
30
  # This should make it possible to do basic analysis independently from the concrete type.
22
31
  module Token
23
-
32
+
24
33
  def size
25
34
  variables.size
26
35
  end
27
-
36
+
28
37
  end
29
-
38
+
30
39
  # A module which all literal tokens should include.
31
40
  module Literal
32
-
41
+
33
42
  include Token
34
-
43
+
35
44
  attr_reader :string
36
-
45
+
37
46
  def literal?
38
47
  true
39
48
  end
40
-
49
+
41
50
  def expression?
42
51
  false
43
52
  end
44
-
53
+
45
54
  def variables
46
55
  []
47
56
  end
48
-
57
+
49
58
  def size
50
59
  0
51
60
  end
52
-
61
+
53
62
  def expand(*_)
54
63
  return string
55
64
  end
56
-
65
+
57
66
  end
58
-
59
-
67
+
60
68
  # A module which all non-literal tokens should include.
61
69
  module Expression
62
-
70
+
63
71
  include Token
64
-
72
+
65
73
  attr_reader :variables
66
-
74
+
67
75
  def literal?
68
76
  false
69
77
  end
70
-
78
+
71
79
  def expression?
72
80
  true
73
81
  end
74
-
82
+
75
83
  end
76
84
 
77
85
  autoload :Utils, 'uri_template/utils'
78
86
  autoload :Draft7, 'uri_template/draft7'
79
87
  autoload :Colon, 'uri_template/colon'
80
-
88
+
81
89
  # A hash with all available implementations.
82
90
  # Currently the only implementation is :draft7. But there also aliases :default and :latest available. This should make it possible to add newer specs later.
83
91
  # @see resolve_class
@@ -87,7 +95,7 @@ module URITemplate
87
95
  :colon => :Colon,
88
96
  :latest => :Draft7
89
97
  }
90
-
98
+
91
99
  # Looks up which implementation to use.
92
100
  # Extracts all symbols from args and looks up the first in {VERSIONS}.
93
101
  #
@@ -105,7 +113,7 @@ module URITemplate
105
113
  raise ArgumentError, "Unknown template version #{version.inspect}, defined versions: #{VERSIONS.keys.inspect}" unless VERSIONS.key?(version)
106
114
  return self.const_get(VERSIONS[version]), rest
107
115
  end
108
-
116
+
109
117
  # Creates an uri template using an implementation.
110
118
  # The args should at least contain a pattern string.
111
119
  # Symbols in the args are used to determine the actual implementation.
@@ -121,7 +129,7 @@ module URITemplate
121
129
  klass, rest = resolve_class(*args)
122
130
  return klass.new(*rest)
123
131
  end
124
-
132
+
125
133
  # Tries to convert the given argument into an {URITemplate}.
126
134
  # Returns nil if this fails.
127
135
  #
@@ -135,7 +143,7 @@ module URITemplate
135
143
  return nil
136
144
  end
137
145
  end
138
-
146
+
139
147
  # Same as {.try_convert} but raises an ArgumentError when the given argument could not be converted.
140
148
  #
141
149
  # @raise ArgumentError if the argument is unconvertable
@@ -147,7 +155,7 @@ module URITemplate
147
155
  end
148
156
  return o
149
157
  end
150
-
158
+
151
159
  # Tries to coerce two URITemplates into a common representation.
152
160
  # Returns an array with two {URITemplate}s and two booleans indicating which of the two were converted or raises an ArgumentError.
153
161
  #
@@ -180,7 +188,7 @@ module URITemplate
180
188
  raise ArgumentError, "Expected at least on URITemplate, but got #{a.inspect} and #{b.inspect}" unless a.kind_of? URITemplate or b.kind_of? URITemplate
181
189
  raise ArgumentError, "Cannot coerce #{a.inspect} and #{b.inspect} into a common representation."
182
190
  end
183
-
191
+
184
192
  # Applies a method to a URITemplate with another URITemplate as argument.
185
193
  # This is a useful shorthand since both URITemplates are automatically coerced.
186
194
  #
@@ -194,11 +202,11 @@ module URITemplate
194
202
  a,b,_,_ = self.coerce(a,b)
195
203
  a.send(method,b,*args)
196
204
  end
197
-
205
+
198
206
  # A base class for all errors which will be raised upon invalid syntax.
199
207
  module Invalid
200
208
  end
201
-
209
+
202
210
  # Expands this uri template with the given variables.
203
211
  # The variables should be converted to strings using {Utils#object_to_param}.
204
212
  # @raise {Unconvertable} if a variable could not be converted to a string.
@@ -209,19 +217,19 @@ module URITemplate
209
217
  part.expand(variables)
210
218
  }.join
211
219
  end
212
-
220
+
213
221
  # @abstract
214
222
  # 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.
215
223
  def type
216
224
  raise "Please implement #type on #{self.class.inspect}."
217
225
  end
218
-
226
+
219
227
  # @abstract
220
228
  # Returns the tokens of this templates. Tokens should include either {Literal} or {Expression}.
221
229
  def tokens
222
230
  raise "Please implement #tokens on #{self.class.inspect}."
223
231
  end
224
-
232
+
225
233
  # Returns an array containing all variables. Repeated variables are ignored. The concrete order of the variables may change.
226
234
  # @example
227
235
  # URITemplate.new('{foo}{bar}{baz}').variables #=> ['foo','bar','baz']
@@ -231,7 +239,7 @@ module URITemplate
231
239
  def variables
232
240
  @variables ||= tokens.select(&:expression?).map(&:variables).flatten.uniq
233
241
  end
234
-
242
+
235
243
  # Returns the number of static characters in this template.
236
244
  # This method is useful for routing, since it's often pointful to use the url with fewer variable characters.
237
245
  # 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.
@@ -245,7 +253,7 @@ module URITemplate
245
253
  def static_characters
246
254
  @static_characters ||= tokens.select(&:literal?).map{|t| t.string.size }.inject(0,:+)
247
255
  end
248
-
256
+
249
257
  # Returns whether this uri-template is absolute.
250
258
  # This is detected by checking for "://".
251
259
  #
@@ -260,14 +268,14 @@ module URITemplate
260
268
  end
261
269
  if read_chars =~ /^[a-z]+:\/\//i
262
270
  return @absolute = true
263
- elsif read_chars =~ /(?<!:|\/)\/(?!\/)/
271
+ elsif read_chars =~ /(^|[^:\/])\/(?!\/)/
264
272
  return @absolute = false
265
273
  end
266
274
  end
267
-
275
+
268
276
  return @absolute = false
269
277
  end
270
-
278
+
271
279
  # Opposite of {#absolute?}
272
280
  def relative?
273
281
  !absolute?
@@ -23,7 +23,7 @@ require 'uri_template/utils'
23
23
  # A colon based template denotes variables with a colon.
24
24
  # This template type is realy basic but having just on template type was a bit weird.
25
25
  module URITemplate
26
-
26
+
27
27
  class Colon
28
28
 
29
29
  include URITemplate
@@ -31,46 +31,46 @@ class Colon
31
31
  VAR = /(?:\{:([a-z]+)\}|:([a-z]+)(?![a-z]))/u
32
32
 
33
33
  class Token
34
-
34
+
35
35
  class Variable < self
36
-
36
+
37
37
  include URITemplate::Expression
38
-
38
+
39
39
  attr_reader :name
40
-
40
+
41
41
  def initialize(name)
42
42
  @name = name
43
43
  @variables = [name]
44
44
  end
45
-
45
+
46
46
  def expand(vars)
47
47
  return Utils.escape_url(Utils.object_to_param(vars[@name]))
48
48
  end
49
-
49
+
50
50
  def to_r
51
51
  return ['([^/]*?)'].join
52
52
  end
53
-
53
+
54
54
  end
55
-
55
+
56
56
  class Static < self
57
-
57
+
58
58
  include URITemplate::Literal
59
-
59
+
60
60
  def initialize(str)
61
61
  @string = str
62
62
  end
63
-
63
+
64
64
  def expand(_)
65
65
  return @string
66
66
  end
67
-
67
+
68
68
  def to_r
69
69
  return Regexp.escape(@string)
70
70
  end
71
-
71
+
72
72
  end
73
-
73
+
74
74
  end
75
75
 
76
76
  attr_reader :pattern
@@ -94,7 +94,7 @@ class Colon
94
94
  def initialize(pattern)
95
95
  @pattern = pattern
96
96
  end
97
-
97
+
98
98
  # Extracts variables from an uri.
99
99
  #
100
100
  # @param String uri
@@ -106,19 +106,19 @@ class Colon
106
106
  [v, Utils.unescape_url(md[i+1])]
107
107
  }.flatten(1) ]
108
108
  end
109
-
109
+
110
110
  def type
111
111
  :colon
112
112
  end
113
-
113
+
114
114
  def to_r
115
115
  @regexp ||= Regexp.new('\A' + tokens.map(&:to_r).join + '\z', Utils::KCODE_UTF8)
116
116
  end
117
-
117
+
118
118
  def tokens
119
119
  @tokens ||= tokenize!
120
120
  end
121
-
121
+
122
122
  # Tries to concatenate two templates, as if they were path segments.
123
123
  # Removes double slashes or inserts one if they are missing.
124
124
  #
@@ -136,7 +136,7 @@ class Colon
136
136
  end
137
137
  return self.class.new( File.join( this.pattern, other.pattern ) )
138
138
  end
139
-
139
+
140
140
  protected
141
141
 
142
142
  def tokenize!
@@ -29,127 +29,127 @@ class URITemplate::Draft7
29
29
 
30
30
  include URITemplate
31
31
  extend Forwardable
32
-
32
+
33
33
  # @private
34
34
  Utils = URITemplate::Utils
35
-
36
- # @private
37
- # \/ - unicode ctrl-chars ( \p{Cc} doen't work with rbx
38
- LITERAL = /^([^"'%<>\\^`{|}\s\p{Cntrl}]|%\h\h)+/u
39
-
35
+
36
+ if SUPPORTS_UNICODE_CHARS
37
+ # @private
38
+ # \/ - unicode ctrl-chars
39
+ LITERAL = /([^"'%<>\\^`{|}\u0000-\u001F\u007F-\u009F\s]|%[0-9a-fA-F]{2})+/u
40
+ else
41
+ # @private
42
+ LITERAL = Regexp.compile('([^"\'%<>\\\\^`{|}\x00-\x1F\x7F-\x9F\s]|%[0-9a-fA-F]{2})+',Utils::KCODE_UTF8)
43
+ end
44
+
40
45
  # @private
41
46
  CHARACTER_CLASSES = {
42
-
47
+
43
48
  :unreserved => {
44
- :unencoded => /([^A-Za-z0-9\-\._])/u,
45
- :class => '(?:[A-Za-z0-9\-\._]|%\h\h)',
46
-
47
- :class_name => 'c_u_',
49
+ :class => '(?:[A-Za-z0-9\-\._]|%[0-9a-fA-F]{2})',
48
50
  :grabs_comma => false
49
51
  },
50
52
  :unreserved_reserved_pct => {
51
- :unencoded => /([^A-Za-z0-9\-\._:\/?#\[\]@!\$%'\(\)*+,;=]|%(?!\h\h))/u,
52
- :class => '(?:[A-Za-z0-9\-\._:\/?#\[\]@!\$%\'\(\)*+,;=]|%\h\h)',
53
- :class_name => 'c_urp_',
53
+ :class => '(?:[A-Za-z0-9\-\._:\/?#\[\]@!\$%\'\(\)*+,;=]|%[0-9a-fA-F]{2})',
54
54
  :grabs_comma => true
55
55
  },
56
-
56
+
57
57
  :varname => {
58
58
  :class => '(?:[a-zA-Z_]|%[0-9a-fA-F]{2})(?:[a-zA-Z_\.]|%[0-9a-fA-F]{2})*?',
59
59
  :class_name => 'c_vn_'
60
60
  }
61
-
61
+
62
62
  }
63
-
63
+
64
64
  # Specifies that no processing should be done upon extraction.
65
65
  # @see #extract
66
66
  NO_PROCESSING = []
67
-
67
+
68
68
  # Specifies that the extracted values should be processed.
69
69
  # @see #extract
70
70
  CONVERT_VALUES = [:convert_values]
71
-
71
+
72
72
  # Specifies that the extracted variable list should be processed.
73
73
  # @see #extract
74
74
  CONVERT_RESULT = [:convert_result]
75
-
75
+
76
76
  # Default processing. Means: convert values and the list itself.
77
77
  # @see #extract
78
78
  DEFAULT_PROCESSING = CONVERT_VALUES + CONVERT_RESULT
79
-
79
+
80
80
  # @private
81
81
  VAR = Regexp.compile(<<'__REGEXP__'.strip, Utils::KCODE_UTF8)
82
82
  ((?:[a-zA-Z_]|%[0-9a-fA-F]{2})(?:[a-zA-Z_\.]|%[0-9a-fA-F]{2})*)(\*)?(?::(\d+))?
83
83
  __REGEXP__
84
-
84
+
85
85
  # @private
86
86
  EXPRESSION = Regexp.compile(<<'__REGEXP__'.strip, Utils::KCODE_UTF8)
87
87
  \{([+#\./;?&]?)((?:[a-zA-Z_]|%[0-9a-fA-F]{2})(?:[a-zA-Z_\.]|%[0-9a-fA-F]{2})*\*?(?::\d+)?(?:,(?:[a-zA-Z_]|%[0-9a-fA-F]{2})(?:[a-zA-Z_\.]|%[0-9a-fA-F]{2})*\*?(?::\d+)?)*)\}
88
88
  __REGEXP__
89
89
 
90
90
  # @private
91
- URI = Regexp.compile(<<'__REGEXP__'.strip, Utils::KCODE_UTF8)
92
- \A(([^"'%<>^`{|}\s\p{Cntrl}]|%\h\h)+|\{([+#\./;?&]?)((?:[a-zA-Z_]|%[0-9a-fA-F]{2})(?:[a-zA-Z_\.]|%[0-9a-fA-F]{2})*\*?(?::\d+)?(?:,(?:[a-zA-Z_]|%[0-9a-fA-F]{2})(?:[a-zA-Z_\.]|%[0-9a-fA-F]{2})*\*?(?::\d+)?)*)\})*\z
91
+ URI = Regexp.compile(<<__REGEXP__.strip, Utils::KCODE_UTF8)
92
+ \\A(#{LITERAL.source}|#{EXPRESSION.source})*\\z
93
93
  __REGEXP__
94
94
 
95
95
  SLASH = ?/
96
-
96
+
97
97
  # @private
98
98
  class Token
99
99
  end
100
-
100
+
101
101
  # @private
102
102
  class Literal < Token
103
-
103
+
104
104
  include URITemplate::Literal
105
-
105
+
106
106
  def initialize(string)
107
107
  @string = string
108
108
  end
109
-
109
+
110
110
  def level
111
111
  1
112
112
  end
113
-
113
+
114
114
  def arity
115
115
  0
116
116
  end
117
-
117
+
118
118
  def to_r_source(*_)
119
119
  Regexp.escape(@string)
120
120
  end
121
-
121
+
122
122
  def to_s
123
123
  @string
124
124
  end
125
-
125
+
126
126
  end
127
127
 
128
128
  # @private
129
129
  class Expression < Token
130
-
130
+
131
131
  include URITemplate::Expression
132
-
132
+
133
133
  attr_reader :variables, :max_length
134
-
134
+
135
135
  def initialize(vars)
136
136
  @variable_specs = vars
137
137
  @variables = vars.map(&:first)
138
138
  @variables.uniq!
139
139
  end
140
-
140
+
141
141
  PREFIX = ''.freeze
142
142
  SEPARATOR = ','.freeze
143
143
  PAIR_CONNECTOR = '='.freeze
144
144
  PAIR_IF_EMPTY = true
145
145
  LIST_CONNECTOR = ','.freeze
146
146
  BASE_LEVEL = 1
147
-
147
+
148
148
  CHARACTER_CLASS = CHARACTER_CLASSES[:unreserved]
149
-
149
+
150
150
  NAMED = false
151
151
  OPERATOR = ''
152
-
152
+
153
153
  def level
154
154
  if @variable_specs.none?{|_,expand,ml| expand || (ml > 0) }
155
155
  if @variable_specs.size == 1
@@ -161,16 +161,16 @@ __REGEXP__
161
161
  return 4
162
162
  end
163
163
  end
164
-
164
+
165
165
  def arity
166
166
  @variable_specs.size
167
167
  end
168
-
168
+
169
169
  def expand( vars )
170
170
  result = []
171
171
  @variable_specs.each{| var, expand , max_length |
172
172
  unless vars[var].nil?
173
- if vars[var].kind_of? Hash
173
+ if vars[var].kind_of?(Hash) or Utils.pair_array?(vars[var])
174
174
  result.push( *transform_hash(var, vars[var], expand, max_length) )
175
175
  elsif vars[var].kind_of? Array
176
176
  result.push( *transform_array(var, vars[var], expand, max_length) )
@@ -189,11 +189,11 @@ __REGEXP__
189
189
  return ''
190
190
  end
191
191
  end
192
-
192
+
193
193
  def to_s
194
194
  return '{' + self.class::OPERATOR + @variable_specs.map{|name,expand,max_length| name + (expand ? '*': '') + (max_length > 0 ? (':' + max_length.to_s) : '') }.join(',') + '}'
195
195
  end
196
-
196
+
197
197
  #TODO: certain things after a slurpy variable will never get matched. therefore, it's pointless to add expressions for them
198
198
  #TODO: variables, which appear twice could be compacted, don't they?
199
199
  def to_r_source
@@ -203,11 +203,11 @@ __REGEXP__
203
203
  i = 0
204
204
  if self.class::NAMED
205
205
  @variable_specs.each{| var, expand , max_length |
206
- value = "(?:#{self.class::CHARACTER_CLASS[:class]}|,)#{(max_length > 0)?'{,'+max_length.to_s+'}':'*'}"
206
+ value = "(?:#{self.class::CHARACTER_CLASS[:class]}|,)#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*'}"
207
207
  if expand
208
208
  #if self.class::PAIR_IF_EMPTY
209
209
  pair = "(?:#{CHARACTER_CLASSES[:varname][:class]}#{Regexp.escape(self.class::PAIR_CONNECTOR)})?#{value}"
210
-
210
+
211
211
  if first
212
212
  source << "((?:#{pair})(?:#{Regexp.escape(self.class::SEPARATOR)}#{pair})*)"
213
213
  else
@@ -219,14 +219,14 @@ __REGEXP__
219
219
  else
220
220
  pair = "#{Regexp.escape(var)}(#{Regexp.escape(self.class::PAIR_CONNECTOR)}#{value}|)"
221
221
  end
222
-
222
+
223
223
  if first
224
224
  source << "(?:#{pair})"
225
225
  else
226
226
  source << "(?:#{Regexp.escape(self.class::SEPARATOR)}#{pair})?"
227
227
  end
228
228
  end
229
-
229
+
230
230
  first = false
231
231
  i = i+1
232
232
  }
@@ -235,20 +235,20 @@ __REGEXP__
235
235
  last = (vs == i)
236
236
  if expand
237
237
  # could be list or map, too
238
- value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{,'+max_length.to_s+'}':'*'}"
239
-
238
+ value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*'}"
239
+
240
240
  pair = "(?:#{CHARACTER_CLASSES[:varname][:class]}#{Regexp.escape(self.class::PAIR_CONNECTOR)})?#{value}"
241
-
241
+
242
242
  value = "#{pair}(?:#{Regexp.escape(self.class::SEPARATOR)}#{pair})*"
243
243
  elsif last
244
244
  # the last will slurp lists
245
245
  if self.class::CHARACTER_CLASS[:grabs_comma]
246
- value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{,'+max_length.to_s+'}':'*?'}"
246
+ value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*?'}"
247
247
  else
248
- value = "(?:#{self.class::CHARACTER_CLASS[:class]}|,)#{(max_length > 0)?'{,'+max_length.to_s+'}':'*?'}"
248
+ value = "(?:#{self.class::CHARACTER_CLASS[:class]}|,)#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*?'}"
249
249
  end
250
250
  else
251
- value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{,'+max_length.to_s+'}':'*?'}"
251
+ value = "#{self.class::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*?'}"
252
252
  end
253
253
  if first
254
254
  source << "(#{value})"
@@ -261,7 +261,7 @@ __REGEXP__
261
261
  end
262
262
  return '(?:' + Regexp.escape(self.class::PREFIX) + source.join + ')?'
263
263
  end
264
-
264
+
265
265
  def extract(position,matched)
266
266
  name, expand, max_length = @variable_specs[position]
267
267
  if matched.nil?
@@ -302,38 +302,38 @@ __REGEXP__
302
302
  elsif self.class::NAMED
303
303
  return [ [ name, decode( matched[1..-1] ) ] ]
304
304
  end
305
-
305
+
306
306
  return [ [ name, decode( matched ) ] ]
307
307
  end
308
-
308
+
309
309
  protected
310
-
310
+
311
311
  module ClassMethods
312
-
312
+
313
313
  def hash_extractor(max_length)
314
314
  @hash_extractors ||= {}
315
315
  @hash_extractors[max_length] ||= begin
316
- value = "#{self::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{,'+max_length.to_s+'}':'*?'}"
316
+ value = "#{self::CHARACTER_CLASS[:class]}#{(max_length > 0)?'{0,'+max_length.to_s+'}':'*?'}"
317
317
  pair = "(#{CHARACTER_CLASSES[:varname][:class]}#{Regexp.escape(self::PAIR_CONNECTOR)})?(#{value})"
318
318
  source = "\\A#{Regexp.escape(self::SEPARATOR)}?" + pair + "(\\z|#{Regexp.escape(self::SEPARATOR)}(?!#{Regexp.escape(self::SEPARATOR)}))"
319
319
  Regexp.new( source , Utils::KCODE_UTF8)
320
320
  end
321
321
  end
322
-
322
+
323
323
  end
324
-
324
+
325
325
  extend ClassMethods
326
-
326
+
327
327
  def escape(x)
328
328
  Utils.escape_url(Utils.object_to_param(x))
329
329
  end
330
-
330
+
331
331
  def unescape(x)
332
332
  Utils.unescape_url(x)
333
333
  end
334
-
334
+
335
335
  SPLITTER = /^(?:,(,*)|([^,]+))/
336
-
336
+
337
337
  def decode(x, split = true)
338
338
  if x.nil?
339
339
  if self.class::PAIR_IF_EMPTY
@@ -362,17 +362,17 @@ __REGEXP__
362
362
  unescape(x)
363
363
  end
364
364
  end
365
-
365
+
366
366
  def cut(str,chars)
367
367
  if chars > 0
368
- md = Regexp.compile("\\A#{self.class::CHARACTER_CLASS[:class]}{,#{chars.to_s}}", Utils::KCODE_UTF8).match(str)
368
+ md = Regexp.compile("\\A#{self.class::CHARACTER_CLASS[:class]}{0,#{chars.to_s}}", Utils::KCODE_UTF8).match(str)
369
369
  #TODO: handle invalid matches
370
370
  return md[0]
371
371
  else
372
372
  return str
373
373
  end
374
374
  end
375
-
375
+
376
376
  def pair(key, value, max_length = 0)
377
377
  ek = escape(key)
378
378
  ev = escape(value)
@@ -382,7 +382,7 @@ __REGEXP__
382
382
  return ek + self.class::PAIR_CONNECTOR + cut( ev, max_length )
383
383
  end
384
384
  end
385
-
385
+
386
386
  def transform_hash(name, hsh, expand , max_length)
387
387
  if expand
388
388
  hsh.map{|key,value| pair(key,value) }
@@ -392,7 +392,7 @@ __REGEXP__
392
392
  [ (self.class::NAMED ? escape(name)+self.class::PAIR_CONNECTOR : '' ) + hsh.map{|key,value| escape(key)+self.class::LIST_CONNECTOR+escape(value) }.join(self.class::LIST_CONNECTOR) ]
393
393
  end
394
394
  end
395
-
395
+
396
396
  def transform_array(name, ary, expand , max_length)
397
397
  if expand
398
398
  ary.map{|value| escape(value) }
@@ -402,91 +402,91 @@ __REGEXP__
402
402
  [ (self.class::NAMED ? escape(name)+self.class::PAIR_CONNECTOR : '' ) + ary.map{|value| escape(value) }.join(self.class::LIST_CONNECTOR) ]
403
403
  end
404
404
  end
405
-
405
+
406
406
  class Reserved < self
407
-
407
+
408
408
  CHARACTER_CLASS = CHARACTER_CLASSES[:unreserved_reserved_pct]
409
409
  OPERATOR = '+'.freeze
410
410
  BASE_LEVEL = 2
411
-
411
+
412
412
  def escape(x)
413
413
  Utils.escape_uri(Utils.object_to_param(x))
414
414
  end
415
-
415
+
416
416
  def unescape(x)
417
417
  Utils.unescape_uri(x)
418
418
  end
419
-
419
+
420
420
  end
421
-
421
+
422
422
  class Fragment < self
423
-
423
+
424
424
  CHARACTER_CLASS = CHARACTER_CLASSES[:unreserved_reserved_pct]
425
425
  PREFIX = '#'.freeze
426
426
  OPERATOR = '#'.freeze
427
427
  BASE_LEVEL = 2
428
-
428
+
429
429
  def escape(x)
430
430
  Utils.escape_uri(Utils.object_to_param(x))
431
431
  end
432
-
432
+
433
433
  def unescape(x)
434
434
  Utils.unescape_uri(x)
435
435
  end
436
-
436
+
437
437
  end
438
-
438
+
439
439
  class Label < self
440
-
440
+
441
441
  SEPARATOR = '.'.freeze
442
442
  PREFIX = '.'.freeze
443
443
  OPERATOR = '.'.freeze
444
444
  BASE_LEVEL = 3
445
-
445
+
446
446
  end
447
-
447
+
448
448
  class Path < self
449
-
449
+
450
450
  SEPARATOR = '/'.freeze
451
451
  PREFIX = '/'.freeze
452
452
  OPERATOR = '/'.freeze
453
453
  BASE_LEVEL = 3
454
-
454
+
455
455
  end
456
-
456
+
457
457
  class PathParameters < self
458
-
458
+
459
459
  SEPARATOR = ';'.freeze
460
460
  PREFIX = ';'.freeze
461
461
  NAMED = true
462
462
  PAIR_IF_EMPTY = false
463
463
  OPERATOR = ';'.freeze
464
464
  BASE_LEVEL = 3
465
-
465
+
466
466
  end
467
-
467
+
468
468
  class FormQuery < self
469
-
469
+
470
470
  SEPARATOR = '&'.freeze
471
471
  PREFIX = '?'.freeze
472
472
  NAMED = true
473
473
  OPERATOR = '?'.freeze
474
474
  BASE_LEVEL = 3
475
-
475
+
476
476
  end
477
-
477
+
478
478
  class FormQueryContinuation < self
479
-
479
+
480
480
  SEPARATOR = '&'.freeze
481
481
  PREFIX = '&'.freeze
482
482
  NAMED = true
483
483
  OPERATOR = '&'.freeze
484
484
  BASE_LEVEL = 3
485
-
485
+
486
486
  end
487
-
487
+
488
488
  end
489
-
489
+
490
490
  # @private
491
491
  OPERATORS = {
492
492
  '' => Expression,
@@ -498,33 +498,33 @@ __REGEXP__
498
498
  '?' => Expression::FormQuery,
499
499
  '&' => Expression::FormQueryContinuation
500
500
  }
501
-
501
+
502
502
  # This error is raised when an invalid pattern was given.
503
503
  class Invalid < StandardError
504
-
504
+
505
505
  include URITemplate::Invalid
506
-
506
+
507
507
  attr_reader :pattern, :position
508
-
508
+
509
509
  def initialize(source, position)
510
510
  @pattern = pattern
511
511
  @position = position
512
512
  super("Invalid expression found in #{source.inspect} at #{position}: '#{source[position..-1]}'")
513
513
  end
514
-
514
+
515
515
  end
516
-
516
+
517
517
  # @private
518
518
  class Tokenizer
519
-
519
+
520
520
  include Enumerable
521
-
521
+
522
522
  attr_reader :source
523
-
523
+
524
524
  def initialize(source)
525
525
  @source = source
526
526
  end
527
-
527
+
528
528
  def each
529
529
  if !block_given?
530
530
  return Enumerator.new(self)
@@ -551,12 +551,12 @@ __REGEXP__
551
551
  end
552
552
  end
553
553
  end
554
-
554
+
555
555
  end
556
-
556
+
557
557
  # The class methods for all draft7 templates.
558
558
  module ClassMethods
559
-
559
+
560
560
  # Tries to convert the given param in to a instance of {Draft7}
561
561
  # It basically passes thru instances of that class, parses strings and return nil on everything else.
562
562
  #
@@ -586,8 +586,7 @@ __REGEXP__
586
586
  return nil
587
587
  end
588
588
  end
589
-
590
-
589
+
591
590
  # Like {.try_convert}, but raises an ArgumentError, when the conversion failed.
592
591
  #
593
592
  # @raise ArgumentError
@@ -599,7 +598,7 @@ __REGEXP__
599
598
  return o
600
599
  end
601
600
  end
602
-
601
+
603
602
  # Tests whether a given pattern is a valid template pattern.
604
603
  # @example
605
604
  # URITemplate::Draft7.valid? 'foo' #=> true
@@ -608,13 +607,13 @@ __REGEXP__
608
607
  def valid?(pattern)
609
608
  URI === pattern
610
609
  end
611
-
610
+
612
611
  end
613
-
612
+
614
613
  extend ClassMethods
615
-
614
+
616
615
  attr_reader :options
617
-
616
+
618
617
  # @param String,Array either a pattern as String or an Array of tokens
619
618
  # @param Hash some options
620
619
  # @option :lazy If true the pattern will be parsed on first access, this also means that syntax errors will not be detected unless accessed.
@@ -633,7 +632,7 @@ __REGEXP__
633
632
  raise ArgumentError, "Expected to receive a pattern string, but got #{pattern_or_tokens.inspect}"
634
633
  end
635
634
  end
636
-
635
+
637
636
  # @method expand(variables = {})
638
637
  # Expands the template with the given variables.
639
638
  # The expansion should be compatible to uritemplate spec draft 7 ( http://tools.ietf.org/html/draft-gregorio-uritemplate-07 ).
@@ -648,7 +647,7 @@ __REGEXP__
648
647
  #
649
648
  # @param variables Hash
650
649
  # @return String
651
-
650
+
652
651
  # Compiles this template into a regular expression which can be used to test whether a given uri matches this template. This template is also used for {#===}.
653
652
  #
654
653
  # @example
@@ -666,8 +665,7 @@ __REGEXP__
666
665
  Regexp.new( source.join, Utils::KCODE_UTF8)
667
666
  end
668
667
  end
669
-
670
-
668
+
671
669
  # Extracts variables from a uri ( given as string ) or an instance of MatchData ( which was matched by the regexp of this template.
672
670
  # The actual result depends on the value of post_processing.
673
671
  # This argument specifies whether pair arrays should be converted to hashes.
@@ -724,25 +722,25 @@ __REGEXP__
724
722
  if block_given?
725
723
  return yield result
726
724
  end
727
-
725
+
728
726
  return result
729
727
  end
730
728
  end
731
-
729
+
732
730
  # Extracts variables without any proccessing.
733
731
  # This is equivalent to {#extract} with options {NO_PROCESSING}.
734
732
  # @see #extract
735
733
  def extract_simple(uri_or_match)
736
734
  extract( uri_or_match, NO_PROCESSING )
737
735
  end
738
-
736
+
739
737
  # Returns the pattern for this template.
740
738
  def pattern
741
739
  @pattern ||= tokens.map(&:to_s).join
742
740
  end
743
-
741
+
744
742
  alias to_s pattern
745
-
743
+
746
744
  # Compares two template patterns.
747
745
  def ==(o)
748
746
  this, other, this_converted, _ = URITemplate.coerce( self, o )
@@ -751,12 +749,12 @@ __REGEXP__
751
749
  end
752
750
  return this.pattern == other.pattern
753
751
  end
754
-
752
+
755
753
  # @method ===(uri)
756
754
  # Alias for to_r.=== . Tests whether this template matches a given uri.
757
755
  # @return TrueClass, FalseClass
758
756
  def_delegators :to_r, :===
759
-
757
+
760
758
  # @method match(uri)
761
759
  # Alias for to_r.match . Matches this template against the given uri.
762
760
  # @yield MatchData
@@ -774,7 +772,7 @@ __REGEXP__
774
772
  def type
775
773
  :draft7
776
774
  end
777
-
775
+
778
776
  # Returns the level of this template according to the draft ( http://tools.ietf.org/html/draft-gregorio-uritemplate-07#section-1.2 ). Higher level means higher complexity.
779
777
  # Basically this is defined as:
780
778
  #
@@ -797,7 +795,7 @@ __REGEXP__
797
795
  def level
798
796
  tokens.map(&:level).max
799
797
  end
800
-
798
+
801
799
  # Tries to concatenate two templates, as if they were path segments.
802
800
  # Removes double slashes or insert one if they are missing.
803
801
  #
@@ -813,11 +811,11 @@ __REGEXP__
813
811
  if this_converted
814
812
  return this / other
815
813
  end
816
-
814
+
817
815
  if other.absolute?
818
816
  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."
819
817
  end
820
-
818
+
821
819
  if other.pattern == ''
822
820
  return self
823
821
  end
@@ -848,7 +846,7 @@ __REGEXP__
848
846
  end
849
847
  end
850
848
  end
851
-
849
+
852
850
  if other.tokens.first.kind_of?(Literal)
853
851
  if other.tokens.first.string[0] == SLASH
854
852
  return self.class.new( self.tokens + other.tokens )
@@ -861,7 +859,7 @@ __REGEXP__
861
859
  return self.class.new( self.tokens + [Literal.new('/')] + other.tokens )
862
860
  end
863
861
  end
864
-
862
+
865
863
  # Returns an array containing a the template tokens.
866
864
  def tokens
867
865
  @tokens ||= tokenize!
@@ -872,11 +870,11 @@ protected
872
870
  def tokenize!
873
871
  Tokenizer.new(pattern).to_a
874
872
  end
875
-
873
+
876
874
  def arity
877
875
  @arity ||= tokens.inject(0){|a,t| a + t.arity }
878
876
  end
879
-
877
+
880
878
  # @private
881
879
  def extract_matchdata(matchdata, post_processing)
882
880
  bc = 1
@@ -908,7 +906,5 @@ protected
908
906
  end
909
907
  end
910
908
  end
911
-
912
- end
913
-
914
909
 
910
+ end