Class: Merb::Router

Inherits:
Object show all
Defined in:
lib/merb-core/dispatch/router.rb,
lib/merb-core/dispatch/router/route.rb,
lib/merb-core/dispatch/router/behavior.rb,
lib/merb-core/dispatch/router/cached_proc.rb

Overview

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.

Defined Under Namespace

Classes: Behavior, CachedProc, Route

Constant Summary collapse

SEGMENT_REGEXP =
/(:([a-z_][a-z0-9_]*|:))/
SEGMENT_REGEXP_WITH_BRACKETS =
/(:[a-z_]+)(\[(\d+)\])?/
JUST_BRACKETS =
/\[(\d+)\]/
PARENTHETICAL_SEGMENT_STRING =
"([^\/.,;?]+)".freeze
@@named_routes =
{}
@@routes =
[]
@@compiler_mutex =
Mutex.new

Class Method Summary collapse

Class Method Details

.append(&block) ⇒ Object

Appends the generated routes to the current routes.

Parameters

&block

A block that generates new routes when yielded a new Behavior.



65
66
67
# File 'lib/merb-core/dispatch/router.rb', line 65

def append(&block)
  prepare(@@routes, [], &block)
end

.capture(&block) ⇒ Object

Capture any new routes that have been added within the block.

This utility method lets you track routes that have been added; it doesn’t affect how/which routes are added.

&block

A context in which routes are generated.



100
101
102
103
104
# File 'lib/merb-core/dispatch/router.rb', line 100

def capture(&block)
  routes_before, named_route_keys_before = self.routes.dup, self.named_routes.keys
  yield
  [self.routes - routes_before, self.named_routes.except(*named_route_keys_before)]
end

.compileObject

Defines the match function for this class based on the compiled_statement.



122
123
124
125
# File 'lib/merb-core/dispatch/router.rb', line 122

def compile
  puts "compiled route: #{compiled_statement}" if $DEBUG
  eval(compiled_statement, binding, "Generated Code for Router#match(#{__FILE__}:#{__LINE__})", 1)
end

.compiled_statementObject

Returns

String

A routing lambda statement generated from the routes.



108
109
110
111
112
113
114
115
116
117
118
# File 'lib/merb-core/dispatch/router.rb', line 108

def compiled_statement
  @@compiler_mutex.synchronize do
    @@compiled_statement = "def match(request)\n"
    @@compiled_statement << "  params = request.params\n"
    @@compiled_statement << "  cached_path = request.path\n  cached_method = request.method.to_s\n  "
    @@routes.each_with_index { |route, i| @@compiled_statement << route.compile(i == 0) }
    @@compiled_statement << "  else\n    [nil, {}]\n"
    @@compiled_statement << "  end\n"
    @@compiled_statement << "end"
  end
end

.generate(name, params = {}, fallback = {}) ⇒ Object

Generates a URL based on passed options.

Parameters

name<~to_sym, Hash>

The name of the route to generate.

params<Hash, Fixnum, Object>

The params to use in the route generation.

fallback<Hash>

Parameters for generating a fallback URL.

Returns

String

The generated URL.

Alternatives

If name is a hash, it will be merged with params and passed on to generate_for_default_route along with fallback.



140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/merb-core/dispatch/router.rb', line 140

def generate(name, params = {}, fallback = {})
  params.reject! { |k,v| v.nil? } if params.is_a? Hash
  if name.is_a? Hash
    name.reject! { |k,v| v.nil? }
    return generate_for_default_route(name.merge(params), fallback)
  end
  name = name.to_sym
  unless @@named_routes.key? name
    raise "Named route not found: #{name}"
  else
    @@named_routes[name].generate(params, fallback)
  end
end

.generate_for_default_route(params, fallback) ⇒ Object

Generates a URL based on the default route scheme of “/:controller/:action/:id.:format”.

Parameters

params<Hash>

The primary parameters to create the route from (see below).

fallback<Hash>

Fallback parameters. Same options as params.

Options (params)

:controller<~to_s>

The controller name. Required.

:action<~to_s>

The action name. Required.

:id<~to_s>

The ID for use in the action.

:format<~to_s>

The format of the preferred response.

Returns

String

The generated URL.



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
# File 'lib/merb-core/dispatch/router.rb', line 170

def generate_for_default_route(params, fallback)
  query_params = params.reject do |k,v|
    [:controller, :action, :id, :format, :fragment].include?(k.to_sym)
  end

  controller = params[:controller] || fallback[:controller]
  raise "Controller Not Specified" unless controller
  url = "/#{controller}"

  if params[:action] || params[:id] || params[:format] || !query_params.empty?
    action = params[:action] || fallback[:action]
    raise "Action Not Specified" unless action
    url += "/#{action}"
  end
  if params[:id]
    url += "/#{params[:id]}"
  end
  if format = params[:format]
    format = fallback[:format] if format == :current
    url += ".#{format}"
  end
  unless query_params.empty?
    url += "?" + Merb::Request.params_to_query_string(query_params)
  end
  if params[:fragment]
    url += "##{params[:fragment]}"
  end
  url
end

.prepare(first = [], last = []) {|Behavior.new({}, { :action => 'index' })| ... } ⇒ Object

Prepares new routes and adds them to existing routes.

Parameters

first<Array>

An array of routes to add before the generated routes.

last<Array>

An array of routes to add after the generated routes.

&block

A block that generates new routes.

Block parameters (&block)

new_behavior<Behavior>

Behavior for child routes.

Yields:

  • (Behavior.new({}, { :action => 'index' }))


87
88
89
90
91
92
# File 'lib/merb-core/dispatch/router.rb', line 87

def prepare(first = [], last = [], &block)
  @@routes = []
  yield Behavior.new({}, { :action => 'index' }) # defaults
  @@routes = first + @@routes + last
  compile
end

.prepend(&block) ⇒ Object

Prepends the generated routes to the current routes.

Parameters

&block

A block that generates new routes when yielded a new Behavior.



74
75
76
# File 'lib/merb-core/dispatch/router.rb', line 74

def prepend(&block)
  prepare([], @@routes, &block)
end

.reset!Object

Clear all routes.



56
57
58
# File 'lib/merb-core/dispatch/router.rb', line 56

def reset!
  self.routes, self.named_routes = [], {}
end

.route_for(request) ⇒ Object

Finds route matching URI of the request and returns a tuple of [route index, route params].

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.



45
46
47
48
49
50
51
52
53
# File 'lib/merb-core/dispatch/router.rb', line 45

def route_for(request)
  index, params = match(request)
  route = routes[index] if index
  if !route
    raise ControllerExceptions::NotFound, 
      "No routes match the request: #{request.uri}"
  end
  [route, params]
end