I'm currently working on my Rails Blog. While the development is coming along nicely, I've hit a roadblock in the comment.
Every time I try to leave a test comment, I always get "rollback transaction" just before the comment gets saved.
Here's what happens when I create a comment.
Here's the code for looking at blog entry:
post/show.html.erb
<h1 class="title">
<%= #post.title %>
</h1>
<h2>Authored by:
<%= #post.user.username%>
</h2>
<p>
<%=raw #post.content %>
</p>
<div class="comments">
<h3 class="is-3">Comments</h3>
<%if #post.comments.count == 0%>
<strong>There are no comments on this post. Feel free to send one!</strong><br/>
<% else %>
<%= render #post.comments %>
<% end %>
<%if !user_signed_in? %>
<strong>Yo, if you wanna comment on my site, either <%= link_to 'Sign up', new_user_registration_path %> or <%= link_to 'Log in', new_user_session_path %></strong>
<% else %>
<%= render partial: "comments/form", locals: {comment: #post.comments.new} %>
<% end %>
</div>
</div>
<br>
<% if user_signed_in? %>
<div class="functions">
<%= link_to "Update", edit_post_path(#post) if policy(#post).update? do%>
<i class="fa fa-edit editpage fa-3x"></i>
<% end %>
<%= link_to "Delete", #post, :confirm => "Are you sure you want to delete this post?", :method => :delete if policy(#post).destroy? do%>
<i class="fa fa-trash deletepage fa-3x"></i>
<% end %>
</div>
<% end %>
<%= link_to 'Back to the main page', root_path %>
In case you're curious, here's what the comment partial looks like:
<strong><%= #post.comment.user.username %> says:</strong><br>
<%=#post.comment.content %>
<p>
<=time_ago_in_words(comment.created_at) %>
</p>
Can anyone help me figure out why is it that when I create a comment, it rolls back the transaction? I'll provide more information if required.
Edit: Here's the Comments Controller for my rails blog.
class CommentsController < ApplicationController
before_action :set_post
def create
set_post
# Create associated model, just like we did in the console before
#comment = #post.comments.create(comment_params)
# We want to show the comment in the context of the Post
#comment.post_id = #post.post_id
#comment.user_id = current_user.user_id
#comment.save
redirect_to #post
end
def update
#comment = set_comment
#comment.update(comment_params)
redirect_to #post
end
def destroy
#comment = set_comment
#comment.destroy
redirect_to #post
end
private
def comment_params
params.require(:comment).permit(:content)
end
def set_post
#post = Post.friendly.find(params[:post_id])
end
def set_comment
#comment = Comment.find(params[:id])
end
end
Comment Model:
class Comment < ApplicationRecord
belongs_to :post
belongs_to :user
end
Also, the Comment form
<strong><%= #post.comment.user.username %> says:</strong><br>
<%=#post.comment.content %>
<p><= time_ago_in_words(comment.created_at) %></p>
Related
I am following ruby-on-rails instruction guide to creating a simple blog web application: https://guides.rubyonrails.org/getting_started.html#generating-a-controller
All my project files are pretty much the same as the ones in the guide.
app/views/articles/show.html.erb
<p>
<strong>Title:</strong>
<%= #article.title %>
</p>
<p>
<strong>Text:</strong>
<%= #article.text %>
</p>
<h2>Add a comment:</h2>
<%= render 'comments/form' %>
<h2>Comments (<%= #article.comments.count %>)</h2>
<%= render 'comment_section' %>
<%#= render #article.comments %>
<%= link_to 'Edit', edit_article_path(#article) %> |
<%= link_to 'Delete', article_path(#article),
method: :delete,
data: {confirm: 'Are you sure?'} %> |
<%= link_to 'Back', articles_path %>
app/views/comments/_form.html.erb
<%= form_with(model: [#article, #article.comments.build], local: true) do |form| %>
<p>
<%= form.label :commenter %><br>
<%= form.text_field :commenter %>
</p>
<p>
<%= form.label :body %><br>
<%= form.text_area :body %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
app/views/articles/_comment_section.html.erb
<% if #article.comments.count > 0 %>
<%= render #article.comments %>
<% else %>
<p>There are no comments yet!</p>
<% end %>
app/views/comments/_comment.html.erb
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<p>
<%= link_to 'Delete comment', [comment.article, comment],
method: :delete,
data: {confirm: 'Are you sure you want to delete this comment?'}
%>
A simple article with no comments works as expected:
However, when showing an article with some actual comments, an extra empty comment gets displayed at the end:
When I try to delete that comment I get the following error (11 in the path is the article_id):
Deleting other comments works fine.
Rest of the files that I think might be relevant:
app/config/routes.rb
Rails.application.routes.draw do
get 'welcome/index'
resources :articles do
resources :comments
end
root 'welcome#index'
end
app/models/article.rb
class Article < ApplicationRecord
has_many :comments, dependent: :destroy
validates :title, presence: true, length: {minimum: 5}
end
app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :article
end
app/controllers/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 edit
#article = Article.find(params[:id])
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.create(comment_params)
redirect_to article_path(#article)
end
def destroy
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
#comment.destroy
redirect_to article_path(#article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
I'm using:
ruby 2.6.5p114
Rails 6.0.0
sqlite3 3.8.7.2
RubyMine 2019.2.3
I'm developing on Windows
The reason why this is happening is this line:
<%= form_with(model: [#article, #article.comments.build], local: true) do |form| %>
The part that says #article.comments.build is building an empty comment on the article. If there are no comments on the article and you were to print out #article.comments.count it would be zero. It does this because #article.comments.count runs a query, and since the blank comment isn't saved yet, it doesn't count it against the comments count.
As a side note, #article.comments.size would return 1, since in this case it returns the size of the relation with the blank comment. This is why you don't get a blank comment when the article has no comments.
However if you were to already have a comment and print out #article.comments.count, it would be 1 because now you have a saved comment in the database. This renders your comments out on the page now. The thing is that there is a blank comment inside of the #article.comments return value. This gets printed out to the screen, and since it doesn't have an id, the route for delete gets rendered like this /article/11/comments without a comment id. This route does not exist, so you get an error.
One possible way to fix this would be to change this line in your comment_section partial from this:
<%= render #article.comments %>
to this:
<%= render #article.comments.select { |comment| comment.persisted? %>
UPDATE:
I think that arieljuod's solution is even cleaner, to change this:
<%= form_with(model: [#article, #article.comments.build], local: true) do |form| %>
To this:
<%= form_with(model: [#article, Comment.new], local: true) do |form| %>
in your views/comments/_comment.html.erb
change
<%= link_to 'Delete comment', [comment.article, comment],
method: :delete,
data: {confirm: 'Are you sure you want to delete this comment?'} %>
to
<%= link_to 'Delete comment', comment_path(comment),
method: :delete,
data: {confirm: 'Are you sure you want to delete this comment?'} %>
Yes, I already checked here. It didn't work.
Rails: 5.0.2
Ruby: 2.4.0
I have a collection for comments and every time it is called, it renders one time too many and an empty comment always appears below others and when no comments exist, one empty one still renders.
Here is the code:
View
<h2>Add a comment:</h2>
<%= render 'comments/form' %>
<h2>Comments</h2>
<%= render #video.comments || "There are no comments yet." %>
Form partial
<%= form_for([#video, #video.comments.new]) do |f| %>
<p>
<%= f.label :name %><br>
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Comment partial
<p>
<strong>Name:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<p>
<%= link_to 'Destroy Comment', [comment.video, comment],
method: :delete,
data: { confirm: 'Are you sure?' } %>
</p>
Controller
def create
#video = Video.find(params[:video_id])
#comment = #video.comments.create(comment_params)
redirect_to video_path(#video)
end
def destroy
#video = Video.find(params[:video_id])
#comment = #video.comments.find(params[:id])
#comment.destroy
redirect_to video_path(#video)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
Does anyone know why this would render the partial an extra time?
You may try to call .scope on comments association:
<%= render #video.comments.scope || "There are no comments yet." %>
I'm using Carrierwave with Rails to upload and display images. I've been using it for articles and other parts of my site without a problem but for some reason it's not working for the model I just created. I can see the space for the image tag rendered without the image and then disappear quickly when the page loads. Nothing like this has occurred until I tried it with this model which isn't much different from my other models:
class Ad < ActiveRecord::Base
validates_numericality_of :zip_code
validates_presence_of :image, :advertiser
mount_uploader :image, ImageUploader
end
My create and index actions:
def create
#ad = Ad.new(params[:ad].permit(:image, :advertiser, :zip_code))
if #ad.save
redirect_to :back
else
flash[:error] = "Invalid input"
redirect_to :back
end
end
def index
#new_ad = Ad.new
#ads = Ad.all.reverse
end
My _new partial:
<div id="card">
<%= form_for #new_ad, html: {multipart: true, "data-ajax" => false} do |f| %>
<p>
<%= f.file_field :image %>
</p>
<p>
<%= f.text_field :advertiser, placeholder: "Advertiser" %>
</p>
<p>
<%= f.text_field :zip_code, placeholder: "Zip code" %>
</p>
<p>
<%= f.submit "Upload" %>
</p>
<% end %>
</div></br>
My index view:
<%= render "new" %>
<% for ad in #ads %>
<div id="card">
<div align="center">
<%= image_tag ad.image %>
<%= ad.advertiser %>
<%= ad.zip_code %>
</div>
</div></br>
<% end %>
As per my understanding problem is with your index view.
<%= image_tag ad.image %>
you are using ad.image this will return you imageuploader object not image path. So instead of using ad.image you can use ad.image.url(relative path) or ad.image.current_path(absolute path)
<%= image_tag ad.image.url %>
I created a user profile page that shows all the listings the user made. Each listing has a 'show' link that links to a new page that shows the listing individually on a different page. I got this to work for the user profile page.
I'm using this link
<li><%= link_to "Show", user_listing_path(name: #user.name, id: listing.id) %></li>
However, now I want to create a listings index page that shows all listings of every user. Each listing again should have a 'show' link that links to a page that shows the listing individually. The same link that worked on the user profile page does not work on the listing index page.
I get the following error undefined method `name' for nil:NilClass
and it points to
<%= link_to "Show", user_listing_path(name: #user.name, id: listing.id) %>
Does anyone know why?
User show file (show.html.erb)
<div class= "showuser">
<div class="error-message">
<% flash.each do |key, value| %>
<div class="alert alert-<%= key %>"><%= value %></div>
<% end %>
</div>
<h4>
<%= gravatar_for #user %>
<%= #user.name %>
</h4>
<div class="span 8">
<% if #user.listings.any? %>
<h3> Job Posts (<%= #user.listings.count %>)</h3>
<ol class="listings">
<%= render #listings %>
<% #listings.each do |listing| %>
<% end %>
</ol>
<%= will_paginate #listings %>
<% end %>
</div>
</div>
listing file (_listing.html.erb)
<li>
<h4><%= listing.title %></h4>
<p><%= listing.location %></h4><br>
<span class="content"><%= listing.description %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(listing.created_at) %> ago
</span>
<li><%= link_to "Show", user_listing_path(name: #user.name, id: listing.id) %></li>
<% if current_user?(listing.user) %>
<li><%= link_to "Edit", edit_listing_path %></li>
<%= link_to "delete", listing, method: :delete,
data: { confirm: "You sure?" },
title: listing.description %>
<% end %>
</li>
listing controller
class ListingsController < ApplicationController
before_action :signed_in_user, only: [:create, :destroy, :edit, :update]
before_action :correct_user, only: [:destroy, :edit, :update]
def create
#listing = current_user.listings.build(listing_params)
if #listing.save
flash[:success] = "Job Post created"
redirect_to current_user
else
render 'listings/new'
end
end
def edit
end
def update
if #listing.update_attributes(listing_params)
flash[:success] = "Listing updated"
redirect_to #listing
else
render 'edit'
end
end
def show
#user = User.find_by_name(params[:name])
#listing = Listing.find_by_id(params[:id])
end
def new
#listing = Listing.new
#listings = Listing.paginate(page: params[:page])
end
def destroy
#listing.destroy
redirect_to current_user
end
def index
#listings = Listing.all
#listings = Listing.paginate(page: params[:page])
#user = User.find_by_name(params[:name])
#listing = Listing.find_by_id(params[:id])
end
private
def listing_params
params.require(:listing).permit(:description, :location, :title)
end
def correct_user
#listing = current_user.listings.find_by(id: params[:id])
redirect_to current_user if #listing.nil?
end
end
listing show file (show.html.erb) Shows listing indivudally
<div class="show_listing">
<div class="col-md-6">
<div class="col-md-6">
<h3><%= #listing.title %></h3>
<h3><%= #listing.location %></h3>
<p><%= #listing.description %></p><br>
<div class="center">
<%= link_to "Apply Now", '#', class: "btn btn-info", data: {no_turbolink: true} %>
</div>
</div>
</div>
</div>
<div class="show_link_position">
<% if current_user == #listing.user %>
<%= link_to 'Edit', edit_listing_path, class: "btn btn-link" %> |
<% end %>
<%= link_to 'Back', current_user, class: "btn btn-link" %>
</div>
listing index file (index.html.erb)
<div class="top">
<div class="categories-container">
<div class="boxed grid-3 category-link ">
<li><%= link_to "Find Jobs", findjobs_path, class: "category-link"%></li>
<li><%= link_to "Post Jobs", new_path, class: "category-link" %></li>
<li><%= link_to "Find Jobs", findjobs_path, class: "category-link" %></li>
<li><%= link_to "Post Jobs", new_path, class: "category-link" %></li>
<li><%= link_to "Find Jobs", findjobs_path, class: "category-link" %></li>
<li><%= link_to "Post Jobs", new_path, class: "category-link" %></li>
<li><%= link_to "Find Jobs", findjobs_path, class: "category-link" %></li>
<li><%= link_to "Post Jobs", new_path, class: "category-link" %></li>
</div>
</div>
<div class="table-container">
<div class= "grid-8 grid-moved">
<% #listings.each do |listing| %>
<h4><%= listing.title %></h4>
<h5> <%= listing.user.name %>, Posted <%= time_ago_in_words(listing.created_at) %> ago</h5>
<h5>Job Description:</h5>
<p><%= listing.description %></p>
<p>Location: <%= listing.location %></p>
<li><%= link_to "Show", user_listing_path(name: #user.name, id: listing.id) %></li>
<br><hr><br>
<% end %>
</div>
</div>
<div class="container">
<div class="pagination">
<%= will_paginate #listings %>
</div>
</div>
routes
Rails.application.routes.draw do
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :listings
root 'static_pages#home'
match '/signup', to: 'users#new', via: 'get'
match '/signin', to: 'sessions#new', via: 'get'
match '/signout', to: 'sessions#destroy', via:'delete'
match '/help', to: 'static_pages#help', via: 'get'
match '/contact', to: 'static_pages#contact', via: 'get'
match '/about', to: 'static_pages#about', via: 'get'
match '/new', to: 'listings#new', via: 'get'
match '/users/:name/:id', to: 'listings#show', via: :get, as: :user_listing
match '/findjobs', to: 'listings#index', via: 'get'
Let me know if I forgo any important information.
You have not defined #user in the controller.
In the next line, you appropriately access the listing user, as follows:
<% if current_user?(listing.user) %>
You simply need to adjust your code to look like:
<li><%= link_to "Show", user_listing_path(name: listing.user.name, id: listing.id) %></li>
Make sense?
Try:
<li><%= link_to "Show", user_listing_path(#user, listing) %></li>
And on your controller:
def show
#user = User.find(params[:id)
#listing = Listing.find(params[:id])
end
I was following the Michael Hartl tutorial for creating a basic twitter type app, and I am trying to figure out how to get my posts to show up on a follower's wall rather than always posting to my own. After going through my code and various other answers, I believe the mistake lies with in the forms tags I set up inside of my HTML... Is there some very noticeable mistake? Or maybe a helpful tutorial on the topic that you know of?
<div class="user-post">
<%= form_for(#micropost) do |f| %>
<div>
<%= f.text_area :content, placeholder: "Compose new micropost...", class: 'create-post-input' %>
<div>
<%= f.submit "Post", class: 'post-button-light' %>
</div>
</div>
<% end %>
<div>
<div class= "scrollBox" >
<% if #user.microposts.any? %>
<% #microposts.each do |micropost| %>
<li>
<%= micropost.content %>
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete %>
<%end %>
</li>
<% end %>
<%end%>
</div>
</div>
Controllers:
def show
#micropost = current_user.microposts.build if signed_in?
#feed_items = current_user.feed.paginate(page: params[:page])
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
Micropost controller:
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
redirect_to current_user
else
redirect_to current_user
end
end
def destroy
#micropost.destroy
redirect_to current_user
end
private
def micropost_params
params.require(:micropost).permit(:content)
end
def correct_user
#micropost = current_user.microposts.find_by(id: params[:id])
redirect_to root_url if #micropost.nil?
end