rails route to specific id - ruby-on-rails-3

I have a pages controller with two records in the db; 'pages' and 'contact'.
The id for each page record is the title.
How do I write a specific route for each page?
I currently have a catch-all route which works...
match '/:id' => 'pages#show'
but I want to create a single route for each page

I probably don't understand your question because I have no idea why you would want to do that ;)
Anyhow, say you have a page what the title/id "about". This is what your route could look like:
match '/about' => 'pages#show', :defaults => { :id => 'about' }
cf. http://guides.rubyonrails.org/routing.html#defining-defaults
Note: I wouldn't call the route you're using already a "catchall"; it's a pretty normal Rails route. This is what I would call a catchall:
match ':controller(/:action(/:id))'

Related

Changing the default rails route

In the rails the default behavior shows the id in the url like
http://0.0.0.0:3000/users/1
Now to change the url to show me something other than the id, I know I could put this in model
def to_param
name
end
And then get something like http://0.0.0.0:3000/users/mikey
I am wondering how can achieve a twitter style url. Currently the model name is still showing in the url. Something like this http://0.0.0.0:3000/mikey without the model name
Get the name of the database column storing your users’ names (let’s say it’s ‘name’).
In config/routes.rb add, somewhere above the default route:
match 'users/:name', :controller => 'users', :action => 'show'
Now, in users_controller, find def show and change it to:
#user = User.find_by_name(params[:name])
Lastly, all the id-based urls pointing to your users need to be updated to reflect the name-based change. Like the one your Users index.html.erb file.
link_to #user.name, 'users/#{#user.name}'
Add a route, near the bottom of your routes file (right above your root route!)
get '/:id', :to => "users#show", :as => :friendly_user
This will act as a fall-through route, so /anything that wasn't caught by an earlier route will route to users#show and pass an :id accordingly. You can then use friendly_user_path(#user) to generate links to that user's Twitter-style profile.
Be sure that this is one of the lowest-priority routes, as you wouldn't want a user to be able to sign up with a username like "logout" and replace your /logout page with their profile!

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.)

Rails -- rename restful route

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?

How to give a different view for each user in Rails 3?

So I have my table users and each record has a username field
and I have my root to controller main and action `index
root :to => 'pages#main'
so if go to http://mydomain.com It'll show my main#index page and i have the about and contact pages as well.
but if i go to http://mydomain.com/Mr_Nizzle it shows me the page of the user Mr_Nizzle which is on users#show (just like example) and as well for the other users to show every user's page...
is it right if i go =>
match ':username' => 'users#show'
match 'contact_us' => 'main#contact'
match 'about' => 'main#about'
root :to => 'pages#main'
so i can leave all the logic on route instead of in the main controller?
Thanks.
I'm not sure exactly what your asking, however this may be of some help. I think (at least for about and contact) the correct syntax is match '/contact_us' => 'main#contact (the same for about but replace contact with about), the root :to => 'pages#main' is correct (that's the same as doing match '/' => 'pages#main')
What are you asking about the match ':username' => 'users#show'? Do you want (for example) a url to be http://mydomain.com/users_username ? Where users_username is the name of the user's profile that they are navigating to? If so, i think you can do something like match '/:username' => 'users#show' but i'm not entirely sure.
Get back to me on what works and what doesn't

Rails 3 routes: How to avoid conflict with 'show' action?

I currently have the following routes set up for photos:
resources :photos
match 'photos/:user' => 'photos#user', :as => :user_photo
match 'photos/:user/:key' => 'photos#show', :as => :show_photo
Those two match routes give me URLs like:
http://example.com/photos/joe_schmoe
http://example.com/photos/joe_schmoe/123xyz
...similar to the way Flickr formats its URLs.
The problem I'm having, though, is that the photos/:user route is either interpreted as the show method, or if I put those custom routes before the resources then routes like /new get interpreted as the user method.
How can I get around that without having to do a ton of custom routes?
You'll want to put this custom route above the resources :users, so that it is matched first in the routes.
match 'photos/:user' => 'photos#user', :as => :user_photo
resources :photos
Additionally you can disable the show action altogether:
resources :photos, :except => :show
It's not a good idea to mix restful routes with custom match routes on the same resource. As you observed these two routes will intercept each others actions. Your best choice is to pick only one routing system for a resource and stick with it. If you want flickr style routes you should remove the restful route and add the other necessary match routes for new/create/etc you might need. If you desperately want to keep both of these routes You either need to disable show from the rest route with the rest route being on top, or you disable new from the match route while match being on top. You can use regexp to filter out some requests from match like this:
match 'photos/:user' => 'photos#user', :as => :user_photo, :constraints => { :user => /.*[^n][^e][^w].*/ }
This gets ugly really fast tho and I suggest just not using the rest route at all.