Change devise route from ID to Username - ruby-on-rails-3

I have tried using the post to change the routes to point at the username. I know this is simple, but for some reason I can't compute it at the moment. I have tried everything on the devise documentation page as well.
Drawing username routes
I just want to have the routes layout to use the username instead of id and not have the users prefix. Like:
http://example.com/username
instead of
http://example.com/users/1

class User < ActiveRecord::Base
def to_param
username
end
end
in your controller make
#user = User.find_by_username(params[:id])
instead of
#user = User.find(params[:id])
this will make your routes like http://example.com/users/username
to make what you want, you can do route like:
resources :users, :path => '' do
# nested resources...
end
so, user_path(#user) will make url http://example.com/username
but It's not a good practice, cause it's not a REST. I advise you to leave urls like http://example.com/users/username

Related

how can I make a simple route on rails and can I use it for an ajax form?

Ive been trying to create a simple route on rails, following this instructions
http://guides.rubyonrails.org/routing.html
my problem is that when I want to enter to my method I get a weird error.
I have a controler user and on my routes I wrote something like this
resources :users do
match "/custom/" => "user#custom"
end
So, at my controller I add this code
def custom
#user = User.find(params[:user_id])
end
but when I try to enter doing localhost:3000/users/1/custom I get an error like
uninitialized constant UserController
doing rake routes I can see
user_custom /users/:user_id/custom(.:format) user#custom
Any idea how to solve this problem?
I want this route to submit a form... is it possible to use this route (if i make it run) for use ajax? I want to submit a form.
Thanks
Change your route to:
resources :users do
match "/custom/" => "users#custom"
end
You should avoid the use of match though, since it will be deprecated in Rails 4. Try this instead
resources :users do
get :custom, on: :member
end
get is the verb, :custom the route and on: :member means that you are looking for a /users/:id/custom route instead of a /users/custom one. If you are looking for the latter, do this:
resources :users do
get :custom, on: :collection
end
Another way to do it is like this, which I prefer:
resources :users do
get 'custom', on: :collection
end
That gives you a route of /users/custom. If you were do use on: :member, then it would give you a route of /users/:id/custom.
You can also use a block for defining multiple custom actions for collections or members.
For example:
resources :users do
collection do
get 'custom'
post 'some_other_method'
end
member do
get 'some_action'
end
end

Correct routing for short url by username in Rails

I am trying to replace user profile views of the sort
/users/1
with
/username
I'm aware this means I'll need to check for collisions of various kinds. I've consulted the following similar SO questions:
Ruby on rails routing matching username
customize rails url with username
Routing in Rails making the Username an URL:
routing error with :username in url
Here are the various failed routes.rb route definitions I've tried, and associated errors:
match "/:username" => "users#show", via: "get"
Here's the error:
ActiveRecord::RecordNotFound in UsersController#show
Couldn't find User without an ID
app/controllers/users_controller.rb:7:in `show'
Here is my corresponding users_controller:
6 def show
7 #user = User.find(params[:id])
8 end
match "/:username" => 'users#show', :as => :profile
Same error as above.
match "/:username", :controller => "users/:id", :action => 'show'
Routing Error
uninitialized constant Users
Try running rake routes for more information on available routes.
match '/:username', :controller => 'users', :action => 'show'
Same error as 1.
match '/:username', to: 'users/:id', via: 'show'
Server does not start.
match "/:username" => redirect("/users/:id")
Error:
ActiveRecord::RecordNotFound in UsersController#show
Couldn't find User with id=:id
Any idea why my routing is not working the same way that everyone else who asks this question's is?
Update
Just to take this issue out of the comments and put it in the question more cleanly. After making the change by #Ryan Bigg below, I had a routing problem in my redirect to profile when a new one is created. Here's my create code:
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
flash[:success] = "Thank you for signing up."
redirect_to ('/'+#user.username)
#redirect_to #user, notice: "Thank you for signing up!"
else
render "new"
end
end
And here is my user.rb
def to_param
self.username
#username
end
However, the commented out redirect, which I think should work with the to_param update, doesn't work, while the ugly hackish one above it does. Why is the to_param overwrite, which worked for other people, not working on my app? My #update and #edit methods are also not working, as their redirects go to "users/1/edit" instead of "username/edit" if overwriting to_param doesn't take care of this.
The first one is correct, but isn't working because you're still attempting to do something like this inside your controller:
User.find(params[:username])
When you should instead be doing this:
User.find_by_username!(params[:username])
The first one will attempt to find by the primary key of your table, where the second one will, correctly, query on the username field instead.
In addition to the update for to_params, the bottom of the routes file needs the following line:
resources :users, :path => '/'

How can you make sure a username won't conflict with an existing route?

So I'd like to have urls on my site like http://foobar.com/hadees that goes to someone's profile. However when registering usernames how do I make sure they don't pick something that will conflict with my existing routes?
I'm guessing I need to get a list of the existing routes but I'm not sure how to do it.
A short google search gives me that:
http://henrik.nyh.se/2008/10/validating-slugs-against-existing-routes-in-rails
In rails 3 the method has moved to Rails.application.routes.recognize_path
So I summarize :
class User < ActiveRecord::Base
validates_format_of :name, :with => /\A[\w-]+\Z/
validates_uniqueness_of :name
validate :name_is_not_a_route
protected
def name_is_not_a_route
path = Rails.application.routes.recognize_path("/#{name}", :method => :get) rescue nil
errors.add(:name, "conflicts with existing path (/#{name})") if path && !path[:username]
end
end
Good question. Through a little tinkering, I found that you can get the routes in your app via:
Rails.application.routes.routes.collect{|r| r.path}

REST Routes and overriding :id with to_param

My controller is using the default RESTful routes for creating, adding, editing etc
I want to change the default :id to use :guuid. So what I did was:
# routes.rb
resources :posts
# Post Model
class Post < ActiveRecord::Base
def to_param # overridden
guuid
end
end
This works but my modifed REST controller code has something like this
def show
#post = Post.find_by_guuid(params[:id])
#title = "Review"
respond_to do |format|
format.html # show.html.erb
end
end
When I see this this code ..
Post.find_by_guuid(params[:id])
it would seem wrong but it works.
I don't understand why I can't write it out like this:
Post.find_by_guuid(params[:guuid])
Why do I still have to pass in the params[:id] when I'm not using it?
Looking for feedback on whether my approach is correct or anything else to consider.
Even though it works it doesn't always mean it's right.
Type rake routes in your console, and check the output of the routes. You'll see the fragment ':id' in some of them, that's where the params[:id] comes from. It's a rails convention : when you use resources in your routes, the parameter is named id. I don't know if you can change it (while keeping resources; otherwise you could just go with matching rules), but you shouldn't anyway : even if it seems not very logic, it actually has sense, once your understand how rails routing works.

Rails 3 route scoping and/or nesting

I am having trouble scoping routes that I don't want to nest.
I have the following routes:
resources :foos
resources :bars
resources :bazs do
resources :hellos
resources :worlds
end
The foo, bar, and baz models all belong_to a user model. I don't want to nest another layer, but I do want to have a prefix in my url that corresponds to a user's permalink attribute (similar to each github repo prefixed by a username). So I have a before filter on all of my controllers
def get_scope
#user = User.find_by_permalink(params[:permalink])
end
Modified to_param thanks to #cowboycoded
class User < ActiveRecord::Base
def to_param
permalink
end
end
I wrapped those routes with
scope ":permalink", :as => :user do
#nested routes here
end
Now everything works fine as long as I pass #user to every non-index route. It doesn't seem very dry to have to go back to all of my views and replace (#foo) with (#user, #foo) when it is already scoped.
Unless I am mistaken, the to_param method simply replaces :id so that urls such as /users/:id appear as users/permalink instead of users/1. I attempted to use this :id in my scope, but it conflicts with foo's :id param and breaks everything. Maybe there is a connection to paths that I am missing?
Thanks for any suggestions that you may have!
Have you tried using the to_param method in your model? This will allow you to override the default and use something other than id, and will work with the URL helpers
http://api.rubyonrails.org/classes/ActiveRecord/Base.html#method-i-to_param
Example from documentation:
class User < ActiveRecord::Base
def to_param # overridden
name
end
end
user = User.find_by_name('Phusion')
user_path(user) # => "/users/Phusion"
I'm not sure how well this plays with scope, since I haven't tried it, but I guess its worth a shot.