I am started learning Ruby. I just followed this guide http://guides.rubyonrails.org/getting_started.html to create blog app. One thing I noticed if we try to submit the form without entering data from url http://localhost:3000/articles/new it showing error message and redirect to http://localhost:3000/articles
I think it should keep same url and show error message.
Not sure how to fix that.
articles_controller.rb
class ArticlesController < ApplicationController
def index
#articles = Article.all
end
def show
#article = Article.find(params[:id])
end
def new
#article = Article.new
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
new.html.erb
<%= form_with scope: :article, url: articles_path, local: true do |form| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
routes.rb
Rails.application.routes.draw do
get 'welcome/index'
resources :articles
root 'welcome#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
rails routes
Prefix Verb URI Pattern Controller#Action
welcome_index GET /welcome/index(.:format) welcome#index
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
root GET / welcome#index
This is the standard behavior. If you want to change it however to keep the URL, you can modify the new and create actions:
def new
if article_params
create
return
end
#article = Article.new
render 'new'
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
And in routes.rb:
resources :articles
post "articles/new"
#Shannon answer is correct, I'm writing this for your comment on his answer, Try to use model argument, according to this when you gonna use model: #article it will generate everything automatically for you
Hope it helps.
Related
I am trying to edit an article and re-save it. I can create a new article and save that. I can even comment on the article and save that. When I go to edit the article it auto populates the form with the text to be edited but when I try to save that it gives me the "No route matches [Patch]"/artices.7" error.
article controller
class ArticlesController < ApplicationController
def index
#articles = Article.all
end
def show
#articles = Article.find(params[:id])
end
def new
#articles = Article.new
end
def edit
#articles = Article.find(params[:id])
end
def create
#articles = Article.new(articles_params)
if #articles.save
redirect_to #articles
else
render 'new'
end
end
def update
#articles = Article.find(params[:id])
if #articles.update(articles_params)
redirect_to #articles
else
render 'edit'
end
end
def destroy
#articles = Article.find(params[:id])
#articles.destroy
redirect_to articles_path
end
private
def articles_params
params.require(:articles).permit(:title, :text)
end
end
comment controller
class CommentsController < ApplicationController
def create
#articles = Article.find(params[:article_id])
#comment = #articles.comments.create(comment_params)
redirect_to articles_path(#articles)
end
def destroy
#articles = Article.find(params[:article_id])
#comment = #articles.comments.find(params[:id])
#comment.destroy
redirect_to articles_path(#articles)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
routes
Rails.application.routes.draw do
resources :articles do
resources :comments
end
end
When I run the edit blog edit.html.erb it gives me "No route matches [Patch] error. Should I do my routes a different way? I thought resources would cover that. Here is the edit file
<h1>Editing article</h1>
<%= form_for :articles, url: articles_path(#articles), method: :patch do |f| %>
<% if #articles.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#articles.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% #articles.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Updated with rake routes
Prefix Verb URI Pattern Controller#Action
nba GET /nba(.:format) nba#games
nba_index GET /nba(.:format) nba#index
POST /nba(.:format) nba#create
new_nba GET /nba/new(.:format) nba#new
edit_nba GET /nba/:id/edit(.:format) nba#edit
GET /nba/:id(.:format) nba#show
PATCH /nba/:id(.:format) nba#update
PUT /nba/:id(.:format) nba#update
DELETE /nba/:id(.:format) nba#destroy
article_comments GET /articles/:article_id/comments(.:format) comments#index
POST /articles/:article_id/comments(.:format) comments#create
new_article_comment GET /articles/:article_id/comments/new(.:format) comments#new
edit_article_comment GET /articles/:article_id/comments/:id/edit(.:format) comments#edit
article_comment GET /articles/:article_id/comments/:id(.:format) comments#show
PATCH /articles/:article_id/comments/:id(.:format) comments#update
PUT /articles/:article_id/comments/:id(.:format) comments#update
DELETE /articles/:article_id/comments/:id(.:format) comments#destroy
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
root GET / welcome#index
I think I found it, in your form_for declaration you specified articles_path rather than article_path. Those are two different methods and they expect different parameters. Use article_path instead and you should get the expected result.
Also, this doesn't make any difference in terms of the computer processing, but you should name your instance variable #article instead of #articles, because it only refers to one object, rather than a list of objects. As you work with Rails (especially the routes, controller names, model names etc.) you'll keep noticing that the framework is very picky about when you use singulars and when you use plurals, which is of course why you made the mistake of using articles_path as explained above.
Rails makes it even more frustrating because they didn't inform you that you were using the wrong method. articles_path doesn't require an article ID, so ideally Rails would raise an error when you gave it one, but it turns out that all the _path and _url route helper methods accept an additional variable so you can define the file extension of the URL (which you would normally define like article_path(#article, "xml").
I tried to use paperclip to include Images in my design of a blog. I keep getting the error :
ActionController::UrlGenerationError in ArticlesController#create
No route matches {:action=>"show", :controller=>"articles"} missing required keys: [:id]
Now, whenever I click on the submit button to create a new article, it says "Could not find article without id". I tried also accessing the view for the show page through a link and I could not.
Extracted source (around line #30):
Here is my Articles Controller
class ArticlesController < ApplicationController
def index
#articles = Article.all
end
def show
#article = Article.find(params[:id])
#comment = Comment.new
#comment.article_id = #article.id
end
def new
#article = Article.new
end
def create
#article = Article.new(article_params)
#article.save
redirect_to article_path
end
def edit
#article = Article.find(params[:id])
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
def update
#article = Article.find(params[:id])
#article.update(article_params)
flash.notice = "Article '#{#article.title}' Updated!"
redirect_to article_path
end
def article_params
params.require(:article).permit(:title, :body, :tag_list, :image)
end
end
Here is my articles Helper:
module ArticlesHelper
def article_params
params.require(:article).permit(:title, :body, :tag_list, :image)
end
end
Here is my articles/show.html.erb
<h1><%= #article.title %></h1>
<p>
Tags:
<% #article.tags.each do |tag| %> <%= link_to tag.name, tag_path(tag) %>
<% end %>
</p>
<% if #article.image.exists? %>
<p><%= image_tag #article.image.url %></p>
<% end %>
<p><%= #article.body %></p>
<h3>Comments (<%= #article.comments.size %>)</h3>
<%= render partial: 'articles/comment', collection: #article.comments %>
<%= render partial: 'comments/form' %>
<%= link_to "<< Back to Articles List", articles_path %>
<%= link_to "delete", article_path(#article), method: :delete, data: {confirm: "Really delete the article?"} %>
<%= link_to "edit", edit_article_path(#article) %>
And here is my routes file
TheBlog::Application.routes.draw do
root 'static_pages#home'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'contact' => 'static_pages#contact'
get 'signup' => 'users#new'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
get 'article' => 'articles#show'
resources :users
resources :articles do
resources :comments
end
resources :tags
end
The error is telling you that you're missing the :id parameter when attempting to generate the article show route in your ArticlesController.
The resources :articles line in your config/routes file will generate a number of routes helper methods, including article_path for the show route. By default, this helper method requires one argument - one that is can translate into the aforementioned :id param. This argument should be an article id or, more commonly, an instance of an article. You need to tell Rails to which article show page to send the user, right?
Hence, you need to pass an article instance to your call to article_path in your create action (and update it appears). Here is a rewrite of your create action:
def create
#article = Article.new(article_params)
#article.save
redirect_to article_path(#article)
end
**ARTICLE CONTROLLER** //controller
class ArticlesController < ApplicationController
def index
#article=Article.all
end
def show
#article=Article.find(params[:id])
#comment = Comment.new
#comment.article_id = #article.id
end
def new
#article=Article.new
end
def create
#article = Article.new(article_params)
#article.save
redirect_to article_path(#article)
end
def destroy
article=Article.find(params[:id])
article.destroy
redirect_to articles_path
end
def edit
#article = Article.find(params[:id])
end
def update
#article = Article.find(params[:id])
#article.update(article_params)
flash.notice = "Article '#{#article.title}' Updated!"
redirect_to article_path(#article)
end
def article_params
params.require(:article).permit(:title, :body)
end
end
**SHOW.HTML.ERB** /view file
<h1><%=#article.title%></h1>
<p><%=#article.body%></p>
<br><hr>
<%= link_to "edit", edit_article_path(#article) %>|
<%= link_to "delete",article_path(#article), method: :delete,data: {confirm: "Really delete the article?"} %>|
<%= link_to "<< Back to Articles List", articles_path %>
<h3>Comments</h3>
<%= render partial: 'articles/comment', collection: #article.comments %>
<%= render partial: 'comments/form' %>
**_FORM.HTML.ERB** //_form view
<h3>Post a Comment</h3>
<%= form_for [ #article, #comment ] do |f| %>
<p>
<%= f.label :author_name %><br/>
<%= f.text_field :author_name %>
</p>
<p>
<%= f.label :body %><br/>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit 'Submit' %>
</p>
<% end %>
rake 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
root GET / articles#index
comments GET /comments(.:format) comments#index
POST /comments(.:format) comments#create
new_comment GET /comments/new(.:format) comments#new
edit_comment GET /comments/:id/edit(.:format) comments#edit
comment GET /comments/:id(.:format) comments#show
PATCH /comments/:id(.:format) comments#update
PUT /comments/:id(.:format) comments#update
DELETE /comments/:id(.:format) comments#destroy
i get no articles_comments_path!
NoMethodError in Articles#show
Showing /home/manoj/ror/blogger/app/views/comments/_form.html.erb where line #3 raised:
undefined method article_comments_path' for #<#<Class:0x00000005be3d40>:0x000000054cacc0>
app/views/comments/_form.html.erb:3:in_app_views_comments__form_html_erb__3077497298558231225_47236640'
app/views/articles/show.html.erb:11:in `_app_views_articles_show_html_erb__4529829459036724249_48053660'
You are getting this error because you don't have article_comments_path helper method. Change your routes to this:
resources :articles do
resources :comments
end
OR
If you don't want to nest routes then change your form to this:
<%= form_for #comment do |f| %>
// form fields
<% end %>
Also in show action you can dry up your code by using association methods
def show
#article= Article.find(params[:id])
#comment = #article.comments.build
end
I'm a beginner in Rails, This is the rails guide blog app. After submitting the form I get No route matches [POST] "/articles/new" which is weird because as you can see from the routes below, articles/new doesn't have a post.
Controller file:
def new
#article = Article.new
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def show
#article = Article.find(params[:id])
end
private
def article_params
params.require(:article).permit(:title, :text)
end
Routes file:
Rails.application.routes.draw do
get 'welcome/index'
root 'welcome#index'
resources :articles
Routes:
Prefix Verb URI Pattern Controller#Action
welcome_index GET /welcome/index(.:format) welcome#index
root GET / welcome#index
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
[EDIT] Here's the form also
<%= form_for :article, url: articles_path do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%=f.submit %>
</p>
<% end %>
Change the form helper to:
<%= form_for #article do |f| %>
You don't need to specify the url if it's the default controller action (articles#create)
Other option:
<%= form_for Article.new do |f| %>
As you're new to Rails, let me give you some ideas
form_for
The problem with this is down to your use of form_for
form_for basically creates a form out of an ActiveRecord object - allowing you to populate various attributes & other elements of the object with a standard method.
Part of this is to create a url component for the form, which will be built from the model_name attribute of your object. Typically, you'll populate the form_for with a pure ActiveRecord object; but as you've just used a symbol, it's going to cause a problem when looking for the url in the non-existent object:
--
Fix
The way to fix this is populate the form_for with an ActiveRecord object:
<%= form_for #article do |f| %>
You've already done most of the work for this - in your controller, you have to declare the #article variable in the new and create actions:
#app/controllers/articles_controller.rb
Class ArticlesController < ApplicationController
def new
#article = Article.new
end
def create
#article = Article.new(article_params)
#article.save
end
private
def article_params
parmas.require(:article).permit(:your, :attributes)
end
end
I'm doing an online tutorial and was tasked with implementing a comment resource that is nested under a post resource, which in turn is under a topic resource. I've figured out all of the steps of the exercise except getting the 'comment create' form to show up on post#show. I have been trying for several hours to fix it but keep getting the following error: undefined method "model_name" for NilClass:Class. I have no idea what it is referring to by model_name and in my desperation I even looked at the tutorial's code on GitHub, but I still can't see where I'm making the mistake.
My form is in a partial and the error points to the first line, so I'll paste that code here first:
<%= form_for [topic, post, comment] do |f| %>
<div class="controls">
<%= f.text_area :body, rows: 8 %>
</div>
<div class="control-group">
<div class="controls">
<%= f.submit "Add Comment", class: 'btn' %>
</div>
</div>
<% end %>
I'll post my routes so you can see the nesting:
X::Application.routes.draw do
devise_for :users
resources :topics do
resources :posts, except: [:index] do
resources :comments, only: [:create]
end
end
match "about" => 'welcome#about', via: :get
root to: 'welcome#index'
end
Here is my Comments controller:
class CommentsController < ApplicationController
def new
end
def create
#topic = Topic.find(params[:topic_id])
#post = Post.find(params[:post_id])
#comment = current_user.comments.build(params[:comment])
#comment.post = #post
if #comment.save
flash[:notice] = "Comment was saved."
redirect_to #post
else
flash[:error] = "Error! Try again."
render :new
end
end
end
Here is my post#show view (form should be rendering at the bottom):
<h1><%= markdown #post.title %></h1>
<div class="row">
<div class="span8">
<small>
<%= image_tag(#post.user.avatar.tiny.url) if #post.user.avatar? %>
submitted <%= time_ago_in_words(#post.created_at) %> ago by
<%= #post.user.name %>
</small>
<br><br>
<p><%= image_tag(#post.image.url) if #post.image? %></p>
<p><%= markdown #post.body %></p>
</div>
<div class="span2">
<% if can? :edit, #post %>
<%= link_to "Edit Post", edit_topic_post_path(#topic,#post), class: 'btn btn-small' %>
<% end %>
</div>
</div>
<br><br>
<h4>Comments</h4>
<%= render #comments %>
<br>
<%= render :template => "/comments/_form.html.erb", locals: { topic: #topic, post: #post, comment: #comment } %>
I would be SO grateful if anyone could help me with this problem. And let me know if there's any info I should add. Thank you!
Generally from what i have read its not recommended using 3 level nested resources. Could you link to the tutorial you are following?
Anyhow I would suggest using shallow on your routes instead.
resources :topics, shallow: true do
resources :posts, except: [:index], shallow: true do
resources :comments, only: [:create]
end
end
Will give you the following resources, which in return will make it easier to create correct forms in your view.
post_comments POST /posts/:post_id/comments(.:format) comments#create
topic_posts POST /topics/:topic_id/posts(.:format) posts#create
new_topic_post GET /topics/:topic_id/posts/new(.:format) posts#new
edit_post GET /posts/:id/edit(.:format) posts#edit
post GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format) posts#destroy
topics GET /topics(.:format) topics#index
POST /topics(.:format) topics#create
new_topic GET /topics/new(.:format) topics#new
edit_topic GET /topics/:id/edit(.:format) topics#edit
topic GET /topics/:id(.:format) topics#show
PATCH /topics/:id(.:format) topics#update
PUT /topics/:id(.:format) topics#update
DELETE /topics/:id(.:format) topics#destroy
Now you should only have to send post_id to your comments form. Read more here I haven't worked much with 3 level nesting but this should give you an idea of how you could make it easier for yourself.
EDIT:
In the create action you should build the comment using #post and then set the user to current_user.
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:comment])
#comment.user = current_user
if #comment.save
flash[:notice] = "Comment was saved."
redirect_to #post
else
flash[:error] = "Error! Try again."
render :new
end
end
And now no need for #topic
<%= render :template => "/comments/_form.html.erb", locals: { post: #post, comment: #comment } %>
Add '#comment = Comment.new' to the "show" action of the PostController. It had been defined only in the CommentController "create" action.