Adding a route to match a specific pattern - ruby-on-rails

I have a controller in Ruby on Rails and I would like to do the following:
When a user is trying to access the controller I want to match some parameters. For example
domain.com/hello/19213/sayHello
I want to translate this to:
domain.com/hello/:id/:method
in my routes files.
Keep in mind that :method parameter might not exist but :id will always be there.
In addition if it is possible I would like to match the specific :method with a method in my controller. If not I plan to use a switch case.
Can someone provide an example for this and what I have to put in my routes.rb?
I found this:
match ':controller(/:action(/:id(.:format)))'
But I believe that this is somehow different as I only want to match router for the specific controller not every controller in my project.
Thanks

By method, I assume you mean a method you have in your model. If that is the case, I believe you want something like this in your routes.rb
match ':controller/:id(/:method)'
The parentheses around :method mean that it is optional.
If you do not include a method, it will evaluate your SHOW action with params[:id] = :id
If you do include a method, it will evaluate the :method with params[:id] = :id
This is for methods you have defined in your controller

Related

How does this rails route match any incoming action to it's correlated method on the controller?

I have a Rails route defined like this:
match '/test_report(/:action)' => 'test_report#index', via: :all
I am not an expert on Rails routing, but using context clues I would have expected that this route to map any request like /test_report/some_action or /test_report/other_action to the index method on the TestReportController, because of the part of the route definition after the hash rocket: => 'test_report#index'.
However, this is not the behavior. Instead, I can create another method on the TestReportController called update_report, and then I can POST to /test_report/update_report to trigger the update_report method. From the route definition I'm using, I wouldn't expect this to work, but it does. I would have expected the POST to hit the index method on the controller.
Note: Just so we're clear, I do understand that the (/:action) part of the route marks it as an optional part of the URL, and the :action symbol is special here and is interpreted as an action's name.
TL;DR:
If the => 'test_report#index' in the above route doesn't map all requests to the index method on the controller, what does it actually do?
As you probably know, whatever parameters you define within the route pattern become available in the params hash in the controller.
So in your example, :action becomes available within the controller as params[:action], which is also used internally by Rails to load the correct action. (If you inspect, you'll see params[:route], params[:controller], params[:action], etc.)
I'm guessing that defining your own :action in the URL pattern like that causes Rails to prefer that over what's in your mapping (skipping the index in test_report#index).
If you want to live in both worlds, you'll probably want to name it something other than :action.

Submit a form to a Rails route with a dynamic segment

I have a route that looks like
match 'solar_systems/:planet_num/:moon_num' => 'solar_system#moon', :as => :moon
I'd like to have a form with a select box for planet number and moon number and have it submit to this route. However I cannot use moon_path because it will have an error if the dynamic parameters are not included in it like this moon_path(4, 1). Is what I want even possible? If so, what do I give to the form tag for the route?
You don't have to use the routing helper methods, and here you can't since at the time of rendering your form you do not know the required parameters. You do, however, know the controller and action, which is really all that's needed for the destination URL. So this should work:
= form_tag('/solar_systems/moon') do
= select_tag(:planet_num, ...
= select_tag(:moon_num, ...
This should render the form tag. To process the request, you will also have to add another route so the right controller action is called:
match 'solar_systems#moon' => 'solar_system#moon', :via => :post
Or, if it makes more sense in the context of your application, you could modify your existing route to make the parameters optional:
match 'solar_systems(/:planet_num(/:moon_num')) => 'solar_system#moon', :as => :moon
See this Rails guide for more details on non-resourceful routes.
If you use this params on controller you need to specified what params is each one, btw in you helper you need to do something like this
moon_path(planet_moon: 4, moon_num: 1)
Cheers!

Why is link_to with an absolute url considered technically superior to targetting controller action?

New to rails, so if this is discussed somewhere, just link me off: I had a good search but all I could find were people trying to figure out how to use link_to, not any discussion of this comment:
link_to "Profile", profile_path(#profile)
# => Profile
in place of the older more verbose, non-resource-oriented
link_to "Profile", :controller => "profiles", :action => "show", :id => #profile
# => Profile
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
I get that the latter is more verbose, and thus undesirable, but the former seems like a strange thing to be recommending.
If I have an action at say: /blah/add and I link to it using:
link_to "Link", link_add_path
Then I'm linking to mysite.com/link/add. This is a hard coded url.
If I change the route that this maps to, I have to change every instance of link_to in my code base to point to the new absolute url. This seems crazy.
However, if I link to it using:
link_to "Link", :controller => "thing", :action => "add"
Then the url is dynamically determined. If I have to change the path all I do is edit config/routes.rb and not touch any of my code. This seems like much lower maintenance.
I appreciate it's slightly more complex than that, the blah_path variable is not actually a static route, and actually contains some smarts like the application base url and prevents you from linking to urls that don't exist, but it seems like a step backwards to facilitate a fractionally less verbose syntax.
So, what's up with that?
What technical reason would you choose the former link_to syntax over the latter?
"You're doing it wrong" :P
Seriously though: use named resources, and here's why that's cool:
Example:
you've got this in your routes file:
resources :user_orders
And you are using "user_orders_path" everywhere. Then you do a refactor and decide (because the orders are now generic) that you want the controller to be called "orders" but you don't want to break all your old code. you can do this:
resources :user_orders, controller: "orders"
And your existing links will continue to work! (plus you can add a "orders" resource to move things over to the new scheme)
There's also neat things like named links:
match 'exit' => 'sessions#destroy', :as => :logout
I'd also like to add, if you needed to refactor your controller using the old link syntax - you'd still have to change a pile of controller links!
Then I'm linking to mysite.com/link/add. This is a hard coded url.
No, it's not. link_add_path is a method generated by Rails that points to a specific route in your config/routes.rb. You can see this by running
rake routes | grep link_add
If I change the route that this maps to, I have to change every instance of link_to in my code base to point to the new absolute url. This seems crazy.
No, you don't. Take the following example
get "link/add", as: :link_add, controller: :links, action: :add
If I run the above
rake routes | grep link_add
I get
link_add GET /link/add(.:format) links#add
But what if I change the name of my controller to UrisController? Just change the route in config/routes.rb
get "link/add", as: :link_add, controller: :uris, action: :add
and now you have
link_add GET /link/add(.:format) uris#add
The link_to's don't have to change because the link_add_path method is still mapped to the newly modified line in my config/routes.rb because the route name is the same. With your more explicit way of specifying controllers/actions for every link_to, you have to go through every link and update it manually to reflect the new controller: :uris controller.
Read about Naming Routes in the rails guide.

Routing custom action in Rails 3

I have a very simple question.
Trying to figure out what is the simplest way to route the custom action in rails 3.
Let's say i have controller UsersController and action promote_to_premium
Nor
http://localhost:3000/users/#{user_id}/promote_to_premium
neither
http://localhost:3000/users/promote_to_premium/#{user_id}
works.
Should I specify in routes.rb every custom action that differs from new/delete/update/create/ect/....?????
Thank You.
Yes you need to specify in your routes.rb.
Example:
resources :users do
member do
post :promote_to_premium
end
end
This way you can access the route like this:
http://localhost:3000/users/#{user_id}/promote_to_premium
You should use this in routes.rb:
match "/users/:id/promote_to_premium" => "users#promote_to_premium"
You should mention the route in routes.rb file for custom methods in the controller.
You can specify the routes using either get"" or a match""=>"" or a "post"
when you write get "controller/something" something should be an action(method) called by the name "something" in your controller. But in your case you cannot use get"controller/:id" as there is no ":id" method in your controller. So, you should match your controller/:id to some 'action' in your controller.
Hence you need to write
"match users/:id/promote_to_premium"=> "users#promote_to_premium"
But if you are writing something into the database then you should use 'post'. From whatever i know, i think you can try
match 'users/:id/promote_to_premium' => 'users#promote_to_premium', :via => :post
You can study more about routes in the below link:
http://guides.rubyonrails.org/routing.html
Yes you need to specify every route. Actually you define the normal routes too with the resource command.
There is a specific wildcard command to allow access of any action, but it is only for debug purposes, because it allows access to actions you may not want to be accessible:
match ':controller(/:action(/:id(.:format)))'

I want the following urls pattern, please help I'm using rails 3

I want the following urls for my UserController:
localhost/user/join
localhost/user/login
localhost/user/user_name (this can be any name in here, it should fire the 'profile' action)
Then within the /user/user_name_here/ folder I want:
/user/user_name/blah1/
/user/user_name/blah2/
/user/user_name/blah3/
It seems doing resources :user only creates things for index/show/get, so I'm confused as to how to do this other than creating so many match '/user/join' etc. lines in routes.
match "user/:user_name" => "users#show"
then /user/username will redirect to the User controller, call the show method, and pass the :user_name param
you could do the same to other actions that doesn't neet parameters,
match '/user/login' => "sessions#new"
match '/user/join' => "user#new"
Yup - 'resources :user' is just scaffolding for the usual CRUD methods. If you want paths additional to those, you're going to have to create routes (it's the only way your app knows how to route a given URL to a given controller and method).
The friendly_id gem does something similar to what you're suggesting (though I believe it's monkey-patching the .find method on ActiveRecords classes rather than handling routing specifically).

Resources