Class: Blix::Rest::RequestMapper
- Inherits:
-
Object
- Object
- Blix::Rest::RequestMapper
- Defined in:
- lib/blix/rest/request_mapper.rb
Overview
register routes with this class and then we can match paths to these routes and return an associated block and parameters.
Defined Under Namespace
Classes: TableNode
Constant Summary collapse
- WILD_PLACEHOLDER =
'/'
- PATH_SEP =
'/'
- STAR_PLACEHOLDER =
'*'
- @@mutex =
Mutex.new
Class Method Summary collapse
-
.add_path(verb, path, opts = {}, &blk) ⇒ Object
declare a route.
-
.compile ⇒ Object
compile routes into a tree structure for easy lookup.
- .dump ⇒ Object
- .dump_node(item, indent = 0) ⇒ Object
-
.ensure_full_path(path) ⇒ Object
ensure that absolute path is the full path.
- .full_path(path) ⇒ Object
- .locations ⇒ Object
-
.match(verb, path) ⇒ Object
match a given path to declared route.
-
.path_root ⇒ Object
if the path_root has not been set then return ‘/’.
-
.path_root_length ⇒ Object
return 0 if the path_root has not been set.
-
.process(verb, path) ⇒ Object
match a path to a route and call any associated block with the extracted parameters.
-
.reset(vals = nil) ⇒ Object
used for testing only !!.
- .routes ⇒ Object
-
.set_path_root(root) ⇒ Object
the root always starts with ‘/’ and finishes with ‘/’.
- .table ⇒ Object
Class Method Details
.add_path(verb, path, opts = {}, &blk) ⇒ Object
declare a route
165 166 167 168 169 170 |
# File 'lib/blix/rest/request_mapper.rb', line 165 def add_path(verb, path, opts = {}, &blk) path = path[1..-1] if path[0, 1] == PATH_SEP RequestMapper.locations[verb] << [verb, path, opts, blk] @table = nil # force recompile true end |
.compile ⇒ Object
compile routes into a tree structure for easy lookup
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 157 158 159 160 161 162 |
# File 'lib/blix/rest/request_mapper.rb', line 131 def compile @table = Hash.new { |h, k| h[k] = TableNode.new('') } locations.each do |verb, routes| routes.each do |info| verb, path, opts, blk = info parts = path.split(PATH_SEP) current = @table[verb] parts.each_with_index do |section, idx| node = TableNode.new(section) # check that a wildstar is the last element. if (section[0] == STAR_PLACEHOLDER) && (idx < (parts.length - 1)) raise RequestMapperError, "do not add a path after the * in #{path}" end # check that wild card match in name if current[node.value] if (node.value == WILD_PLACEHOLDER) && (node.parameter != current[node.value].parameter) raise RequestMapperError, "parameter mismatch in route=#{path}, expected #{current[node.value].parameter} but got #{node.parameter}" end else current[node.value] = node end current = current[node.value] end current.blk = blk current.opts = opts || {} current.extract_format = opts[:extension] if opts.key?(:extension) end end @table end |
.dump ⇒ Object
101 102 103 104 105 106 |
# File 'lib/blix/rest/request_mapper.rb', line 101 def dump table.each do |k, v| puts k dump_node(v, 1) end end |
.dump_node(item, indent = 0) ⇒ Object
108 109 110 111 |
# File 'lib/blix/rest/request_mapper.rb', line 108 def dump_node(item, indent = 0) puts "#{' ' * indent} value=#{item.value.inspect} opts=#{item.opts.inspect} params=#{item.parameter.inspect}" item.children.each_value { |c| dump_node(c, indent + 1) } end |
.ensure_full_path(path) ⇒ Object
ensure that absolute path is the full path
85 86 87 88 89 90 |
# File 'lib/blix/rest/request_mapper.rb', line 85 def ensure_full_path(path) if path[0, 1] == '/' && (path_root_length>0) && path[0, path_root_length] != path_root[0, path_root_length] path = path_root + path[1..-1] end path end |
.full_path(path) ⇒ Object
79 80 81 82 |
# File 'lib/blix/rest/request_mapper.rb', line 79 def full_path(path) path = path[1..-1] if path[0, 1] == '/' path_root + path end |
.locations ⇒ Object
92 93 94 |
# File 'lib/blix/rest/request_mapper.rb', line 92 def locations @locations ||= Hash.new { |h, k| h[k] = [] } end |
.match(verb, path) ⇒ Object
match a given path to declared route.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/blix/rest/request_mapper.rb', line 173 def match(verb, path) path = PATH_SEP + path if path[0, 1] != PATH_SEP # ensure a leading slash on path path = path[path_root_length..-1] if (path_root_length.to_i > 0) #&& (path[0,path_root_length] == path_root) if path path = path[1..-1] if path[0, 1] == PATH_SEP # remove the leading slash else return [nil, {}, nil] end parameters = StringHash.new parts = path.split(PATH_SEP) current = table[verb] limit = parts.length - 1 # handle the root node here if path == '' if current.blk return [current.blk, parameters, current.opts] elsif (havewild = current[STAR_PLACEHOLDER]) parameters[havewild.parameter.to_s] = '/' return [havewild.blk, parameters, havewild.opts] else return [nil, {}, nil] end end parts.each_with_index do |section, idx| # first save the last node that we used # before updating the current node. last = current # table nodes # check to see if there is a path which includes a format part # only on the last section if idx == limit if last[section] current = last[section] else format = File.extname(section) base = File.basename(section, format) current = last[base] if current parameters['format'] = format[1..-1].to_sym # !format.empty? section = base end end else current = last[section] end # if current is set here that means that this section matches a fixed # part of the route. if current # if this is the last section then we have to decide here if we # have a valid result.. # .. if we have a block then fine .. # .. if there is a wildpath foloowing then fine .. # .. otherwise an error ! if idx == limit # the last section of path if current.blk return [current.blk, parameters, current.opts] elsif (havewild = current[STAR_PLACEHOLDER]) parameters[havewild.parameter.to_s] = '/' return [havewild.blk, parameters, havewild.opts, true] else return [nil, {}, nil] end end else # this section is not part of a static path so # check if we have a path variable first .. current = last[WILD_PLACEHOLDER] if current # yes this is a path variable section if idx == limit # the last section of request - if current.extract_format format = File.extname(section) base = File.basename(section, format) parameters[current.parameter.to_s] = base parameters['format'] = format[1..-1].to_sym unless format.empty? else parameters[current.parameter.to_s] = section end # check if we have a valid block otherwise see if # a wild path follows. if current.blk return [current.blk, parameters, current.opts] elsif (havewild = current[STAR_PLACEHOLDER]) parameters[havewild.parameter.to_s] = '/' return [havewild.blk, parameters, havewild.opts, true] else return [nil, {}, nil] end else parameters[current.parameter.to_s] = section end else current = last[STAR_PLACEHOLDER] if current wildpath = '/' + parts[idx..-1].join('/') wildformat = File.extname(wildpath) unless wildformat.empty? || !current.extract_format wildpath = wildpath[0..-(wildformat.length + 1)] parameters['format'] = wildformat[1..-1].to_sym end parameters[current.parameter.to_s] = wildpath return [current.blk, parameters, current.opts, true] else return [nil, {}, nil] end end end end [nil, {}, nil] end |
.path_root ⇒ Object
if the path_root has not been set then return ‘/’
70 71 72 |
# File 'lib/blix/rest/request_mapper.rb', line 70 def path_root @path_root || '/' end |
.path_root_length ⇒ Object
return 0 if the path_root has not been set
75 76 77 |
# File 'lib/blix/rest/request_mapper.rb', line 75 def path_root_length @path_root_length || 0 end |
.process(verb, path) ⇒ Object
match a path to a route and call any associated block with the extracted parameters.
302 303 304 305 |
# File 'lib/blix/rest/request_mapper.rb', line 302 def process(verb, path) blk, params = match(verb, path) blk&.call(params) end |
.reset(vals = nil) ⇒ Object
used for testing only !!
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/blix/rest/request_mapper.rb', line 114 def reset(vals = nil) save = [@table&.dup, @locations&.dup, @path_root&.dup, @path_root_length] if vals @table = vals[0] @locations = vals[1] @path_root = vals[2] @path_root_length = vals[3] else @table = nil @locations = nil @path_root = nil @path_root_length = 0 end save end |
.routes ⇒ Object
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/blix/rest/request_mapper.rb', line 307 def routes hash = {} locations.values.each do |group| group.each do |route| verb = route[0] = route[2] = String.new = ' ' + .inspect.to_s unless .empty? path = '/' + route[1] hash[path] ||= {} hash[path][verb] = end end list = hash.to_a list.sort! { |a, b| a[0] <=> b[0] } str = String.new list.each do |route| pairs = route[1].to_a.sort{|a,b| a[0]<=>b[0]} pairs.each do |pair| str << pair[0] << "\t" << route[0] << "\t" << pair[1] << "\n" end str << "\n" end str end |
.set_path_root(root) ⇒ Object
the root always starts with ‘/’ and finishes with ‘/’
61 62 63 64 65 66 67 |
# File 'lib/blix/rest/request_mapper.rb', line 61 def set_path_root(root) root = root.to_s root = '/' + root unless root.start_with?('/') root += '/' unless root.end_with?('/') @path_root = root @path_root_length = @path_root.length - 1 end |
.table ⇒ Object
96 97 98 99 |
# File 'lib/blix/rest/request_mapper.rb', line 96 def table # compile the table in one thread only. @table || @@mutex.synchronize{@table ||= compile} end |