Rails: Matching Comment id to Specific Micropost id - ruby-on-rails

Currently I am have a comment which belongs to a micropost, but the issue is, when a user creates a comment, the comment gets stored in the database with a micropost id but the id is not for the specific micropost rather it seem as though the comment just incremented the micropost id by + 1. Very confused and would very much appreciate any help. Thank you!
Comment Model
class Comment < ActiveRecord::Base
attr_accessible :content, :user_id, :micropost_id
belongs_to :micropost
belongs_to :user
validates :content, presence: true, length: { maximum: 140 }
default_scope order: 'comments.created_at DESC'
end
Micropost Model
class Micropost < ActiveRecord::Base
attr_accessible :title, :content, :view_count
belongs_to :user
has_many :comments
accepts_nested_attributes_for :comments
end
Comments Controller
class CommentsController < ApplicationController
def create
#micropost = Micropost.find(params[:micropost_id])
#comment = #micropost.comments.build(params[:comment])
#comment.user_id = current_user.id
#comment.save
respond_to do |format|
format.html
format.js
end
end
end
Form
<div class="CommentField">
<%= form_for ([#micropost, #micropost.comments.new]) do |f| %>
<%= f.text_area :content, :class => "CommentText", :placeholder => "Write a Comment..." %>
<div class="CommentButtonContainer">
<%= f.submit "Comment", :class => "CommentButton b1" %>
</div>
<% end %>
</div>
Routes
resources :microposts do
resources :comments
end
Raked Routes
micropost_comments GET /microposts/:micropost_id/comments(.:format) comments#index
POST /microposts/:micropost_id/comments(.:format) comments#create
new_micropost_comment GET /microposts/:micropost_id/comments/new(.:format) comments#new
edit_micropost_comment GET /microposts/:micropost_id/comments/:id/edit(.:format) comments#edit
micropost_comment GET /microposts/:micropost_id/comments/:id(.:format) comments#show
PUT /microposts/:micropost_id/comments/:id(.:format) comments#update
DELETE /microposts/:micropost_id/comments/:id(.:format) comments#destroy

I think the issue here is how much work you are putting into this. Rails is built to know about most of this without the need to do what you are doing. My suggestion would be to change your comments controller to something like this
class CommentsController < ApplicationController
def create
#comment = Comment.new(params[:comment])
#comment.save
respond_to do |format|
format.html
format.js
end
end
end
since you are rendering your comments partial form through another partial you'll need to pass along the local variable of the associated post above it.
"comments/form", :locals => { :micropost => micropost } %>
and your form to something like this
<div class="CommentField">
<%= form_for ([micropost, #comment]) do |f| %>
<%= f.text_area :content, :class => "CommentText", :placeholder => "Write a Comment..." %>
<div class="CommentButtonContainer">
<%= f.submit "Comment", :class => "CommentButton b1" %>
</div>
<% end %>
</div>
In all my rails apps, that is all the association I would need to do for it to properly assign the Ids by itself. I'm sure that will fix the issue.

Related

Creating a comment reply won't work while comment works Rails 7

I am trying to allow reply to comments and for some reason when I create a comment it works uneventfully but when I reply the "body" on params comes back empty. The weird part is that I am using the same form. Please take a look:
Note: I am using Action Text rich_text_area, if I use a simple text_area it works just fine.
models/comment.rb
class Comment < ApplicationRecord
belongs_to :post
belongs_to :user
belongs_to :parent, class_name: "Comment", optional: true
has_many :comments, class_name: "Comment", foreign_key: :parent_id
has_rich_text :body
validates_presence_of :body
end
routes.rb
resources :posts do
resources :comments, only: [:create, :update, :destroy]
end
comments_controller.rb
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(comment_params)
#comment.user = current_user
respond_to do |format|
format.html do
if #comment.save
flash[:success] = "Comment was successfully created."
else
flash[:danger] = #comment.errors.full_messages.to_sentence
end
redirect_to post_url(#post)
end
end
end
private
def comment_params
params.require(:comment).permit(:body, :parent_id)
end
_comment.rb
// this works (creating a comment without parent)
= render "comments/form", post: #post, comment: #post.comments.build, submit_label: "Comment"
// this won't work (creating a comment with a parent being the current comment)
= render "comments/form", post: #post, comment: #post.comments.build, parent: comment, submit_label: "Reply"
- _form.rb
<%= form_with model: [post, comment], class: "form" do |f| %>
<% if !parent.nil? %>
<%= f.hidden_field :parent_id, value: parent.id %>
<% end %>
<div class="field">
<%= f.rich_text_area :body, placeholder: "What do you think?" %>
</div>
<div class="actions">
<%= f.submit submit_label, class: "btn btn-primary" %>
</div>
<% end %>
What puzzles me the most is that the problem is that the body comes back empty. I cant't see the relation between the body and everything else.
Parameters: {"authenticity_token"=>"[FILTERED]", "comment"=>{"body"=>"", "parent_id"=>"14"}, "commit"=>"Reply", "post_id"=>"4"}
Thanks in advance!

undefined method `post_id' with post as a join table?

Okay, so I am currently working on a commenting system where posts belong to classrooms and posts have comments. So classrooms have comments through posts.
Here are the models
class Classroom < ActiveRecord::Base
has_many :posts
has_many :comments, through: :posts
...
end
class Comment < ActiveRecord::Base
belongs_to :post
...
end
class Post < ActiveRecord::Base
belongs_to :classroom
has_many :comments
...
end
I'm trying to save post_ids but it won't let me with a simple hidden field so I tried it with a value and it still doesn't work. it says that post_id is an undefined method.
Here is my classroom controller's show method because it is where the new comment form is being rendered.
def show
#classroom = Classroom.find(params[:id])
#posts = #classroom.posts
#comments = #classroom.comments
#comment = Comment.new
end
Here is the new comment form.
<%= simple_form_for(#comment) do |f| %>
<%= f.error_notification %>
<%= f.text_area :content %>
<%= f.hidden_field :post_id, :value => #classroom.post_id %>
<div class="form-actions">
<br>
<%= f.button :submit %>
</div>
<% end %>
Error Message
NoMethodError in Classrooms#show
undefined method `post_id' for Classroom:0x007f52aa646b18
How do I save the post_id?
Thanks!
NoMethodError in Classrooms#show undefined method `post_id' for
Classroom:0x007f52aa646b18
<%= f.hidden_field :post_id, :value => #classroom.post_id %>
The error is obvious as you are doing #classroom.post_id as Classroom don't have a field called post_id
Define a #post with the help of #classroom
def show
#classroom = Classroom.find(params[:id])
#post = Post.where(classroom_id: #classroom.id).first
#posts = #classroom.posts
#comments = #classroom.comments
#comment = Comment.new
end
and use that in the hidden_field
<%= f.hidden_field :post_id, :value => #post.id %>

I can't create model objects using accepts_nested_attributes_for. It won't create the nested object

My model structure looks like this:
Board has_many Topics. Topic has_many Posts.
app/models/board.rb
class Board < ActiveRecord::Base
has_many :topics
end
app/models/topic.rb
class Topic < ActiveRecord::Base
belongs_to :user
belongs_to :board
has_many :posts
accepts_nested_attributes_for :posts
validates :title, presence: true, length: { maximum: 255 }
validates :user_id, presence: true
validates :board_id, presence: true
...
end
app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :topic
validates :user_id, presence: true
validates :topic_id, presence: true
validates :content, length: { minimum: 8 }
end
Here is my view for creating a new Topic. the fields_for section is used to create the :content on the new Post
app/views/topics/new.html.erb
<div>
<%= form_for [#board, #topic] do |f| %>
<%= render 'shared/error_messages', object: #topic %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.fields_for #post do |p| %>
<%= p.label :content %>
<%= p.text_area :content %>
<% end %>
<%= f.submit "Post new topic", class: "button submit" %>
<% end %>
</div>
When creating a new Topic, I want a new post with the :content from the form to also be created. Since the Post is dependent on having a Topic in order to be valid, they need to be created or rejected in tandem (if the :content or :title is invalid). I was told that accepts_nested_attributes_for would work correctly but when my code executes it only creates the Topic, not the Post.
app/controllers/topics_controller.rb
def new
#board = Board.find(params[:board_id])
#topic = #board.topics.build
#post = #topic.posts.build
end
def create
#board = Board.find(params[:board_id])
#topic = #board.topics.build(topic_params.merge({user_id: current_user.id}))
if #topic.save
flash[:success] = "Topic created"
redirect_to #topic
else
render 'new'
end
end
private
def topic_params
params.require(:topic).permit(:title, posts_attributes: [:content])
end
For the record, here is my Posts controller and routes, if it helps.
app/controllers/posts_controller.rb
def create
#topic = Topic.find(params[:topic_id])
#post = #topic.posts.build(post_params.merge({user_id: current_user.id}))
if #post.save
flash[:success] = "Post Created"
redirect_to topic_path(#topic)
else
render 'new'
end
end
private
def post_params
params.require(:post).permit(:content)
end
rake routes for Boards, Topics and Posts
topic_posts GET /topics/:topic_id/posts(.:format) posts#index
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
board_topics GET /boards/:board_id/topics(.:format) topics#index
POST /boards/:board_id/topics(.:format) topics#create
new_board_topic GET /boards/:board_id/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
boards GET /boards(.:format) boards#index
POST /boards(.:format) boards#create
new_board GET /boards/new(.:format) boards#new
edit_board GET /boards/:id/edit(.:format) boards#edit
board GET /boards/:id(.:format) boards#show
PATCH /boards/:id(.:format) boards#update
PUT /boards/:id(.:format) boards#update
DELETE /boards/:id(.:format) boards#destroy
And also the value of params at the start of topics_controller#create
{"utf8"=>"✓", "authenticity_token"=>"...", "topic"=>{"title"=>"New Title", "post"=>{"content"=>"New Content"}},"commit"=>"Post new topic", "action"=>"create", "controller"=>"topics", "board_id"=>"1"}
Finally found the solution here
This was after I fixed the form to create the params correctly.
Basically, I needed to use :inverse_of on my models. I don't really understand what this accomplishes but it works. Here's my code
topic.rb
class Topic < ActiveRecord::Base
belongs_to :user
belongs_to :board
has_many :posts, :inverse_of => :topic
accepts_nested_attributes_for :posts
validates :title, presence: true, length: { maximum: 255 }
validates :user, presence: true
validates :board, presence: true
end
post.rb
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :topic, :inverse_of => :posts
validates :user, presence: true
validates :topic, presence: true
validates :content, presence: true
end
app/views/topics/new.html.erb
<div>
<%= form_for [#board, #topic] do |f| %>
<%= render 'shared/error_messages', object: #topic %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.fields_for :posts do |p| %>
<!-- I needed to pass in the current_user.id for the post -->
<%= p.hidden_field :user_id, :value => current_user.id %>
<%= p.label :content %>
<%= p.text_area :content %>
<% end %>
<%= f.submit "Post new topic", class: "button submit" %>
<% end %>
</div>
app/controllers/topics_controller.rb
def create
#board = Board.find(params[:board_id])
#topic = #board.topics.build(topic_params.merge({user_id: current_user.id}))
debugger
if #topic.save
flash[:success] = "Topic created"
redirect_to #topic
else
render 'new'
end
end
private
def topic_params
params.require(:topic).permit(:title, posts_attributes: [:content, :user_id])
end

acts as commentable: comment body formatting

I think I have a working version of acts_as_commenting_with_threading in my rails app, but it seems like the body of every comment is saved with weird formatting. How do I remove the formatting in my view so it only displays the text (and not the formatting)? For example, if I type the text "test comment," the body of the comment is saved as "---\nbody: test comment\n". I tried html_safe, but it didn't work.
step.rb
class Step < ActiveRecord::Base
extend FriendlyId
acts_as_commentable
friendly_id :position
has_ancestry :orphan_strategy => :adopt
attr_accessible :description, :name, :position, :project_id, :images_attributes, :parent_id, :ancestry, :published_on
belongs_to :project
has_many :images, :dependent => :destroy
accepts_nested_attributes_for :images, :allow_destroy => :true
validates :name, :presence => true
end
comments_controller.rb
class CommentsController < ApplicationController
def create
#project = Project.find(params[:project_id])
#commentText = params[:comment]
#user = current_user
#comment = Comment.build_from(#project.steps.find(params[:step_id]), #user.id, #commentText)
respond_to do |format|
if #comment.save
format.html {redirect_to :back}
else
format.html { render :action => 'new' }
end
end
end
end
show.html.erb:
<div class="stepComments">
<% if step.comment_threads.count >0 %>
<% step.comment_threads.each do |stepComment| %>
<% if stepComment.body.length>0 %>
<%= render :partial => 'comments', :locals => {:comment=> stepComment} %>
<% end %>
<br>
<% end %>
<% end %>
</div>
_comments.html.erb
<div class="comment">
<div class="userIcon">
<%= User.find(comment.user_id).username %>
<%= image_tag(User.where(:id=>comment.user_id).first.avatar_url(:thumb), :class=>"commentAvatar img-polaroid")%>
</div>
<div class="field">
<%= comment.body %>
</div>
</div>
This prints: "---\nbody: test comment\n"
The rails helper simple_format will print using the formatting rules so you will get just the text.
For example, <% simple_format(comment.body) %>
I couldn't figure out a way to do it besides just edited the string manually. This is what I ended up using:
<%= comment.body.slice((comment.body.index(' ')+1..comment.body.length)) %>
It seems very odd that there isn't some built in function for doing this...
It ended up being a quite simple solution; I had been calling the parameter incorrectly. It should have been:
#commentText = params[:comment][:body]

Rails - How Do I Nest Comments - Polymorphism

For my application, I have Projects. I have used Polymorphism to build a model called "Newcomment" for comments made on these Projects. I followed this railscast. This works great.
But now, I want to build comments on top of comments. I tried following this tutorial (http://kconrails.com/2010/10/23/nested-comments-in-ruby-on-rails-1-models/) and (http://kconrails.com/2011/01/26/nested-comments-in-ruby-on-rails-controllers-and-views/). I put a form for comments in each comment that I render. I also adjusted the newcomment.rb model, so that newcomment has_many newcomments and the routes.rb file.
Question: Right now, when I make a comment in the form of each comment, it posts as a comment to the project and not as a response to a specific comment. How would I adjust my code so that I can have comments for comments?
newcomment.rb
class Newcomment < ActiveRecord::Base
attr_accessible :content, :user_id
belongs_to :commentable, polymorphic: true
has_many :newcomments, :as => :commentable
belongs_to :user
scope :newest, order("created_at desc")
validates :content, presence: true
end
newcomments_controller.rb
class NewcommentsController < ApplicationController
before_filter :load_commentable
before_filter :authenticate_user!
def create
#newcomment = #commentable.newcomments.new(params[:newcomment])
if #newcomment.save
redirect_to comments_project_path(#commentable), notice: "Comment created."
else
render :new
end
end
def destroy
if current_user.try(:admin?)
#newcomment = Newcomment.find(params[:id])
#commentable = #newcomment.commentable
#newcomment.destroy
if #newcomment.destroy
redirect_to comments_url, notice: "Comment deleted."
end
else
#newcomment = Newcomment.find(params[:id])
#commentable = #newcomment.commentable
#newcomment.destroy
if #newcomment.destroy
redirect_to comments_project_path(#commentable), notice: "Comment deleted."
end
end
end
private
def load_commentable
resource, id = request.path.split('/')[1,2]
#commentable = resource.singularize.classify.constantize.find(id)
end
end
routes.rb
resources :projects do
resources :newcomments do
resources :newcomments
end
end
view/projects/_comments.html.erb
<%= render #newcomments %>
projects_controller.rb
def comments
#commentable = #project
#newcomments = #commentable.newcomments.newest.page(params[:comments_page]).per_page(10)
#newcomment = Newcomment.new
end
view/newcomments/_newcomment.html.erb
<div class="comments">
<%= link_to newcomment.user.name %></strong>
Posted <%= time_ago_in_words(newcomment.created_at) %> ago
<%= newcomment.content %>
</div>
<span class="comment">
<%= form_for [#commentable, #newcomment] do |f| %>
<div class="field">
<%= f.text_area :content, rows: 3, :class => "span8" %>
</div>
<%= f.hidden_field :user_id, :value => current_user.id %>
<div class="actions">
<%= f.submit "Add Comment", :class => "btn btn-header" %>
</div>
<% end %>
<% unless newcomment.newcomments.empty? %>
<%= render #newcomments %>
<% end %>
</span>
All you need, instead of working on it from a scrap, is: https://github.com/elight/acts_as_commentable_with_threading
You should not have the routes like this
resources :newcomments do
resources :newcomments
end
which is a bad smell and we need to fix. In some of our projects, we are also using https://github.com/elight/acts_as_commentable_with_threading and it's good.

Resources