Reputation: 71
I use closure_tree gem for my project to nest micropost but it has errors. Here is my controller:
def index
@microposts = current_user.microposts.hash_tree
end
def new
@micropost = current_user.microposts.build(parent_id: params[:parent_id])
end
def create
if params[:micropost][:parent_id].to_i > 0
parent = current_user.microposts.find_by_id(params[:micropost].delete(:parent_id))
@micropost = parent.children.build(micropost_params) # error in here
else
@micropost = current_user.microposts.build(micropost_params)
end
if @micropost.save
flash[:success] = 'Micropost created!'
redirect_to root_url
else
@feed_items = []
render 'static_pages/home'
end
end
private
def micropost_params
params.require(:micropost).permit(:content, :picture)
end
# Returns the current logged-in user (if any).
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
@current_user = user
end
end
end
end
I can not reply any micropost. First, if I reply myself, error is "User can not be blank". Second, if I reply any user micropost, error is "undefined method `children' for nil:NilClass". Creating micropost is normally.
Upvotes: 2
Views: 102
Reputation: 76774
Firstly, use acts_as_tree
- closure_tree
is good but not as good, except maybe for the querying (which closure_tree
does with 1 select).
--
Anyways, there are quite a few fixes to be made. I'll just detail how I'd do it:
#app/models/micropost.rb
class Micropost < ActiveRecord::Base
has_many :comments
end
#app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :micropost
acts_as_tree #-> requires parent_id column in table
end
This will give you the appropriate setup in your models. Here's how to handle it properly in your controller:
#config/routes.rb
resources :microposts do
resources :comments, only: [:create]
end
#app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
def show
@micropost = Micropost.find params[:id]
@comment = @micropost.comments.new
end
end
#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
@micropost = Micropost.find params[:micropost_id]
@comment = @micropost.comments.new comment_params
redirect_to @micropost if @comment.save
end
private
def comment_params
params.require(:comment).permit(:title, :body)
end
end
This will allow you to show the Micropost
& Comments
in the views:
#app/views/microposts/show.html.erb
<%= render @micropost %>
<%= render "comments/new" %>
<%= render @micropost.comments if @micropost.comments.any? %>
#app/views/microposts/_micropost.html.erb
<%= micropost.title %>
<%= micropost.body %>
#app/views/micropost/_comment.html.erb
<%= comment.title %>
<%= comment.body %>
<%= render @micropost.comments.children if @micropost.comments.children.any? %>
Thanks for the comment, you'll want to look at Single Table Inheritance
if you have exactly the same models:
#app/models/micropost.rb
class Micropost < ActiveRecord::Base
#columns id | type | parent_id | title | body | created_at | updated_at
has_many :comments
end
#app/models/comment.rb
class Comment < Micropost
#no table needed
belongs_to :micropost
acts_as_tree
end
My above code will work in exactly the same way except a single table will be used now.
Upvotes: 2