I'm currently reading Beginning Rails 3. I have a question about redirection. The book states that "redirect_to can also take an object as a parameter" vs a path. So the example they give is that
redirect_to(#article)
is a shortcut equivalent to
redirect_to(article_path(:id => #article))
I'm not sure I understand this. What exactly is the line :id => #article saying?
thanks,
mike
redirect_to(#article) is a shortcut for
redirect_to(article_path(#article)). Rails can do this because it extracts the class name from the #article variable, something like send("#{#article.class.name.downcase}_path").
redirect_to(article_path(#article)) is a shortcut for redirect_to(article_path(:id => #article)), which is a shortcut for redirect_to(article_path(:id => #article.to_param)). Basically Rails says, #article is an ActiveRecord object, I'll ask it for it's magical to_param value. By default, to_param just returns the article's ID, but you can overwrite this to return like a slug or nicename, like 38-hello-world.
So redirect_to(#article) is really saying, OK, I have an Article, so I need the path to the articles, and I need the resource identifier, or to_param, of the article.
Then it constructs the URL!
:id => #article is a shortcut for :id => #article.to_param, which just specifies which article you're looking to view.
First of all, execute rake routes in your console. It will display the list of your app's routes.
There will be a route called article.
article_path is simply the link for the route article.
And by doing (:id => #article), you're passing it an id parameter that has the #article value.
Related
Knowing these things
model_sym = :users
user_id = 1
I can do this:
url = "#{model_sym.to_s}/#{user_id}"
But is there a way I could do something like this?:
url = url_for(model_sym, user_id)
I could first "find" the user to pass into url_for, but I'd rather not.
I think you could use polymorphic_url:
polymorphic_url([model_sym, user_id])
Resources
Having written that, it seems you're getting confused about the resourceful nature of Rails.
Built on Ruby, Rails is object-orientated, which means that everything you do needs to be tied to an object (model):
The reason why this is important is because all of Rails' helpers etc are built around objects. That's why when you create a new set of routes, you can simply call resources (as resourceful is to give the object a set of attributes / methods you can call)
--
Implementation
The problem you have is you're not basing your routes around any objects - you're simply
calling symbols / numbers. Although this will work, it's not the "right" way to create Rails functionality
Your ideal situation is to build objects, and pass them to the routing structure of your application. To build an object, you'd do the following:
#user = User.find params[:id] #-> builds object
<%= link_to "Users Path", #user %> #-> pulls route from object
Something like this:
url_for(:controller => model_sym.to_s, :action => :show, :id => user_id)
You can do send("#{ model_sym.to_s.singularize }_path", id) to get the URLs you want.
This would call user_path(1) in your example.
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
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
I would like to do something to this effect, I believe:
map.connect 'show/:company_name/:id',
:controller => 'companies',
:action => 'show'
Basically, each time the show action is called, I would like it to take the company_name param and place it into the url as such (show/:company_name/:id)
However, it seems I am using old (rails 2.x routing api) and cannot use map.connect without getting an error. How can I upgrade this?
Is there some way to do this with "match"?
Thanks!
===================
This is the error I see when I try to use map.connect:
undefined local variable or method `map' for #<ActionDispatch::Routing::Mapper:0x103757458>
I think your routes lack a "/" symbol in the first line.
Try this:
match '/show/:company_name/:id' => 'companies#show'
You can check your routes path with command rake routes.
--
Besides, the show action is the default RESTful method in Rails. I'll suggest you change a equivalent word, and reserve "show" action for future or other situation.
In Rails convention, you can write resources :companies, and the path will be /companies/:id using show action.
Some adjustment, in app/models/company.rb
def to_param
self.name
end
So your url will look like http://yourdoamin.com/companies/37signals.
In app/controllers/companies_controller.rb
#company = Company.find_by_name(params[:id])
If I'm understanding your goal, try
match 'companies/show/:company_name/:id' => 'companies#show'
Imagine a blog with posts and comments. An individual comment's URL might be posts/741/comments/1220.
However, I'd like to make the URL posts/741#1220, or even posts/741#comment-1230.
What's the least intrusive way of doing this, so that redirect_to comment_path(my_comment) points to the correct URL?
You could simply use
redirect_to post_path(comment.post, :anchor => "comment-#{comment.id}")
to manually build the URL with the anchor. That way, you can still have the absolute URL to your comments as posts/:post_id/comments/:comment_id in your routes. You can also create a helper method in e.g. application_controller.rb
class ApplicationController
helper :comment_link
def comment_link(comment)
post_path(comment.post, :anchor => "comment-#{comment.id}")
end
end
Prefer to keep your anchor builder in one place.
class Comment
...
def anchor
"comment-#{id}#{created_at.to_i}"
end
end
then
post_path(comment.post, :anchor => comment.anchor)
Adding the created_at.to_i obscures your data a bit more and doesn't harm anything.