247

I need to know the current route in a filter in Rails. How can I find out what it is?

I'm doing REST resources, and see no named routes.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
luca
  • 12,311
  • 15
  • 70
  • 103
  • 3
    What are you trying to accomplish with this? When you say "route" do you mean "URI"? – jdl Jul 30 '09 at 01:14
  • any thoughts on how to get it in [middleware](http://stackoverflow.com/q/42691729/1610034). – Saurabh Mar 14 '17 at 08:03

14 Answers14

336

If you are trying to special case something in a view, you can use current_page? as in:

<% if current_page?(:controller => 'users', :action => 'index') %>

...or an action and id...

<% if current_page?(:controller => 'users', :action => 'show', :id => 1) %>

...or a named route...

<% if current_page?(users_path) %>

...and

<% if current_page?(user_path(1)) %>

Because current_page? requires both a controller and action, when I care about just the controller I make a current_controller? method in ApplicationController:

  def current_controller?(names)
    names.include?(current_controller)
  end

And use it like this:

<% if current_controller?('users') %>

...which also works with multiple controller names...

<% if current_controller?(['users', 'comments']) %>
IAmNaN
  • 10,305
  • 3
  • 53
  • 51
  • 30
    Note that you can also use current_page? with named routes: current_page?(users_path) – tothemario Sep 09 '11 at 12:13
  • Nice tothemario. I didn't know that. I'm modifying the answer. – IAmNaN Dec 04 '11 at 19:33
  • it returns true whatever address is "/users", "/users/", "/users?smth=sdfasf".... Sometimes not so good thing in practice – Gediminas Šukys Jun 05 '13 at 17:47
  • 5
    `controller_name` and `action_name` are good for use in helpers and views for this sort of thing too. – Matt Connolly Feb 04 '14 at 05:47
  • Building on @Gediminas and @MattConnolly comments... `current_page?` needs at least the controller and the action. If you need just the controller, you can use `controller_name == 'users'`. I make a `current_controller?` helper that does that check and put it in ApplicationController. – IAmNaN Sep 25 '14 at 19:37
  • 1
    In a view you can also just do <% if params[:action] == 'show' %> so you don't need the controller – rmcsharry May 12 '16 at 17:37
  • How do I apply this if I have a subdomain? – Acrux Sep 14 '17 at 06:58
  • I implemented this current_page logic in my application layout, and it ended up breaking the devise new confirmation page. I had this code: if current_page?(:controller => 'pages', :action => 'about'), and the users/confirmation/new page fails with this error: No route matches {:action=>"about", :controller=>"devise/pages"}. I fixed with if request.path == "/pages/about" from an answer below. – tomb Dec 01 '18 at 16:52
  • 1
    In Rails 6 (and probably as early as Rails 4 or 5), you can simply provide an active record object: `current_page?(@user)` or collection `current_page?(@users)`. Rails uses `polymorphic_path` under the hood to generate the path from the given active record object. Pretty neat! – Goulven Jul 17 '20 at 14:48
  • It's true! It goes back to 1.0.0 actually. This works because ActionView will interpret `@user` as the path `\user\:id`. If the person is on the edit action `users#edit` it will return false, however, because the actual current page is `\user\:id\edit`. So it will work depending on what you are trying to do. – IAmNaN Jul 19 '20 at 18:56
  • For best practice, the `current_controller?` method would be better defined in a helper (perhaps `ApplicationHelper`) instead of in a controller. – Anthony To Nov 23 '21 at 19:51
215

To find out URI:

current_uri = request.env['PATH_INFO']
# If you are browsing http://example.com/my/test/path, 
# then above line will yield current_uri as "/my/test/path"

To find out the route i.e. controller, action and params:

path = ActionController::Routing::Routes.recognize_path "/your/path/here/"

# ...or newer Rails versions:
#
path = Rails.application.routes.recognize_path('/your/path/here')

controller = path[:controller]
action = path[:action]
# You will most certainly know that params are available in 'params' hash
Swanand
  • 12,317
  • 7
  • 45
  • 62
  • 2
    Would you happen to know if this is the same/right way to do it in Rails 3? I'm sure it's still accessible, but I just want to be sure that I'm adhering to the latest conventions. – John Oct 29 '10 at 20:46
  • 40
    The current controller and action are always available in `params[:controller]` and `params[:action]`. However, outside of it, if you want to recognize the route, this API is not available anymore. It has now shifted to `ActionDispatch::Routing` and I haven't tried out the `recognize_path` on it yet. – Swanand Oct 30 '10 at 07:53
  • 41
    It’s better to use `request.path` for finding the current path. – Daniel Brockman Aug 21 '12 at 19:02
  • 3
    You could also call `request.env['ORIGINAL_FULLPATH']` to include the possible parameters in the path, see my answer below. – Darme Feb 18 '13 at 16:46
  • 2
    current_uri = request.env['PATH_INFO'] doesn't work if trailing_slash is set in routes – Gediminas Šukys Jun 05 '13 at 17:30
  • @luca The same approach does not work [here](http://stackoverflow.com/q/42691729/1610034), any thoughts? – Saurabh Mar 14 '17 at 08:02
177

Simplest solution I can come up with in 2015 (verified using Rails 4, but should also work using Rails 3)

request.url
# => "http://localhost:3000/lists/7/items"
request.path
# => "/lists/7/items"
Dennis Hackethal
  • 13,662
  • 12
  • 66
  • 115
21

You can do this

Rails.application.routes.recognize_path "/your/path"

It works for me in rails 3.1.0.rc4

Lucas Renan
  • 3,787
  • 3
  • 28
  • 35
  • This returns a hash which is the params hash. Any way to actually get the route object? With the name and then other attributes? – nroose Mar 27 '21 at 22:23
11

In rails 3 you can access the Rack::Mount::RouteSet object via the Rails.application.routes object, then call recognize on it directly

route, match, params = Rails.application.routes.set.recognize(controller.request)

that gets the first (best) match, the following block form loops over the matching routes:

Rails.application.routes.set.recognize(controller.request) do |r, m, p|
  ... do something here ...
end

once you have the route, you can get the route name via route.name. If you need to get the route name for a particular URL, not the current request path, then you'll need to mock up a fake request object to pass down to rack, check out ActionController::Routing::Routes.recognize_path to see how they're doing it.

KinOfCain
  • 111
  • 1
  • 4
9

Based on @AmNaN suggestion (more details):

class ApplicationController < ActionController::Base

 def current_controller?(names)
  names.include?(params[:controller]) unless params[:controller].blank? || false
 end

 helper_method :current_controller?

end

Now you can call it e.g. in a navigation layout for marking list items as active:

<ul class="nav nav-tabs">
  <li role="presentation" class="<%= current_controller?('items') ? 'active' : '' %>">
    <%= link_to user_items_path(current_user) do %>
      <i class="fa fa-cloud-upload"></i>
    <% end %>
  </li>
  <li role="presentation" class="<%= current_controller?('users') ? 'active' : '' %>">
    <%= link_to users_path do %>
      <i class="fa fa-newspaper-o"></i>
    <% end %>
  </li>
  <li role="presentation" class="<%= current_controller?('alerts') ? 'active' : '' %>">
    <%= link_to alerts_path do %>
      <i class="fa fa-bell-o"></i>
    <% end %>
  </li>
</ul>

For the users and alerts routes, current_page? would be enough:

 current_page?(users_path)
 current_page?(alerts_path)

But with nested routes and request for all actions of a controller (comparable with items), current_controller? was the better method for me:

 resources :users do 
  resources :items
 end

The first menu entry is that way active for the following routes:

   /users/x/items        #index
   /users/x/items/x      #show
   /users/x/items/new    #new
   /users/x/items/x/edit #edit
Alex Kitchens
  • 102
  • 1
  • 10
neonmate
  • 509
  • 4
  • 10
7

Or, more elegantly: request.path_info

Source:
Request Rack Documentation

dipole_moment
  • 5,266
  • 4
  • 39
  • 55
6

Should you also need the parameters:

current_fullpath = request.env['ORIGINAL_FULLPATH']
# If you are browsing http://example.com/my/test/path?param_n=N 
# then current_fullpath will point to "/my/test/path?param_n=N"

And remember you can always call <%= debug request.env %> in a view to see all the available options.

Darme
  • 6,984
  • 5
  • 37
  • 52
4

I'll assume you mean the URI:

class BankController < ActionController::Base
  before_filter :pre_process 

  def index
    # do something
  end

  private
    def pre_process
      logger.debug("The URL" + request.url)
    end
end

As per your comment below, if you need the name of the controller, you can simply do this:

  private
    def pre_process
      self.controller_name        #  Will return "order"
      self.controller_class_name  # Will return "OrderController"
    end
Aaron Rustad
  • 2,016
  • 17
  • 25
  • yes I did that, but I hoped in a better way. What I need is to know which controller has been called, but I have pretty complicated nested resources.. request.path_parameters('controller') doesn't seem to work properly to me. – luca Jul 30 '09 at 08:12
  • No need for `self.` in `self.controller_name` and `self.controller_class_name` – Dennis Hackethal Jul 24 '15 at 18:06
4

request.url

request.path #to get path except the base url

Charles Skariah
  • 670
  • 6
  • 18
2

You can see all routes via rake:routes (this might help you).

  • I prefer to open a new tab with an invalid path, and see all the path/routes from the browser, since they're prettier that way. But I don't think this helps get the current route. – ahnbizcad Jul 02 '14 at 01:25
1

You can do this:

def active_action?(controller)
   'active' if controller.remove('/') == controller_name
end

Now, you can use like this:

<%= link_to users_path, class: "some-class #{active_action? users_path}" %>
1

I find that the the approved answer, request.env['PATH_INFO'], works for getting the base URL, but this does not always contain the full path if you have nested routes. You can use request.env['HTTP_REFERER'] to get the full path and then see if it matches a given route:

request.env['HTTP_REFERER'].match?(my_cool_path)
Dan Rice
  • 51
  • 5
0

You can do request.env['REQUEST_URI'] to see the full requested URI.. it will output something like below

http://localhost:3000/client/1/users/1?name=test
Vbp
  • 1,912
  • 1
  • 22
  • 30