简体   繁体   中英

Rails 5 routing: Dynamic controller

Is it possible for the route below to dynamically select different controllers or at least for a single controller to dynamically call another controller?

get '*path' => 'routing#show

For example:

/name-of-a-person => persons#show
/name-of-a-place => places#show

I recall reading something about Rails 5 that would enable this but I can't find it again to save my life. It's possible I imagined it.

Another options is to have a RoutingController that depending on which path is received will call different controllers.

The use case is I have URLs in the database with a type, and the controller depends on what type is the URL. I'm thinking something like this:

get '*path' do |params|
  url = Url.find_by!(path: params[:path])
  case url.type
  when 'person'
    'persons#show'
  when 'place'
    'places#show'
  end
end

I post my second best solution so far; still waiting to see if anyone knows how to do this efficiently within the routes.

class RoutingController < ApplicationController
  def show
    url = Url.find_by!(path: params[:path])
    url.controller_class.dispatch('show', request, response)
  end
end

Hat tip to André for the idea.

You could define one controller and inside its action make something like this:

def generic_show
  url = Url.find_by!(path: params[:path])
  case url.type
  when 'person'
    controller = PersonController.new
    controller.request = request
    controller.response = response
    controller.show
  when 'place'
    ...
  end
end

However, I would recommend you to move the code you want to reuse to other classes and use them in both controllers. It should be easier to understand and maintain.

I think you may be able to do it using advanced routing constraints.

From: http://guides.rubyonrails.org/routing.html#advanced-constraints

If you have a more advanced constraint, you can provide an object that responds to matches? that Rails should use. Let's say you wanted to route all users on a blacklist to the BlacklistController. You could do:

class BlacklistConstraint
  def initialize
    @ips = Blacklist.retrieve_ips
  end

  def matches?(request)
    @ips.include?(request.remote_ip)
  end
end

Rails.application.routes.draw do
  get '*path', to: 'blacklist#index',
    constraints: BlacklistConstraint.new
end

I don't think the Rails guide example is particularly good, because this problem could essentially be solved in your application controllers before_action.

In this example, the constraint is used for IP filtering, but you could also implement matches? to check if it's a person. I would imagine something like

def matches?(request)
  Person.where(slug: request.params[:path]).any?
end

And as such, the Rails router can decide whether or not to dispatch the request to the persons#show action.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM