Getting a username from url with params - ruby-on-rails

I seem to find it difficult to get the username from a url with params, but works when finding IDs.
To get the id from this url: www.foo.com/users/1/post/new, you'd use:
User.find(params[:id]) # same as User.find(1)
I thought I could do the same, based on the docs, for: www.foo.com/users/joe/post/new
User.find_by(name: params[:name])
How to grab the user's name from url with params using the find or find_by method? Thanks

In your User model, override the to_param method like this:
def to_param
name
end
With this code, you're overriding the ActiveRecord default behaviour, so when you link to a User, it will use the name for the parameter instead of id.
Also, I recommend you to take a look at the friendly_id gem as well, using which you can achieve this goal.

Related

How do I use a Twitter username as my user id?

I'm running a rails 4 app using Omniauth with Twitter. I'm trying to achieve something close to producthunt.com, where they authenticate users and use their Twitter username as their url id.
From what I understand, you want the url to look like this: example.com/users/username
instead of example.com/users/123
If so, all you have to do is change the way you find the de correct user in your Users (or whatever you call your user model) controller. Currently it probably looks like this:
#Users Controller
def show
#user = User.find(params[:id])
end
# Your path to that user is probably this:
user_path(123) #basically you pass the user.id
The code above is using Model.find(#) to look for the user. The .find() looks the user up by its id#. Instead, you want to find it by the username, not id. To do this use the Model.find_by You can see all the ways of querying here.
Also, whenever you look for the path to find the user show page, istead of sending the id # to the url, you now have to send the username string.
Your new setup should look like this:
#Users Controller
def show
#user = User.find_by :username params[:id]
#this assumes you have it in your DB as username. Some twitter apps save it as screen_name.
end
# Your path to that user is probably this:
user_path('username') #basically you pass the username instead. current_user.username? I dont know what you call in in your app.
Hope that helps. Let me know if you have questions.

Possible to Change Rails Routing Convention?

I'm wondering if it's possible to edit the default Rails routing convention to fetch a specific record based on a field that is not the ID?
For instance, instead of retrieving a specific record based on ID, with the verb/url combination:
GET /users/:id
Retrieve a specific record based on username, with the verb/url combination:
GET /users/:username
I don't see why this would be a problem theoretically, as long as usernames were required to be unique, but I'm having trouble understanding how to implement it based on the Rails Routing Guide.
I have gathered that I will need to add a line to my routes.rb file, to define a singular resource, just prior to:
resources :users
However, I'm having trouble understanding the syntax to accomplish this. Any help in understanding this would be greatly appreciated.
Yes it is possible and they are called Non Restful Routes in the rails documentation
A trivial example is doing the below in your routes.rb
get ':users/:show/:username', controller: "users", action: "show"
and in your UsersController you have a show action that looks like this:
def show
if params[:id].present?
#user = User.find(params[:id])
elsif params[:username].present?
#user = User.find_by(username: params[:username])
end
end
This way you support showing by id and username, if you want do disable support for either of them, modify the if clause as you wish
I think you are looking to change the to_param method like so:
class User < ActiveRecord::Base
def to_param
"#{id} #{name}".parameterize
end
end
This would give the url as: /user/id-name. If you want to get rid of the id before the name it gets a little more complicated. If you were just to remove it, it will more than likely break since ActiveRecord needs the id first for finds.
To get around this I would suggest using FriendlyId gem: https://github.com/norman/friendly_id
There is also a RailsCast showing how to use Friendly_id but its pretty straight forward.
The routes does not care if it is an ID or username.
It is really how you find it in the controller.
Just in the user show controller:
def show
#user = User.find_by_username params[:id]
end

Rails - how to set up path to action by own criteria (not ID)

When I have a path like this:
user_messages_path(current_user)
it generates following URL:
users/1/listings
I am storing in database users' unique codes, so I wouldn't like to display URLs like
users/ID/listings
but more like
users/CODE/listings
How I need to update routes for using paths with users' codes?
Thanks
user_messages_path(current_user)
is a shortcut for:
user_messages_path(current_user.to_param)
which generally does:
user_messages_path(current_user.id)
You can:
pass any string you want: user_messages_path('foo')
or override to_param in your model
Just beware to update your code responsible to retrieve the object from the params.
in user.rb
def to_param
uuid # or whatever attribute you want to use instead of the id
end
in the controllers instead of User.find(params[:id]):
User.find_by_uuid!(params[:id]) # adjust to the attribute name used in to_param
You can use a custom route, something like the following.
get 'users/:code/:listings' => 'listings#index', as: :user_messages
Then in your params there key :code will return that part of the url e.g. params[:code]
In your controller you could use the following
Rails 3.2
User.find_by_code(params[:code])
Rails 4
User.where(code: params[:code])

Using both id and slug when building paths

I have a model, let's say user, with both an id and a slug. I'd like to be able to generate a url using user_path(#user) that contains both the id and slug.
I know that user_path will use to_param method for the parameter it puts at the end of the url, but is there a way to use 2 (or more parameters) and get something like this:
http://domain.com/users/id/slug
Thanks!
Friendly-id is a great way to generate permalinks. It also offers pretty good customization options.
Did you try this in your model?
def to_param
"#{id}-#{slug)"
end

Permalinks with Ruby on Rails (dynamic routes)

I am currently developing a blogging system with Ruby on Rails and want the user to define his "permalinks" for static pages or blog posts, meaning:
the user should be able to set the page name, eg. "test-article" (that should be available via /posts/test-article) - how would I realize this in the rails applications and the routing file?
for user-friendly permalinks you can use gem 'has_permalink'. For more details http://haspermalink.org
Modifying the to_param method in the Model indeed is required/convenient, like the others said already:
def to_param
pagename.parameterize
end
But in order to find the posts you also need to change the Controller, since the default Post.find methods searches for ID and not pagename. For the show action you'd need something like this:
def show
#post = Post.where(:pagename => params[:id]).first
end
Same goes for the other action methods.
You routing rules can stay the same as for regular routes with an ID number.
I personally prefer to do it this way:
Put the following in your Post model (stick it at the bottom before the closing 'end' tag)
def to_param
permalink
end
def permalink
"#{id}-#{title.parameterize}"
end
That's it. You don't need to change any of the find_by methods. This gives you URL's of the form "123-title-of-post".
You can use the friendly_id gem. There are no special controller changes required. Simple add an attribute for example slug to your model..for more details check out the github repo of the gem.
The #63 and #117 episodes of railscasts might help you. Also check out the resources there.
You should have seolink or permalink attribute in pages' or posts' objects. Then you'd just use to_param method for your post or page model that would return that attribute.
to_param method is used in *_path methods when you pass them an object.
So if your post has title "foo bar" and seolink "baz-quux", you define a to_param method in model like this:
def to_param
seolink
end
Then when you do something like post_path(#post) you'll get the /posts/baz-quux or any other relevant url that you have configured in config/routes.rb file (my example applies to resourceful urls). In the show action of your controller you'll just have to find_by_seolink instead of find[_by_id].

Resources