Changing the default rails route - ruby-on-rails-3

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!

Related

Rails routes won't show the name of the route

I added a route in routes.rb:
match 'notifications/:id/mark_as_read' => 'notifications#mark_as_read', :via => :post
mark_as_read is the only action in notifications controller, and that is the only route for notifications
when I should the routes I see
POST /notifications/:id/mark_as_read(.:format) notifications#mark_as_read
I don't know how to get to it, since it doesn't list as something like notifications_mark_as_read_path
did I do something wrong when I added the route, or can I name the route somehow?
found it..
need to name the route.. with :as => 'my_route_name'

Rails 3: GET request with email crops off TLD

I am trying to use a controller and action to do a simple check to see if a user account with that email address exists or not.
The controller's action looks like this:
def checkEmail
email = params["email"]
if Account.find_by_email(email).blank?
render :inline=>"true"
else
render :inline=>"false"
end
end
And to test this action, I can go to:
http://localhost:3000/home/checkEmail/email#website.com
When I do so, I can see in the Ruby console the following being queried:
Parameters: {"email"=>"email#website"}
Account Load (0.0ms) SELECT `accounts`.* FROM `accounts` WHERE `accounts`.`email` = 'email#website' LIMIT 1
You can see that the TLD of the email address has been cropped off.
However, when I go to:
http://localhost:3000/home/checkEmail/email#website.co.uk
I get a routing error:
No route matches [GET] "/home/checkEmail/email#website.co.uk"
My routes.rb file looks like this:
Gallery::Application.routes.draw do
#Match home URL
match 'home(/:file)' => "home#index"
match 'home/checkUser/:username' => "home#checkUser"
match 'home/checkEmail/:email' => "home#checkEmail"
root :to=> "home#index"
end
Yeah, the default behavior in Rails is to treat a dot (.) in a route as a format specifier, instead of part of the parameter.
In order to match the dot as part of the parameter, specify the route like this:
match "/home/checkEmail/:email" => "home#checkEmail", :constraints => { :email=> /[^\/]*/ }
Here's a post that describes the problem and the fix:
http://coding-journal.com/rails-3-routing-parameters-with-dots/

Questions about rails3 routes

I'm upgrading my app to rails 3, and I am a bit confused about some of the routes. The resourceful ones are easy enough, but how can I set a generic rule for all actions in a specific controller. I tried something like this:
get 'custom/:action/' => {:controller => :custom}
But that didn't work. It seems the new format is "controller#action", but how can I specify the action to be variable?
Also, other than using named routes or resources, is it possible to do shorthand notation to name routes in a specific controller?
i.e. rather than:
get '/tasks', :controller => :home, :action => :tasks, :as => 'tasks_home'
get '/accounts', :controller => :home, :action => :accounts, :as => 'accounts_home'
is it possible to do something a little cleaner, like:
controller => :home do
get :tasks
get :accounts
end
And that would automatically created the named routes?
You can use action as a variable like this:
resource :custom do
match ':action'
end
This will generate
/custom/:action(.:format) customs#:action
custom POST /custom(.:format) customs#create
new_custom GET /custom/new(.:format) customs#new
edit_custom GET /custom/edit(.:format) customs#edit
GET /custom(.:format) customs#show
PUT /custom(.:format) customs#update
DELETE /custom(.:format) customs#destroy
So it will handle your action as a variable URL-s and will add some default CRUD actions as well.
Note that the controller name here is in plural. If you would like to use a route for a controller which name is in singular, use resources instead of resource.
The answer to the second question is almost identical to the first one, use resource:
resource :home do
get :tasks
get :accounts
end
generates:
tasks_home GET /home/tasks(.:format) homes#tasks
accounts_home GET /home/accounts(.:format) homes#accounts
home POST /home(.:format) homes#create
new_home GET /home/new(.:format) homes#new
edit_home GET /home/edit(.:format) homes#edit
GET /home(.:format) homes#show
PUT /home(.:format) homes#update
DELETE /home(.:format) homes#destroy
Note that the matched controller names are in plural again, because of the convention.
Looks like this is related to the persisted field being set to false on nested ActiveResource objects: https://github.com/rails/rails/pull/3107

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.