/usr/lib/ruby/vendor_ruby/merb-core/dispatch/router.rb is in ruby-merb-core 1.1.3+dfsg-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 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 300 301 302 303 304 305 306 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 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | require 'merb-core/dispatch/router/cached_proc'
require 'merb-core/dispatch/router/behavior'
require 'merb-core/dispatch/router/resources'
require 'merb-core/dispatch/router/route'
module Merb
# Router stores route definitions and finds the first
# route that matches the incoming request URL.
#
# Then information from route is used by dispatcher to
# call action on the controller.
#
# ==== Routes compilation.
#
# The most interesting method of Router (and heart of
# route matching machinery) is match method generated
# on the fly from routes definitions. It is called routes
# compilation. Generated match method body contains
# one if/elsif statement that picks the first matching route
# definition and sets values to named parameters of the route.
#
# Compilation is synchronized by mutex.
class Router
@routes = []
@named_routes = {}
@resource_routes = {}
@compiler_mutex = Mutex.new
@root_behavior = Behavior.new.defaults(:action => "index")
# Raised when route lookup fails.
class RouteNotFound < StandardError; end;
# Raised when parameters given to generation
# method do not match route parameters.
class GenerationError < StandardError; end;
class NotCompiledError < StandardError; end;
class << self
# An array containing all the application routes in order of
# priority.
#
# :api: private
attr_accessor :routes
# A hash containing all the named application routes. The names
# are absolute (as in, all routes named in a namespace will
# contain the name of the namespace).
#
# :api: private
attr_accessor :named_routes
# A hash of all the application resource routes. The key of the hash
# is an array with each element containing the "path" for the resource
# for example, given the following resource routes:
#
# resources :users do
# resources :comments
# end
#
# The show comment route will have a key of ["User", "Comment"]
#
# :api: private
attr_accessor :resource_routes
# The starting point for route definition. Any route defined in a
# Merb::Router.prepare block will defined in context of this
# behavior.
#
# ==== Examples
#
# Merb::Router.root_behavior = Merb::Router.root_bavior.match("/hello")
#
# In the previous example, all routes will have the path prefix /hello.
# It is important to note that this attribute must be set before any
# routes are defined in order for the behavior to be applied to the
# routes.
#
# :api: plugin
attr_accessor :root_behavior
# A block that will be run around route matching. This block must yield
# in order for the actual matching to happen.
#
# :api: plugin
attr_accessor :around_match
# Creates a route building context and evaluates the block in it. A
# copy of +root_behavior+ (and instance of Behavior) is copied as
# the context.
#
# ==== Parameters
# first<Array>::
# An array containing routes that should be prepended to the routes
# defined in the block.
# last<Array>::
# An array containing routes that should be appended to the routes
# defined in the block.
#
# ==== Returns
# Merb::Router::
# Returns self to allow chaining of methods.
#
# :api: public
def prepare(first = [], last = [], &block)
@routes = []
root_behavior._with_proxy(&block)
@routes = first + @routes + last
compile
self
end
# Clears the routing table. Route generation and request matching
# won't work anymore until a new routing table is built.
#
# :api: private
def reset!
class << self
alias_method :match, :match_before_compilation
end
self.routes, self.named_routes, self.resource_routes = [], {}, {}
end
# Finds route matching URI of the request and returns a tuple of
# [route index, route params]. This method is called by the
# dispatcher and isn't as useful in applications.
#
# ==== Parameters
# request<Merb::Request>:: request to match.
#
# ==== Returns
# Array[Integer, Hash]::
# Two-tuple: route index and route parameters. Route parameters
# are :controller, :action and all the named segments of the route.
#
# :api: private
def route_for(request)
index, params = if @around_match
send(@around_match, request) { match(request) }
else
match(request)
end
route = routes[index] if index
if !route
raise ControllerExceptions::NotFound,
"No routes match the request: #{request.uri}"
end
[route, params]
end
# A placeholder for the compiled match method.
#
# ==== Notes
# This method is aliased as +match+ but this method gets overridden with
# the actual +match+ method (generated from the routes definitions) after
# being compiled. This method is only ever called before routes are
# compiled.
#
# ==== Raises
# NotCompiledError:: routes have not been compiled yet.
#
# :api: private
def match_before_compilation(request) #:nodoc:
raise NotCompiledError, "The routes have not been compiled yet"
end
alias_method :match, :match_before_compilation
# There are three possible ways to use this method. First, if you have a named route,
# you can specify the route as the first parameter as a symbol and any paramters in a
# hash. Second, you can generate the default route by just passing the params hash,
# just passing the params hash. Finally, you can use the anonymous parameters. This
# allows you to specify the parameters to a named route in the order they appear in the
# router.
#
# ==== Parameters(Named Route)
# name<Symbol>::
# The name of the route.
# args<Hash>::
# Parameters for the route generation.
#
# ==== Parameters(Default Route)
# args<Hash>::
# Parameters for the route generation. This route will use the default route.
#
# ==== Parameters(Anonymous Parameters)
# name<Symbol>::
# The name of the route.
# args<Array>::
# An array of anonymous parameters to generate the route
# with. These parameters are assigned to the route parameters
# in the order that they are passed.
#
# ==== Returns
# String:: The generated URL.
#
# ==== Examples
# Named Route
#
# Merb::Router.prepare do
# match("/articles/:title").to(:controller => :articles, :action => :show).name("articles")
# end
#
# url(:articles, :title => "new_article")
#
# Default Route
#
# Merb::Router.prepare do
# default_routes
# end
#
# url(:controller => "articles", :action => "new")
#
# Anonymous Paramters
#
# Merb::Router.prepare do
# match("/articles/:year/:month/:title").to(:controller => :articles, :action => :show).name("articles")
# end
#
# url(:articles, 2008, 10, "test_article")
#
# :api: plugin
def url(name, *args)
if name.is_a?(Route)
route = name
else
unless name.is_a?(Symbol)
args.unshift(name)
name = :default
end
unless route = Merb::Router.named_routes[name]
raise Merb::Router::GenerationError, "Named route not found: #{name}"
end
end
defaults = args.pop
route.generate(args, defaults)
end
# Generates a URL from the resource(s)
#
# ==== Parameters
# resources<Symbol,Object>::
# The identifiers for the resource route to generate. These
# can either be symbols or objects. Symbols denote resource
# collection routes and objects denote the members.
#
# params<Hash>::
# Any extra parameters needed to generate the route.
#
# ==== Returns
# String:: The generated URL
#
# :api: plugin
def resource(*args)
defaults = args.pop
options = extract_options_from_args!(args) || {}
key = []
params = []
args.each do |arg|
if arg.is_a?(Symbol) || arg.is_a?(String)
key << arg.to_s
else
key << arg.class.to_s
params << arg
end
end
unless route = Merb::Router.resource_routes[key]
raise Merb::Router::GenerationError, "Resource route not found: #{args.inspect}"
end
params << options
route.generate(params, defaults, true)
end
# Add functionality to the router. This can be in the form of
# including a new module or directly defining new methods.
#
# ==== Parameters
# &block<Block>::
# A block of code used to extend the route builder with. This
# can be including a module or directly defining some new methods
# that should be available to building routes.
#
# ==== Returns
# nil
#
# ==== Example
# Merb::Router.extensions do
# def domain(name, domain, options={}, &block)
# match(:domain => domain).namespace(name, :path => nil, &block)
# end
# end
#
# In this case, a method 'domain' will be available to the route builder
# which will create namespaces around domains instead of path prefixes.
#
# This can then be used as follows.
#
# Merb::Router.prepare do
# domain(:admin, "my-admin.com") do
# # ... routes come here ...
# end
# end
#
# :api: public
def extensions(&block)
Router::Behavior.class_eval(&block)
end
private
# Compiles the routes and creates the +match+ method.
#
# :api: private
def compile
if routes.any?
begin
eval(compiled_statement, binding, "Generated Code for Router", 1)
rescue SyntaxError => e
puts "\nGenerated code failed:\n #{compiled_statement}"
raise e
end
else
reset!
end
end
# Generates the method for evaluation defining a +match+ method to match
# a request with the defined routes.
#
# :api: private
def compiled_statement
@compiler_mutex.synchronize do
condition_keys, if_statements = Set.new, ""
routes.each_with_index do |route, i|
route.freeze
route.conditions.keys.each { |key| condition_keys << key }
if_statements << route.compiled_statement(i == 0)
end
statement = "public\ndef match(request)\n"
statement << condition_keys.inject("") do |cached, key|
cached << " cached_#{key} = request.#{key}.to_s\n"
end
statement << if_statements
statement << " else\n"
statement << " [nil, {}]\n"
statement << " end\n"
statement << "end"
end
end
end # class << self
end
end
|