I have a few questions maybe if I can just tie them up all into one quick go here this could be a good reference for later.
I would like to render a partial [comments/new] in [posts/show]. Now being on our current page/show of posts#show we would be using the posts_controller.rb our model will be the one[s] we would like to call on at any point. -- I need to render [comments/new] in the [posts/show] with the comments_controller.rb enabled for that piece.
second the rake routes shows you a list of routes that are created for you.
post_comments GET /posts/:post_id/comments(.:format) comments#index
POST /posts/:post_id/comments(.:format) comments#create
new_post_comment GET /posts/:post_id/comments/new(.:format) comments#new
edit_post_comment GET /posts/:post_id/comments/:id/edit(.:format) comments#edit
post_comment GET /posts/:post_id/comments/:id(.:format) comments#show
PATCH /posts/:post_id/comments/:id(.:format) comments#update
PUT /posts/:post_id/comments/:id(.:format) comments#update
DELETE /posts/:post_id/comments/:id(.:format) comments#destroy
not sure what everything on this list is or how it works need some help on that one.
class and subclasses? can you have sub-sub-classes and how does linking a class and a subclass work inside of a simple app.
Views are not strictly tied to your controller. Rails is automagically set so that in the new action of the comments controller, it will try to render the views/comments/new view as long as there is no view specified.
You want to call an action from another action. You should not do that. You can call controller method that are not action from an action, but never an actual action.
The shortest answer would be, build an empty comment in the post show actioncontroller (as you probably do).
def show
#post = Post.find params[:id]
#comment= Comment.build
end
and in the view do :
<%=render "comments/form" %>
EDIT: corrected a typo, here it is rendering what we call a partial. A partial file always start with the _ character, but when you call it in the view, you do not put the _ in the path.
But maybe, a more important question is "do you need a new action in comments". Obviously your comments are always used in posts and you never have a use case asking for a comment created ex nihilo. In a case of a basic blog, i would only have a create and update action in my comments controller that would end with a redirect to post_show_path or :back
Nested resource are not really the best answer to your problem, it would be more fitted for something like a photo gallery where you have pages displaying a single photo :
galleries/:id_gallery/photo/:id
Using nested routes in a post/comment context, that would mean that you want to have a dedicated view to create a comment, and a view for each comment. You do not really need that.
:url => { :action => "create", :controller => "posts" }
anytime you can specify a :url I can use this format to call a specific controller and action no matter were I am playing in the views. I can render a partial with this :url in the pages#index then rails know hey don't use the pages controller use the posts controller.
Related
Ok so I have a rails app where user can create pins. Then they can comments those pins. What I what to do is to remove the controller name in pin url.
So instead of: http://localhost:3000/pins/name I have http://localhost:3000/name
**I did that by using this in my **config/routes.rb****
Rails.application.routes.draw do
resources :pins, :only => [:index, :new, :create], :path => '' do
resources :comments
member do
put 'upvote'
end
end
But now, when I try to comment a pin I have this error:
wrong constant name 'pin name'
and the error come fom this lines from my comments_controller.rb:
def load_commentable
resource, id = request.path.split('/')[1, 2]
#commentable = resource.singularize.classify.constantize.friendly.find(id)
end
Any ideas how I could fix this ?
EDIT:
**rake routes** output:
pin_comments GET /:pin_id/comments(.:format) comments#index
POST /:pin_id/comments(.:format) comments#create
new_pin_comment GET /:pin_id/comments/new(.:format) comments#new
edit_pin_comment GET /:pin_id/comments/:id/edit(.:format) comments#edit
pin_comment GET /:pin_id/comments/:id(.:format) comments#show
PATCH /:pin_id/comments/:id(.:format) comments#update
PUT /:pin_id/comments/:id(.:format) comments#update
DELETE /:pin_id/comments/:id(.:format) comments#destroy
Use params (Hash with request parameters) instead of resource, id = request.path.split('/')[1, 2]. This should fix your second problem.
I think you probably came from a php background or something like that, cause I used to think like that back then when I used php my self, but in rails you don't touch the URI or try to parse it or anything, that's the router's job, and if it reached that part of your code then the job was already done.
If you are using the pin's name as a url then you should either use a friendly_id gem, or set the to_param method of the model.
The id of the pin will always be in the params[:pin_id] because that's how it's named in the routes, and the id of the comment will be in the params[:id], mind the variable names in the route
/:pin_id/comments/:id
I'm not sure what you mean by the resource, but if you mean the model name, well you're in a pin's controller, so it's safe to assume it's a pin model, but if you want the name of the controller then you could access params[:controller]
Your load_commentable method would probably look like this after fixing everything
def load_commentable
#commentable = Pin.find(params[:pin_id]).comments.where(id: params[:id])
end
Here is my update function, the moment something is updated it perfectly goes to http://localhost:3000/articles/2
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
How does the redirect_to #article work and show the artiles/2/ page?
Below is my routes
Prefix Verb URI Pattern Controller#Action
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
welcome_index GET /welcome/index(.:format) welcome#index
root GET / welcome#index
The magic method is polymorphic_url.
When you call link_to, redirect_to etc. and you don't pass a string or a hash of routing options Rails will end up calling this method.
Active Record (and Active Model compliant objects) have a model_name class method that returns an ActiveMethod::Name object which has a bunch of methods for obtaining the 'correct' name for a model class for various uses.
One of those (singular_route_key) says what name is used for the model in routes. This also takes care of handling things like namespacing if you're in an engine.
In the simplest case, this boils down to
#article.class.model_name.singular_route_key #=> 'article'
Rails then generates the method name from that by joining this with any prefix options and the correct suffix ('url', 'path', etc.), and ends up with a method name like article_url. It then calls that method (generated for you based on your routes file), passing the article, which returns the url for this article.
redirect_to method checks the class of instance provided and gives you the /articles based on the model class, and path provided in routes, and then it check if the object has id or not. If the object persist in database then it checkes for to_params method on that object and add to /articles/{result of to_params}. And if the object does not persist in database then it gives you articles/new.
See the line with the name article -
article GET /articles/:id(.:format) articles#show
So when you give something like redirect_to #article, it checks for the id within the current view to get the id and displays that corresponding view of record.
Scenario:
1) You are in article index with many records being shown.
2) You clicked the second record whose link is defined with redirect_to #article in a controller.
3) The controller is smart enough to first route through to identify the route and then picks the correct path for it.
4) The View shows the view based on the route forwarded by the controller.
In your controller you have
#article = Article.find(params[:id])
redirect_to #article
So #article contains the array of ids of Article.And also you have /articles/:id(.:format) articles#show
So whenever a particular Article is updated it redeircts to the corresponding article based on the :id which #article contains.
In your case,you are updated an article with id = 2, so the route is /artcles/2
So I have created a blog that users can create posts and others can comment on that post.
I want users to be able to edit the comments.
What I did so far is the following:
My routes are like this:
resources :posts do
resources :comments
end
and when I rake route I get this:
new_post_comment GET /posts/:post_id/comments/new(.:format) comments#new
edit_post_comment GET /posts/:post_id/comments/:id/edit(.:format) comments#edit
post_comment GET /posts/:post_id/comments/:id(.:format) comments#show
on my view file I use this which is obviously wrong
<%= link_to 'Edit Comment', edit_post_comment_path(#comment) %>
and my controller is like that:
def edit
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
end
def update
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
if #comment.update(comments_params)
redirect_to post_path(#post)
else
render 'edit'
end
end
When I try to visit the page (of the post) I get this error:
No route matches {:action=>"edit", :controller=>"comments", :id=>nil, :post_id=>nil, :format=>nil} missing required keys: [:post_id, :id]
That's the first time I work on nested routes. I read some tutorials but had nothing really helped me to fix this. I am not sure how can I pass these keys...
(if you need any other info please let me know and I will provide them)
Any help is greatly appreciated!
You need to pass both the #post and #comment instance.
edit_post_comment_path(#post, #comment)
Generally speaking, you need to pass every instance matching the resource the route is part of. So, in a single route like
edit_post_path(#post)
you pass only the post. In a post/comment nested route you pass both. In a post/comment/whatever you will need to pass the three resources, etc.
When you type
rake routes
a bunch of routes come out, but where are they defined???
I know some are default, and how about the others?
For example, this is a script from a controller, I tried to take off the 's' from do_something, but can't make it work.... are they defined somewhere else too?
Also, when do they take parameters and when not, how I know it ? Thanks!
def hello
redirect_to do_things_shop_path(shop)
end
def do_things
end
Rails routing configurations are kept in config/routes.rb file.
Taking parameters depends on many things. rake routes will show with routes take parameters. Member actions will take parameters.
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
edit_post GET /posts/:id/edit(.:format) posts#edit
In the last line, you will url like posts/:id/edit. This path requires :id parameter. You can call this route many ways. One of them is like:
edit_post_path(#post)
If you want to create a custom action, (say under posts controller), you can declare it as follow:
match `/posts/:id/things_with_id`, :to => 'posts#do_things_with_id', :as => 'do_things_with_id
match `/posts/things_without_id`, :to => 'posts#do_things_without_id', :as => 'do_things_without_id
First one requires an ID while the second one does not. Call them accordingly:
do_things_with_id_path(#post)
do_things_without_id()
For a resource, you can create these easily using member & collection action. Member action needs id while collection action does not.
resources :posts do
member { get 'do_thing' }
collection { get do_things' }
end
hope you got it.
By the way, you must read the following guide if you want to understand these clearly.
http://guides.rubyonrails.org/routing.html
Lets say I am creating a new Foo using a form and a standard Rails restful controller, which looks something like this:
class FoosController < ApplicationController
...
def index
#foos = Foo.all
end
def new
#foo = Foo.new
end
def create
#foo = Foo.create(params[:foo])
if #foo.save
redirect_to foos_path, :notice => 'Created a foo.'
else
render 'new'
end
end
...
end
So, if I use the standard restful controller (as above), then when I'm creating the Foo I am at example.com/foos/new, and if I submit the form and it saves correctly I'm at example.com/foos showing the index action. However, if the form is not filled correctly the form is rendered again and error messages are shown. This is all plain vanilla.
However, if errors are shown, the form page will be rendered but the URL will be example.com/foos, because the CREATE action posts to that url. However, one would expect to find Foos#index at example.com/foos, not the form they just submitted now with error messages added.
This seems to be Rails standard behavior, but it doesn't make a lot of sense to me. Obviously I could redirect back to new instead of rendering new from the create action, but the problem with that is the error messages etc. would be lost along with the partially complete Foos in memory.
Is there a clean solution for this problem, a way to send people back to example.com/foos/new when there are errors in the new Foo form they submitted?
Thanks!
To answer your comment on another answer:
I'm wondering if there's a way, without rewriting the controller at all, to tell rails that you want the URL to match the rendered template, rather than the controller action that called it.
I don't think so; URLs are tied directly to routing, which is tied into a controller and action pair--the rendering layer doesn't touch it at all.
To answer your original question, here's information from another similar question I answered.
As you've found, by default when you specify resources :things, the POST path for creating a new thing is at /things. Here's the output for rake routes:
things GET /things(.:format) {:action=>"index", :controller=>"things"}
POST /things(.:format) {:action=>"create", :controller=>"things"}
new_thing GET /things/new(.:format) {:action=>"new", :controller=>"things"}
edit_thing GET /things/:id/edit(.:format) {:action=>"edit", :controller=>"things"}
thing GET /things/:id(.:format) {:action=>"show", :controller=>"things"}
PUT /things/:id(.:format) {:action=>"update", :controller=>"things"}
DELETE /things/:id(.:format) {:action=>"destroy", :controller=>"things"}
It sounds like you want something more like this:
create_things POST /things/new(.:format) {:action=>"create", :controller=>"things"}
things GET /things(.:format) {:action=>"index", :controller=>"things"}
new_thing GET /things/new(.:format) {:action=>"new", :controller=>"things"}
edit_thing GET /things/:id/edit(.:format) {:action=>"edit", :controller=>"things"}
thing GET /things/:id(.:format) {:action=>"show", :controller=>"things"}
PUT /things/:id(.:format) {:action=>"update", :controller=>"things"}
DELETE /things/:id(.:format) {:action=>"destroy", :controller=>"things"}
Although not recommended, you can get this result with the following route:
resources :things, :except => [ :create ] do
post "create" => "things#create", :as => :create, :path => 'new', :on => :collection
end
You would also need to modify your forms to make them POST to the correct path.
You could hook into rails routing by adding this in an initializer:
https://gist.github.com/903411
Then just put the regular resources in your routes.rb:
resources :users
It should create the routes and behaviour you are looking for.
You can set up the routing manually, if you're that concerned about what URL is going to show. For what you want, you can have a GET to /foos/new render your form, and a POST to the same URL do the creation:
map.with_options :controller => :foos do |foo|
foo.new_foo '/foos/new', :conditions => {:method => :get}, :action => :new
foo.create_foo '/foos/new', :conditions => {:method => :post}, :action => :create
foo.foos '/foos', :conditions => {:method => :get}, :action => :index
end
This should work without requiring any changes to your controller (yay!) - all three actions from your example are taken care of. The few disclaimers:
This is based on my routing for a 2.3.8 app - some syntax (semantics?) changes are probably required to get it into Rails 3 routing style.
My attempts to mix this style of routing with map.resources have failed horribly - unless you're more familiar with this than me, or Rails 3 routing is better (both easily possible), you'll have to do this for every route to the controller.
And finally, don't forget to add /:id, (.:format), etc. to the routes that need them (none in this example, but see #2).
Hope this helps!
Edit: One last thing - you'll need to hard-code the URL in your form_for helper on /foos/new.html.erb. Just add :url => create_foo_path, so Rails doesn't try to post to /foos, which it will by default (there might be a way to change the creation URL in the model, but I don't know of it, if there is one).
You could use Rack::Flash to store the parameters you wanted in the user's session and then redirect to your form url.
def create
#foo = Foo.new(params[:foo])
if #foo.save
redirect_to foos_path, :notice => 'Created a foo.'
else
flash[:foo] = params[:foo]
flash[:errors] = #foo.errors
redirect_to new_foo_path #sorry - can't remember the Rails convention for this route
end
end
def new
# in your view, output the contents of flash[:foo]
#foo = Foo.new(flash[:foo])
end