Rails url needing posts/:id/the-name-of-post - ruby-on-rails

I would like my rails url to look like:
/posts/345/the-great-concept
when i use the following in my posts model,
def to_param
"#{id}/#{name.parameterize.downcase}"
end
the urls look great upon mousover in the browser. and function correctly. however, once the page is loaded in the browser url it looks like:
/posts/345%2Fthe-great-concept
and to be clear, the "name" is just for good looks - the post is retrieved only by id. also i do not want to use a database slug approach.
how should i better approach this?
ps. don't want "/posts/345-the-great-concept" either ...

Its escaped because its not part of the path, but a param, so it needs to be escaped or you will be on the wrong uri.
def to_param
"#{id}-#{name.parameterize.downcase}"
end
edit: Okay, so the slash is indeed important; Here's how to tackle it:
Create a custom route for that:
# in config/routes.rb
resources :posts
match '/posts/:id/:slug' => 'posts#show', :as => :slug
Then create your slug method:
# in app/models/post.rb
def slug
title.parameterize.downcase
end
Then change your routes to the show action so the link to the fancy url:
# in any link to show; redirect after create, etc..
link_to slug_path(#post, :slug => #post.slug)
I created an app to test all this out, if interested, you can check it out at:
https://github.com/unixmonkey/Pretty-Path

Related

Routing in rails - When to route to a crud method and when to route with the given parameter

I am in need of a little help regarding routing in rails. Traditionally if I have a route in routes.rb
resources:categories
it will match www.website.com/categories/id were id is an integer. But what happens if I wanted to route to a specific user or category like so:
www.example.com/categories/apple
instead of the traditional:
www.example.com/categories/4
I currently have these two lines in my routes.rb
match 'categories/:name' => 'categories#show'
resources :categories
so www.example.com/categories/apple will route to the show method in the categories controller correctly.
but
What if I want to create a new category? Here's the problem
www.example.com/categories/new
will route to the show method, and will not route to the new method
I can place an if statement in the show method checking is params[:name] == new, but I feel that there must be a better way to solve this problem.
My end goal is to route based on the string of the category (apple) and not based on it's ID (4) but also be able to create, update, and destroy a category.
Any tips?
Thanks!
you can pass a name as a parameter, for example you can try
link_to category_path(category.name) instead of link_to category_path(category)
and it will go to categories#show in controller, the route will look like example.com/categories/categoryName, in the show action of your cateogries controller, params[:id] will have the name, you'll need something like
Category.find_by_name params[:id] instead of Category.find params[:id]
Hope this helps you.
As #pdoherty926 says in the comment, you could use FriendlyId. If you want to manage it manually, you don't have to overwrite the routes, just put the needed code in the :show action:
class CategoriesControllers < ...
...
def show
if /^\d+$/ =~ params[:id]
# ... normal workflow for integer id
elsif /soe_regexp_that_matches_names>/
# ... do your thing with names
else
raise 404
end
end
...
end
I think the most simple way to do this your way is to define a route before your
match 'categories/:name' => 'categories#show'
like this:
match 'categories/new' => 'categories#new'
match 'categories/:name' => 'categories#show'
the order matters.

Changing the params in URL on rails

I want to change the :id param on the URL. I added to my routes.rb file something like:
match "articles/:name/edit", :to => 'articles#edit', :as => 'edit_article'
Thinking that :name would be readed by the server as params[:name] later for me in rails. I edited my article controller definition for edit so:
def edit
#article = Article.find(params[:name])
end
I get always the error couldn't find article with id=test and I was wondering why "id" instead of :name? I tried also changing match to get but I got the same.
I have also the default resources :articles still in my routes.rb file, don't know if there's something like a double rule working there.
The whole thing is that instead of ID numbers I would use names in my URL —not just the edit one, with the show method I could handle it, but not with edit/update/delete.
I was reading about routing but I can't figure out what I am doing wrong.
By default, find search by id.
You should replace it with find_by_name.
Advice: use friendly_id

Correct method for custom Rails routing

In my routes I currently have resources :users and so I get the routes such as /users/id/ and /users/id/edit and so on...however, I am wanting to have the 'default' URLs for my pages begin with /name where name is the user's unique login name.
So, right now my routes file looks like this.
resources :users
match '/:name' => 'users#show_by_name'
Then in my users_controller.rb file, I have methods defined as such...
def show_by_name
#user = User.find_by_name(params[:name])
render 'show'
end
So, basically it is doing the same thing as def show but instead of an id being passed in the URL, it's a name.
In my views I am linking like this...
<li><%= link_to "My Profile", "/#{current_user.name}" %></li>
As opposed to using <li><%= link_to "My Profile", current_user %></li>
I am wondering if I am going about this the correct way. I feel like I am doing something unnecessary by using extra methods in my users_controller.
Would I be better off just removing the resources :users line and creating my own routes that are more suited towards the type of URLs I want on my application?
Thanks for your time.
You might be better off overriding the to_param method in your User model. Rails has in built function for search friendly URL's
class User < ActiveRecord::Base
def to_param
"#{user.name}"
end
end
Url's will generate as
user_url(#user)
#http://0.0.0.0:3000/users/andrew
# Controller
#user = User.find_by_name(params[:id])
I would advice you to use FriendlyID, it's a neat gem that translates the :id to a value based on one of the table's columns. This could be a title for instance or name in your case.
I found it fairly easy to start using.
Ryan Bates talks about it in this screencast: http://railscasts.com/episodes/314-pretty-urls-with-friendlyid
For installation look here: https://github.com/norman/friendly_id
Both Andrew and Martin are right (the FriendlyID gem actually uses the to_param override method), but you're asking two questions :
can I use another attribute instead of the default id as the id in the route ?
can I use a non-resourceful route ?
In both cases, the answer is yes. You may override the to_param method AND use a non-REST route such as :
match '/:id' => 'users#show'

How do I get the format of my URLs to be username/controller/:id in Rails 3.1?

I want it similar to the way Twitter handles the URLs for its tweets.
For instance, right now my URL looks like this: mydomain.com/feedbacks/1/, where feedbacks is the name of the controller.
I want it to look like: mydomain.com/username/feedbacks/1/ which is similar to Twitter's: twitter.com/username/status/:id/.
My routes.rb looks like this:
resources :users do
resources :feedbacks
end
When I have it like this, it gives me the URLs as mydomain.com/users/1/feedbacks, but I want the actual username in the URL.
How do I get that?
Thanks.
Edit 1: If you are adding another answer to this question, please make sure it addresses my comments/questions to the answer already given. Otherwise it will be redundant.
scope ":username" do
resources :feedbacks
end
From the docs:
This will provide you with URLs such as /bob/posts/1 and will allow
you to reference the username part of the path as params[:username] in
controllers, helpers and views.
UPDATE:
I have tested and confirmed the accuracy of paozac's answer. I'll clarify it a bit.
Suppose you had a #feedback object with an id of 12, and the associated user had a username of foouser. If you wanted to generate a URL to the edit page for that #feedback object, you could do the following:
edit_feedback_url(:username => #feedback.user.username, :id => #feedback)
The output would be "/foouser/feedbacks/12/edit".
# A URL to the show action could be generated like so:
feedback_url(:username => feedback.user.username, :id => feedback)
#=> "/foouser/feedbacks/12"
# New
new_feedback_url(:username => current_user.username)
#=> "/foouser/feedbacks/new"
Additionally, as noted by nathanvda in the comments, you can pass ordered arguments which will be matched with the corresponding dynamic segment. In this case, the username must be passed first, and the feedback id should be passed second, i.e.:
edit_feedback_url(#feedback.user.username, #feedback)
Also, if you need help handling the params from the controller, I suggest creating a new question specific to that.
Once you have defined the scope like dwhalen says you can generate the url like this:
feedbacks_url(:username => 'foo')
and get
http://www.example.com/foo/feedbacks
or
edit_feedback_url(:username => 'foo', :id => 1)
and get
http://www.example.com/foo/feedbacks/1/edit

Clean URLs Using forward slash '/' in to_param with Rails (3)

Is this possible?
def to_param
"#{id}%2F#{slug}"
end
This works in Chrome and Safari, but if Firefox you see the "%2F" in the address bar. Is there a cleaner way?
This is indeed an old post, but I want to build a bit on it.
If you don't want to have to handle an slug variable in your params, you really need to define that method to_param in your model
def to_param
"#{id}/#{title}"
end
and set a route like so:
resources :posts, :id => /[0-9]+\/.+/
That way your link definition looks quite like a normal one:
link_to post.title, post_url(post)
Very simple: http://www.miguelsanmiguel.com/2011/03/17/slug-that-slash
Ok I tried this just now you won't need this:
def to_param
"#{id}/#{slug}"
end
In routes.rb (replace what you want with what you need)
match "/expenses/:id/:slug" => "expenses#show", :as => :expense
Your link_to should look like this now
= link_to "something", expense_url(:id => expense.id, :slug => expense.slug)
hope this helps a bit
Have a look at Friendly ID -- it will eliminate the ID entirely and just use the slug. It is Rails 3 compatible as well.
maybe something like this can help you
match "/foo/:id", :to => redirect("/bar/%{id}s")
check out the section The "Redirect Method" in this article about rails3 and routes
http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/

Resources