I'm pretty much an amateur when it comes to Rails. I got this to work, however I feel like the code is not efficient enough.
Is there any way to speed this up? And also it this how a professional would do it?
Controller
def mark_read
#topic = Topic.find(params[:id])
#topic.mark_as_read! :for => current_user
redirect_to user_path(current_user.slug)
end
def mark_all_read
Topic.mark_as_read! :all, :for => current_user
redirect_to user_path(current_user.slug)
end
Routes
resources :users do
member do
post :mark_read
post :mark_all_read
end
end
View
<% if current_user.id == #user.id %>
<%= link_to "Mark all as read", mark_all_read_user_path, :method=> :post %>
<h4> List of posts unread by you </h4>
<% #unread.each do |topic| %>
<% if #user.following?(Product.find(topic.product_id)) %>
<li> <%= topic.title %> <%= link_to "Mark as read", mark_read_user_path(topic), :method=> :post %> </li>
<% end %>
<% end %>
Is there someway that I could call an action in the controller without a route? I feel it would make the workflow neater.
The answer to your question is no. You can not have access any action in the controller unless it has a route, or its a CRUD generated by default, for example:
resources :articles
generates
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
Related
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.
I am following this tutorial: Getting Started with Rails http://guides.rubyonrails.org/getting_started.html
I have searched on stack: No route matches [POST] “/articles/new” No route matches [POST] "/articles/new" and the recommended spelling correction did not help with my error.
You can find my git: https://github.com/tomile/rails5Blog/tree/adding-partial.
routes.rb
Rails.application.routes.draw do
resources :articles
get 'welcome/index'
root 'welcome#index'
end
articles_controller.rb
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
_form.html.erb (didnt copy paste whole file)
<%= form_for :article do |f| %>
...
<p>
<%= f.submit %>
</p>
<% end %>
new.html.erb
<h1>New Article</h1>
<%= render 'form' %>
<%= link_to 'Back', articles_path %>
$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
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
First thing new route is of type GET.
Second thing is that when we use form_for we provide a instance to it.
So in your articles controller
def new
#article = Article.new
end
and then in form use
<%= form_for #article do |f| %>
It will point the form action to create method of article.
One step of tutorial is missed by you.
You must change first row of form to following:
<%= form_for :article, url: articles_path do |f| %>
After that form works fine.
I'm developing a simple blogger app and I'm trying to add comment functionality to it. I'm trying to add delete/edit functionality to the comment, but I don't know how to get id of the comment I'm trying to edit. Here is what html code looks like:
<div class = "comment">
<p class = "author">Comment by <%= comment.author_name %>
<span class="creationTime"> <%= distance_of_time_in_words(comment.created_at, Time.now) %> ago</span>
</p>
<p class="text"><%= comment.body %></p>
<div class = "Button">
<%= link_to "Edit this comment", edit_article_comment_path(#comment.article_id, #comment.id) %>
</div>
</div>
Article controller show action:
def show
#article = Article.find(params[:id])
#comment = Comment.new
#comment.article_id = #article.id
end
Routes:
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 / articles#index
Error I get:
No route matches {:action=>"edit", :article_id=>"1", :controller=>"comments", :id=>nil} missing required keys: [:id]
Sorry if I'm asking a stupid question, I'm very new to this
You article controller should be look like:
def show
#article = Article.find(params[:id])
#comments = #article.comments
end
Your view should be look like:
<% #comments.each do |comment| %>
<div class = "comment">
<p class = "author">Comment by <%= comment.author_name %>
<span class="creationTime">
<%= distance_of_time_in_words(comment.created_at, Time.now) %> ago
</span>
</p>
<p class="text"><%= comment.body %></p>
<div class = "Button">
<%= link_to "Edit this comment", edit_article_comment_path(#article, comment) %>
</div>
</div>
<% end %>
Add has_many association to Article model as well.
has_many :comments
Hope you have added article_id to comments table
Try this ......
<div class = "Button">
<%= link_to "Edit this comment", edit_article_comment_path(#article.id, #comment.id) %>
</div>
In place of
<div class = "Button">
<%= link_to "Edit this comment", edit_article_comment_path(#comment.article_id, #comment.id) %>
</div>
Hope this will work for you.
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").
**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