AlxGol
AlxGol

Reputation: 156

Comment author. Rails

I want show the user email as author of comment, but I see this error "undefined method `email' for nil:NilClass" comment.rb

class Comment < ActiveRecord::Base
  belongs_to :hotel
  belongs_to :user
end

user.rb

class User < ActiveRecord::Base
  has_many :hotels
  has_many :comments
end

hotel.rb

class Hotel < ActiveRecord::Base
  belongs_to :user
  belongs_to :address
  has_many :comments
  mount_uploader :avatar, AvatarUploader
  accepts_nested_attributes_for :address
end

comments_controller.rb

  def create
    @hotel = Hotel.find(params[:hotel_id])
    @comment = @hotel.comments.new(comment_params)
    @comment.user_id = current_user.id
    @comment.save
    redirect_to @hotel
  end

private
  def comment_params
    params.require(:comment).permit(:user_id, :body, :hotel_id)
  end

_comments.html.haml

= div_for comment do
  %p
    %strong
      Posted #{time_ago_in_words(comment.created_at)} ago
    %br/
    = h comment.user.email
    %br
    = comment.body

Upvotes: 1

Views: 526

Answers (1)

Richard Peck
Richard Peck

Reputation: 76784

Method

The error that you're calling a method which doesn't exist.

The problem is you're calling a method on an associated object which doesn't exist. You probably don't have any user associated to the comment - thus preventing you from being able to call the email method.

Firstly, you need to make sure you have the correct association. Here's how to do that:

$ rails c
$ comment = Comment.find([id])
$ comment.update(user_id: [your_user_id])
$ exit

This will allow you to associate the comment to a particular user, giving you the ability to call the associated method.

--

Controller

When you save your comment in your controller, you need to assign your user to it. We do this using the strong_params functionality, as its the DRYest way we've found:

#app/controllers/comments_controller.rb
Class CommentsController < ApplicationController
    def create
       @comment = Comment.new(comment_params)
    end

    private
    def comment_params
       params.require(:comment).permit(:your, :comment: attributes).merge(user_id: current_user.id)
    end
end  

This will allow you to associate the user at save time, giving you the ability to call the methods you need next time you call the record!


Delegate

You'll also benefit from using the delegate method like this:

#app/models/comment.rb
Class Comment < ActiveRecord::Base
   belongs_to :user
   belongs_to :hotel

   delegate :email, to: :user, prefix: true #-> allows you to call `@comment.user_email`
end

This will solve the law of Demeter issue (where you should aim to have one "point" in your calls")

Upvotes: 3

Related Questions