Rails Routes: How to match Rails routes based on a pattern - ruby-on-rails

I have some routes like:
get 'route1' => 'controller#route1', as: 'route1'
get 'route2' => 'controller#route2', as: 'route2'
get 'route3' => 'controller#route3', as: 'route3'
How can I match more routes automatically with this pattern, e.g. 4, 5...

I am not sure how you can handle as part of route. But you can write this code in another way. You can create a route that handle all such routes at the end of your primary route as below:
get '/:route' => 'controller#route_for_all_views'
In your controller you should have this route_for_all_views action, which can handle all pages.
class SomeController < ApplicationController
def route_for_all_views
# handle your views and code with params[:route] here
end
end

I think you can do something like this:
get "/:action", to: "controller", constraints: {action: /route\d+/}
Please see dynamic segments for routes.
(also note that this would raise an exception if your controller doesn't have the method so you might need to use something like method_missing)

This may be a messy solution, but you could also do something like this, which will give you the *_path and *_url url helpers, that you get when you use the :as option.
%w{ route1 route2 route3 route4 route5 }.each do |route|
get route, to: "controller##{route}", as: route
end

Related

Rails Routing: Set specific controller action to a different path

I am looking to set the show action for a controller to a specific path (a path without the controller prefix). I know this can be done by controller by doing this
resources :items, path: ''
But is there a way to do this on only one specific action within the controller?
My end goal is to be able to say www.example.com/my-item-name and take the user to the item without changing the URL. I tried using a catchall route but redirecting adds the prefix back which I do not want.
Any ideas?
You can specify which controller and action respond to a certain route in your routes.rb file, like this:
get 'something', to: 'controller_name#action_name'
See Rails Routing Guide.
You can define single routes manually with the match, get,post, put, macros:
get :bar, to: 'foos#bar'
get :bar, controller: 'foos' # works the same as above
post :bar, to: 'foos#bar'
You can also use scope if you want to route multiple routes to the same controller more elegantly:
scope controller: 'foos' do
get :bar
get :baz
end

route is redirecting to wrong path

This is what my routes currently look like:
which gives
On my homepage I have a create vacancy button
<%= link_to "plaats", new_employer_vacancy_path(:employer_id)%>
Which should be linked to the line from the first image
get '/employers/:employer_id/vacancies/new', to: 'vacancies#new', as: 'new_employer_vacancy'
In the vacancies_controller#new - create I have:
def new
#vacancy = Vacancy.new
#employervacancy = Employervacancy.new
end
def create
#vacancy = Vacancy.create(vacancy_params)
createEmployervacancy
redirect_to employer_vacancy_path(current_employer, #vacancy)
end
def createEmployervacancy
#employer = current_employer
Employervacancy.create(vacancy_id: #vacancy.id, employer_id: #employer.id)
end
But whenever I click the button I get redirected to some other method in my vacancies_controller that is totally irrelevant.
How is this even possible? Don't I clearly define that when that path is clicked he should go to vacancies#new? and not to vacancies#show_specific_employer_vacancies?
EDIT
After following the answers I am indeed being linked to the correct route.
First, it gave me this error.
After trying to pass the current_employer.id instead of #employer like suggested I got following error:
For your routes, you'd better to change into nested route for easily maintaining routes.
Remove these codes:
get '/employers/:employer_id/vacancies/:id', to:"vacancies#show_specific_employer_vacancies", as: "employer_vacancy"
get '/employers/:employer_id/vacancies/edit/:id' ...
get '/employers/:employer_id/vacancies/index' ...
get '/employers/:employer_id/vacancies/new' ...
path '/employers/:employer_id/vacancies/:id' ...
change into:
resources :employers do
resources :vacancies
end
Try to use basic routes here because you use standard simple form url. For example:
<%= simple_form_for(#employee, #vacancy) %>
The simple_form_for will generate url well if you use nested routes above.
Finally, in your link you have to add #employer_id
<%= link_to "plaats", new_employer_vacancy_path(:employer_id => #employer_id)%>
I hope this help you
Your router cannot tell the difference between your employer_vacancy and new_emplyer_vacancy routes because the :id parameter accepts anything. Because of this, when you point your browser to "/employers/5/vacancies/new", the route is taking your employer_vacancy route and assigning {:employer_id => 5, :id => "new"} instead of going to your new_employer_vacancy route (because routes are first-come-first-serve).
To correct this, add a constraint to your first route to ensure that only numbers (and not the string "new") is accepted into the employer_vacancy route:
get '/employers/:employer_id/vacancies/:id',
to: 'vacancies#show_specific_emplyer_vacancies',
as: 'employer_vacancy',
constraints: { id: /\d+/ } # <- This line
As Wes Foster said rails router is trying to find a first match.
It means that given a path /employers/999/vacancies/new your router looks through the routes and when it sees get '/employers/:employer_id/vacancies/:id he thinks that this route matches. So :employer_id is 999 and :id is new.
I'd suggest to put the route with :id at the end of employers routes:
...
get '/employers/:employer_id/vacancies/new'
...
get '/employers/:employer_id/vacancies/:id'
Btw this is better than adding a constraint because:
It is easier
It doesn't pollute routes file
Later you may want to change ids to be hashed or alphabetic and then you'd have to change the constraint

Rails same named route but with parameter raises an error

As of Rails 4.2, I cannot do the following:
get 'profile', to: 'profile#index', as: 'profile'
get 'profile/:slug', to: 'profile#show', as: 'profile'
because it will raise error saying route is already defined. Why is that? Obviously profile_path and profile_path(User.last.slug) are not the same, and there should be no difficulty differentiating the two even if they happen to share the same base name (You check if a param is passed).
Thoughts?
In rails, the helper names for different routes should be different. And hence, as you rightly understood, you will receive an error if you use the same helper name (ie as: 'profile' in your case) for two different routes.
This restriction in Rails helps maintain sanity in your routes.rb file as well as in your application. For instance consider two methods for a controller:
class XyzController < ApplicationController
def method_a(param1)
end
def method_b(param1)
end
end
In your routes file if there was no restriction of keeping helper names different, you could have used :
get 'xyz/method_a', to: 'profile#method_a', as: 'profile_method'
get 'xyz/method_b', to: 'profile#method_b', as: 'profile_method'
Correspondingly in your view file:
link_to 'link_1', profile_method_path('param1') #intended to route for method_a
link_to 'link_2', profile_method_path('param2') #intended to route for method_b
As obvious, in the view file, not only is it difficult to make out which route is intended for which method, its also not possible to route to any other controller method using the helper 'profile_method' except the method that is first to use this helper in your routes.rb file (as routes are read sequentially).
Hope this helps :)
For both routes you specified as: 'profile' and that's your problem here. Besides that, use pluralized route names for #index action, e.g:
get 'profiles', to: 'profile#index'

Do you have to define controller helpers to get paths for new routes in rails?

I have a Customer model and I want his controller to repond to a find method
I added this in my routes.rb file:
match 'customers/find/:name' => 'mymodel#find' resources :customers
In my controller I have something like this:
def find
#customers = Customer.fin_all_by_name(params[:name])
end
in my views, when I need to create a link for that resource I'm using this:
= link_to 'Find By Name', :controller => "customers", :action => "find", :name => #customer.name
now, I'm trying integration tests with cucumber and I have a problem: I have to create a step definition in my customer_step.rb file for customers having same name:
when /^customers having same name as "(.*)"/
url_encode('/customers/find/' + $1)
now that line doesn't work, it says undefined method `url_encode'
I need to encode that string because if the name contains a space I get obvious errors.
I'm new to ruby and rails and I think I'm missing something here.
Am I following the right pattern to accomplish this search?
Should I define an helper method in my controller to generate search urls?
Is it correct that line I have in my _step.rb file?
I don't want urls to be like this: customers/find?name=test
but: customers/find/test
I just sorted it out, I slightly modified my match expression and added the :as parameter
and this gave me the possibility to call find_path() helper method
match 'customers/find/:name' => 'customers#find', :as => :find
Is this correct?
Using :as should indeed create a route helper for you. If you want to get a list of your matched routes, to which controller/action they route, and the name of the route helper, use rake routes in console.

rails controller action routing

I'm looking to "automatically" generate routes based on actions defined in a controller. resources routing (as far as I know) only automatically generates routes & helpers for http verbs. In this instance I'm serving mostly static pages using rails and have no need for http verbs in my controller.
Specifically:
In a controller I have defined actions which refer to those mostly static pages.
def action
end
In the routes file I have a bunch of
match "/url" => 'controller#action'
I'd like all those matched routes to be generated automatically based on the actions in the controller. Something CONCEPTUALLY along the lines of:
for actions in controller.erb do |action|
'match "/action" => "controller#action"
end
Is this possible? Would I write the code in the routes file directly?
I also have some nested actions to consider... a controller action may be:
def action
def nested_action
end
end
I'd appreciate any thoughts on this matter. Thanks.
What's wrong with the normal /:controller/:action idea?
That won't deal with nested actions, but... I'm having a difficult time understanding why you'd ever want that.
You can do something like this:
controller :controller_name do
get "path/action" => :method_name, :as => :path_action
post "path/save" => :method_name, :as => :path_save
end
That is, you can group different routes within a controller using the method above.

Resources