Class: ActionController::Routing::RouteSet

Inherits:
Object
  • Object
show all
Defined in:
lib/action_controller/routing.rb

Overview

:nodoc:

Defined Under Namespace

Classes: Mapper, NamedRouteCollection

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeRouteSet

Returns a new instance of RouteSet.



1153
1154
1155
1156
# File 'lib/action_controller/routing.rb', line 1153

def initialize
  self.routes = []
  self.named_routes = NamedRouteCollection.new
end

Instance Attribute Details

#named_routesObject

Returns the value of attribute named_routes.



1151
1152
1153
# File 'lib/action_controller/routing.rb', line 1151

def named_routes
  @named_routes
end

#routesObject

Returns the value of attribute routes.



1151
1152
1153
# File 'lib/action_controller/routing.rb', line 1151

def routes
  @routes
end

Instance Method Details

#add_deprecated_named_route(name, deprecated_name) ⇒ Object



1208
1209
1210
# File 'lib/action_controller/routing.rb', line 1208

def add_deprecated_named_route(name, deprecated_name)
  named_routes.define_deprecated_named_route_methods(name, deprecated_name)
end

#add_named_route(name, path, options = {}) ⇒ Object



1204
1205
1206
# File 'lib/action_controller/routing.rb', line 1204

def add_named_route(name, path, options = {})
  named_routes[name] = add_route(path, options)
end

#add_route(path, options = {}) ⇒ Object



1198
1199
1200
1201
1202
# File 'lib/action_controller/routing.rb', line 1198

def add_route(path, options = {})
  route = builder.build(path, options)
  routes << route
  route
end

#build_expiry(options, recall) ⇒ Object



1230
1231
1232
1233
1234
1235
# File 'lib/action_controller/routing.rb', line 1230

def build_expiry(options, recall)
  recall.inject({}) do |expiry, (key, recalled_value)|
    expiry[key] = (options.key?(key) && options[key] != recalled_value)
    expiry
  end
end

#builderObject

Subclasses and plugins may override this method to specify a different RouteBuilder instance, so that other route DSL’s can be created.



1160
1161
1162
# File 'lib/action_controller/routing.rb', line 1160

def builder
  @builder ||= RouteBuilder.new
end

#clear!Object



1170
1171
1172
1173
1174
1175
# File 'lib/action_controller/routing.rb', line 1170

def clear!
  routes.clear
  named_routes.clear
  @combined_regexp = nil
  @routes_by_controller = nil
end

#draw {|Mapper.new(self)| ... } ⇒ Object

Yields:



1164
1165
1166
1167
1168
# File 'lib/action_controller/routing.rb', line 1164

def draw
  clear!
  yield Mapper.new(self)
  named_routes.install
end

#empty?Boolean

Returns:

  • (Boolean)


1177
1178
1179
# File 'lib/action_controller/routing.rb', line 1177

def empty?
  routes.empty?
end

#extra_keys(options, recall = {}) ⇒ Object

Generate the path indicated by the arguments, and return an array of the keys that were not used to generate it.



1239
1240
1241
# File 'lib/action_controller/routing.rb', line 1239

def extra_keys(options, recall={})
  generate_extras(options, recall).last
end

#extract_request_environment(request) ⇒ Object

Subclasses and plugins may override this method to extract further attributes from the request, for use by route conditions and such.



1365
1366
1367
# File 'lib/action_controller/routing.rb', line 1365

def extract_request_environment(request)
  { :method => request.method }
end

#generate(options, recall = {}, method = :generate) ⇒ Object

Raises:



1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
# File 'lib/action_controller/routing.rb', line 1247

def generate(options, recall = {}, method=:generate)
  named_route_name = options.delete(:use_route)
  if named_route_name
    named_route = named_routes[named_route_name]
    options = named_route.parameter_shell.merge(options)
  end

  options = options_as_params(options)
  expire_on = build_expiry(options, recall)

  if options[:controller]
    options[:controller] = options[:controller].to_s
  end
  # if the controller has changed, make sure it changes relative to the
  # current controller module, if any. In other words, if we're currently
  # on admin/get, and the new controller is 'set', the new controller
  # should really be admin/set.
  if !named_route && expire_on[:controller] && options[:controller] && options[:controller][0] != ?/
    old_parts = recall[:controller].split('/')
    new_parts = options[:controller].split('/')
    parts = old_parts[0..-(new_parts.length + 1)] + new_parts
    options[:controller] = parts.join('/')
  end

  # drop the leading '/' on the controller name
  options[:controller] = options[:controller][1..-1] if options[:controller] && options[:controller][0] == ?/
  merged = recall.merge(options)

  if named_route
    path = named_route.generate(options, merged, expire_on)
    if path.nil? 
      raise_named_route_error(options, named_route, named_route_name)
    else
      return path
    end
  else
    merged[:action] ||= 'index'
    options[:action] ||= 'index'
  
    controller = merged[:controller]
    action = merged[:action]

    raise RoutingError, "Need controller and action!" unless controller && action
    # don't use the recalled keys when determining which routes to check
    routes = routes_by_controller[controller][action][options.keys.sort_by { |x| x.object_id }]

    routes.each do |route|
      results = route.send(method, options, merged, expire_on)
      return results if results && (!results.is_a?(Array) || results.first)
    end
  end
    
  raise RoutingError, "No route matches #{options.inspect}"
end

#generate_extras(options, recall = {}) ⇒ Object



1243
1244
1245
# File 'lib/action_controller/routing.rb', line 1243

def generate_extras(options, recall={})
  generate(options, recall, :generate_extras)
end

#load!Object Also known as: reload



1181
1182
1183
1184
1185
1186
# File 'lib/action_controller/routing.rb', line 1181

def load!
  Routing.use_controllers! nil # Clear the controller cache so we may discover new ones
  clear!
  load_routes!
  named_routes.install
end

#load_routes!Object



1190
1191
1192
1193
1194
1195
1196
# File 'lib/action_controller/routing.rb', line 1190

def load_routes!
  if defined?(RAILS_ROOT) && defined?(::ActionController::Routing::Routes) && self == ::ActionController::Routing::Routes
    load File.join("#{RAILS_ROOT}/config/routes.rb")
  else
    add_route ":controller/:action/:id"
  end
end

#options_as_params(options) ⇒ Object



1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
# File 'lib/action_controller/routing.rb', line 1212

def options_as_params(options)
  # If an explicit :controller was given, always make :action explicit
  # too, so that action expiry works as expected for things like
  #
  #   generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
  #
  # (the above is from the unit tests). In the above case, because the
  # controller was explicitly given, but no action, the action is implied to
  # be "index", not the recalled action of "show".
  #
  # great fun, eh?

  options_as_params = options.clone
  options_as_params[:action] ||= 'index' if options[:controller]
  options_as_params[:action] = options_as_params[:action].to_s if options_as_params[:action]
  options_as_params
end

#raise_named_route_error(options, named_route, named_route_name) ⇒ Object

try to give a helpful error message when named route generation fails



1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
# File 'lib/action_controller/routing.rb', line 1303

def raise_named_route_error(options, named_route, named_route_name)
  diff = named_route.requirements.diff(options)
  unless diff.empty?
    raise RoutingError, "#{named_route_name}_url failed to generate from #{options.inspect}, expected: #{named_route.requirements.inspect}, diff: #{named_route.requirements.diff(options).inspect}"
  else
    required_segments = named_route.segments.select {|seg| (!seg.optional?) && (!seg.is_a?(DividerSegment)) }
    required_keys_or_values = required_segments.map { |seg| seg.key rescue seg.value } # we want either the key or the value from the segment
    raise RoutingError, "#{named_route_name}_url failed to generate from #{options.inspect} - you may have ambiguous routes, or you may need to supply additional parameters for this route.  content_url has the following required parameters: #{required_keys_or_values.inspect} - are they all satisifed?"
  end
end

#recognize(request) ⇒ Object



1314
1315
1316
1317
1318
# File 'lib/action_controller/routing.rb', line 1314

def recognize(request)
  params = recognize_path(request.path, extract_request_environment(request))
  request.path_parameters = params.with_indifferent_access
  "#{params[:controller].camelize}Controller".constantize
end

#recognize_path(path, environment = {}) ⇒ Object

Raises:



1320
1321
1322
1323
1324
1325
1326
# File 'lib/action_controller/routing.rb', line 1320

def recognize_path(path, environment={})
  path = CGI.unescape(path)
  routes.each do |route|
    result = route.recognize(path, environment) and return result
  end
  raise RoutingError, "no route found to match #{path.inspect} with #{environment.inspect}"
end

#routes_by_controllerObject



1328
1329
1330
1331
1332
1333
1334
1335
1336
# File 'lib/action_controller/routing.rb', line 1328

def routes_by_controller
  @routes_by_controller ||= Hash.new do |controller_hash, controller|
    controller_hash[controller] = Hash.new do |action_hash, action|
      action_hash[action] = Hash.new do |key_hash, keys|
        key_hash[keys] = routes_for_controller_and_action_and_keys(controller, action, keys)
      end
    end
  end
end

#routes_for(options, merged, expire_on) ⇒ Object



1338
1339
1340
1341
1342
1343
1344
1345
# File 'lib/action_controller/routing.rb', line 1338

def routes_for(options, merged, expire_on)
  raise "Need controller and action!" unless controller && action
  controller = merged[:controller]
  merged = options if expire_on[:controller]
  action = merged[:action] || 'index'
    
  routes_by_controller[controller][action][merged.keys]
end

#routes_for_controller_and_action(controller, action) ⇒ Object



1347
1348
1349
1350
1351
1352
# File 'lib/action_controller/routing.rb', line 1347

def routes_for_controller_and_action(controller, action)
  selected = routes.select do |route|
    route.matches_controller_and_action? controller, action
  end
  (selected.length == routes.length) ? routes : selected
end

#routes_for_controller_and_action_and_keys(controller, action, keys) ⇒ Object



1354
1355
1356
1357
1358
1359
1360
1361
# File 'lib/action_controller/routing.rb', line 1354

def routes_for_controller_and_action_and_keys(controller, action, keys)
  selected = routes.select do |route|
    route.matches_controller_and_action? controller, action
  end
  selected.sort_by do |route|
    (keys - route.significant_keys).length
  end
end