Rails -- rename restful route - ruby-on-rails-3

I wanted to know what the easiest way to rename a restful route is. Basically I have a controller called Employees and rather than have employees/new I want employees/hire to be used and achieve the same thing and make employees/new an invalid url.

For your specific need, the guide has exactly this example for new, edit, this should work:
resources :employees, :path_names => { :new => 'hire' }
http://guides.rubyonrails.org/routing.html#overriding-the-new-and-edit-segments

One of the best sources of data on routes is the rails guide:
Rails Guide on Routes, also the command
rake routes
This command will show you all the current routes.
But in answer to this specification question
if you look into your routes file you can create new routes
manually.
match 'employee/hire' => 'Employees#new', :via => :get, :as => 'employee_path'
the first argument matches what the browser is looking for.
The second argument is the controller and method.
The third is if it is a get, put, post, or delete call.
The fourth is the name for the path so you can access with the standard name_path type of call from code.
This makes sense?

Related

Rails Routing based on Parameters

I am working on a Rails app, and I am looking for a way to route to different actions in the controller based on the existence of parameters in the url.
For example I want website.com/model to route to model#index, however I want website.com/model?opt=dev to route to model#show. Is there some way this can be done?
Use route constraints to look at the request object and see if it has URL parameters. If you're using restful routes, you want to put this "one-off" before the restful route. Something like this:
get 'users' => 'users#show', constraints: { query_string: /.+/ }
resources :users
So what this is saying is that if you request "/users?opt=dev" then it will match your special case. Otherwise, it falls through to your normal restful route to the index action. Your model#show action will then have to know to pick up the param[:opt] and do whatever with it.
Also, note that the regex is very loose and it's simply checking for ANY param...you'll want to tighten that up to fit whatever you're trying to do.
Not strictly the same, but if you came to this post and were wondering how to do the same via a POST, then you can do it based on the request_paramters.
for your routes.rb ..
module MyConstraintName
extend self
def matches?(request)
request.request_parameters["routeFlag"] == "routeToModelShow"
end
end
match "pages/:id", :via=>:post, :controller=>"model", :action=>"show", :constraints => MyConstraintName
and in your form for example..
<%= hidden_field_tag :routeFlag, "routeToModelShow" %>

root path for multiple controllers on rails routes

I have two resource controllers where I am using a slug to represent the ID. (friendly_id gem).
I am able to have the show path for one resource on the route but not for two at the same time. ie.
root :to => 'home#index'
match '/:id' => "properties#show"
match '/:id' => "contents#show"
Basically I want urls like,
# Content
domain.com/about-us
domain.com/terms
# Property
domain.com/unique-property-name
domain.com/another-unique-property-name
Whatever resource I put on top works. Is there a way to do this?
Thanks in advace if you can help.
This is untested, but try utilizing a constraint on your route.
root :to => 'home#index'
match '/:id', :to => "properties#show",
:constraints => lambda { |r| Property.find_by_id(r.params[:id]).present? }
match '/:id', :to => "contests#show",
:constraints => lambda { |r| Contest.find_by_id(r.params[:id]).present? }
Alternatively, you can create a separate class that responds to matches? instead of defining a lambda proc. (I recommend placing these classes into separate files that will autoload within your Rails app.)
# app/constraints/property_constraint.rb
class PropertyConstraint
def self.matches?(request)
property = Property.find_by_id(request.params[:id])
property.present?
end
end
# app/constraints/contest_constraint.rb
class ContestConstraint
def self.matches?(request)
contest = Contest.find_by_id(request.params[:id])
contest.present?
end
end
# config/routes.rb
root :to => 'home#index'
match '/:id', :to => "properties#show", :constraints => PropertyConstraint
match '/:id', :to => "contests#show", :constraints => ContestConstraint
Unfortunately this results in an extra DB query (once in the routes, and once more in your controller). If anyone has a suggestion on minimizing this, please share. :)
This Rails Engine does what you want:
Slug Engine at Github
Basically, the author's approach was to mount a Rails Engine inside his main app. This Engine incorporates both a controller for handling slugs that exist and a piece of middleware for filtering out and abstaining on slugs that don't exist.
He explains why he took this approach and other aborted solutions in a rather detailed and interesting blog post. This blog post and the slug engine source code should be enough detail for you to get your own code up and running, but that open-source engine seems to be exactly what you're looking for if you want a drop-in solution.
You can do it in a middleware
Detect slug in path
If Content with this slug exists - change request path to "contents/:id"
If Property with this slug exists - change request path to "properties/:id"
in your routing set:
match 'contents/:id' => "properties#show"
match 'properties/:id' => "contents#show"
You could write another controller which takes the id from the router and checks if the id belongs to properties or content and renders the appropriate view.
match '/:id' => "router#show"
The controller would do something like this:
def show
#property = Property.find(params[:id])
if #property then
render 'property/show'
else
#content = Content.find(params[:id])
render 'content/show
end
end
Havn't tested this code, but this idea should work.
I would suggest that you do this in a more RESTful way, if possible. Basically, you have two different resources and you should separate them:
match 'properties/:id' => "properties#show"
match 'contents/:id' => "contents#show"
This will give you many advantages down the road. One immediate benefit is that you can avoid clashes between ids for properties and content. (Note that friendly_id will not help you with inter-model slug clashes in your original scheme.)

Rail3 routes [edit problem]

Example I have had configured my routes as
match '/cellphones/:permalink/:charger', :controller => 'mycontroller', :action => 'myaction'
resources :cellphones
Everything ok when I put something like this localhost/cellphones/nokia3323/lion but I can't edit a cellphone because have the same structure localhost/cellphones/edit/4
Routes that are defined earlier take precedence, so you could make the edit route accessible again by reversing the order that you've declared your routes in. Since a route of /cellphones/edit/:id is more restrictive than /cellphones/:permalink/:charger, the edit route will match if the second part of the route is "edit" and pass through to your other route if it's something else.
However, you most likely don't actually have a /cellphones/edit/:id route, because what's created by resources :cellphones is /cellphones/:id/edit which is much harder to distinguish from /cellphones/:permalink/:charger since both have the wildcard part of the route as the second segment.
The easiest way the fix the problem would be to change the /cellphones/:permalink/:charger route so it's easier to distinguish. You could use something like /cellphones/p/:permalink/:charger ("p" for permalink), or anything else that's easy to distinguish from the RESTful routes created by resources.
There's a few other ways you could approach it as well, such as using segment contraints or adding more restful actions.
I found the solution, just had to change the order
resources :cellphones
match '/cellphones/:permalink/:charger', :controller => 'mycontroller', :action => 'myaction'

URL helper for a post route in a resource

I have a resourceful route, with a post route nested within it:
resources :groups, :only => [:index, :show] do
post 'send_audit_reminder', :on => :member
end
If I run rake routes, this route shows up just fine:
send_audit_reminder_group POST /groups/:id/send_audit_reminder(.:format)
{:controller=>"groups", :action=>"send_audit_reminder"}
However, I can't seem to figure out how to refer to the send_audit_reminder URL for a given route. I've tried send_audit_reminder_group_path(#group) and send_audit_reminder_url(#group), which both give me the following error:
No route matches {:controller=>"groups", :action=>"send_audit_reminder"}
As you can see from rake routes, there is indeed a route that matches those parameters, and there is also a matching method on the controller.
How can I find the path or URL for this route? I would like not to hard code it, since our apps are deployed to subdirectories on the same virtual host, so a hard-coded absolute path won't work.
And where would I look for documentation or information on this in the future? Since these path and URL helper methods are generated from my routes, I obviously can't look for documentation, and while rake routes tells me that the route is there, it doesn't appear to be there when I try and get the URL.
It might be that you're missing the placeholders and it can't route because of that. The following should work based on your definition:
send_audit_reminder_group_path(group)
Any time you see identifiers like :id or :group_id in your route, you must supply them unless they are in brackets, which declares them as optional, as is the case here with :format. The arguments need to be supplied in the same order they are declared. For this:
/example/:user_id/groups/:id
The arguments to this route would be user_id and id and both must be supplied. Generally with routes you can either use a literal number or string, or a model that supports to_param as all ActiveRecord::Base-derived ones do.
This all stems from declaring with :member, meaning it is specific to a particular record, and not :collection where that is omitted. The Rails Routing Guide explains more.

Routing problem in ruby on rails

I am new to ruby on rails.
I used the command 'rails generate controller Courses new'
Then, I edited routes.rb file with:
resources :courses
match '/courses', :to => 'courses#new'
When I access http://0.0.0.0:3000/courses. I get an error:
Unknown action
The action 'index' could not be found for CoursesController.
I think i am missing something. Please help
Thanks.
The line
resources :courses generates the routes for courses like so:
/courses -> coursescontroller#index
/courses/:id -> coursescontroller#show
...
and so on. This is known as 'restful routes'.
If you do not want to direct a url of form 'courses.html' to the 'index' action of your courses controller, but to the 'new' action of your courses controller (which would be highly unusual, by the way), just remove the first line from your routes.rb.
If you want to see what routes you have defined, just do
rake routes
from your rails app directory.
You could use this instead:
resources :courses, :except => :index
match '/courses', :to => 'courses#new'
The except option takes a symbol or an array of actions in the controller you do not want to define resource routes for. In this case, we turn off the route for the index action, /courses/.
Next, we define the same route that would have been defined for the index action, but point it at CoursesController#new.
Put your "match" line before your "resources" line.