Undefined method, avatar and name for nil:NilClass - ruby-on-rails

undefined method avatar?' for nil:NilClass
undefined methodname' for nil:NilClass
Hi, I'm receiving the following errors in my partial. The reason I listed both is because after commenting out the line causing the first error message, I get the second error which leads me to believe the problem isn't with "avatar" or "name" specifically, but with something else,though I don't know what. In rails console, I'm able to call user and name on a comment. I also seeded the database using Faker if that matters. Here's the partial.
<%= content_tag :div, class: 'media', id: "comment-#{comment.id}" do %>
<%= link_to '#', class: 'pull-left' do %>
<%= image_tag(comment.user.avatar.small.url) if comment.user.avatar? %>
<% end %>
<div class="media-body">
<small>
<%= comment.user.name %> commented <%= time_ago_in_words(comment.created_at) %> ago
<% if policy(comment).destroy? %>
| <%= link_to "Delete", [#topic, #post, comment], method: :delete %>
<% end %>
</small>
<p><%= comment.body %></p>
</div>
<% end %>
Also, please see the render.
<div class="col-md-4">
<% if policy(Comment.new).create? %>
<h4>Leave a comment</h4>
<br/>
<%= render partial: 'comments/comment', locals: { topic: #topic, post: #post, comment: #comment } %>
<% end %>
</div>
The below are my user model and comments_controller
class UsersController < ApplicationController
before_filter :authenticate_user!
def update
if current_user.update_attributes(user_params)
flash[:notice] = "User information updated"
redirect_to edit_user_registration_path(current_user)
else
render "devise/registrations/edit"
end
end
private
def user_params
params.require(:user).permit(:name, :avatar)
end
end
Comments_controller
def create
#topic = Topic.find(params[:topic_id])
#post = #topic.posts.find(params[:post_id])
#comments = #post.comments
#comment = current_user.comments.build(comment_params)
#comment.post = #post
#new_comment = Comment.new
authorize #comment
if #comment.save
redirect_to [#topic, #post], notice: "Comment was submitted successfully."
else
flash[:error] = "There was an error submitting the comment. Please try again."
end
end
I've already reset the database, but to no avail. Stuck as to what the issue is. Thanks for your help.
Please see below for my User and Comment models.
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
default_scope { order('created_at DESC') }
validates :body, length: { minimum: 5 }, presence: true
after_create :send_favorite_emails
private
def send_favorite_emails
self.post.favorites.each do |favorite|
if favorite.user_id != self.user_id && favorite.user.email_favorites?
FavoriteMailer.new_comment(favorite.user, self.post, self).deliver
end
end
end
end
User model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_many :posts
has_many :comments
has_many :votes, dependent: :destroy
has_many :favorites, dependent: :destroy
mount_uploader :avatar, AvatarUploader
def role?(base_role)
role == base_role.to_s
end
def favorited(post)
self.favorites.where(post_id: post.id).first
end
def voted(post)
self.votes.where(post_id: post.id).first
end
private
end

If you're getting
undefined method foo for nil:NilClass
it's that the thing you're calling your method on is nil.
So in your case, you're calling avatar? and name on something nil.
Looking at your code, it's clear comment.user is (a) what those methods are called on, and hence (b) what is nil.
Result: your comment has no user. Either enforce all comments (including new/empty/stub ones) to have an user (blank user?), or make your view so that a user is not necessary.

The issue was discovered. In the partial render
comment: #comment
should be
comment: comment

Related

How to debug why an update won't work with Rails associations?

I'm trying to setup a simple rails app with job board functionality. I was able to add jobs to the database, until I added an association between my Job model and devise User model. Now it won't update the database when I fill out the form.
jobs_controller
class JobsController < ApplicationController
def index
#jobs = Job.all
end
def new
#job = Job.new
end
def listing
end
def listings
end
def create
#job = Job.new(params.require(:job).permit(:title, :description, :url, :user_id))
if #job.save
redirect_to root_path
else
render "new"
end
end
end
new.html.erb
<%= simple_form_for #job do |form| %>
<%= form.input :title, label: "Job title" %>
<%= form.input :description, label: "Description" %>
<%= form.input :url, label: "URL" %>
<%= form.button :submit %>
<% end %>
index.html.erb
<% #jobs.each do |job| %>
<div class="job">
<h2><%= link_to job.title, job.url %></h2>
<p><%= job.description %></p>
</div>
<% end %>
<p><%= link_to "Add a job", new_job_path %></p>
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :jobs
end
job.rb
class Job < ApplicationRecord
belongs_to :user
end
There isn't an error in the console, but the database doesn't seem to be updated or it's not updating the view.
I also ran a migration:
class AddUserToJob < ActiveRecord::Migration[5.2]
def change
add_reference :jobs, :user, foreign_key: true
end
end
You can get the user with current_user in Devise.
class JobsController < ApplicationController
# This restricts the actions to authenticated users and prevents a nil error
before_action :authenticate_user, except: [:show, :index]
# ...
def create
# this sets the user_id column
#job = current_user.jobs.new(job_params)
if #job.save
# you really should set a flash message or something to notify the user
# and possibly redirect to the show or index action instead
redirect_to root_path
else
render "new"
end
end
private
def job_params
params.require(:job)
.permit(:title, :description, :url, :user_id)
end
end
If you don't want to associate the job immediately to a user, you need to change the association to be optional, like:
class Job < ApplicationRecord
belongs_to :user, optional: true
end
Else you need to supply user_id in your form or set it in the controller action.
You should also delegate this part to a separate method
def job_params
params.require(:job).permit(:title, :description, :url, :user_id)
end
Job.new(job_params)

How to ensure users can only delete their own comments? Rails

I have comments underneath a bunch of questions, right now the code I have shows the delete comment link under everyone's comments not just the user who made the comment. How do I fix this so that users can only delete comments they made themselves? I am using devise gem to authenticate users.
<% commentable.comments.each do |comment| %>
<h6 style="text-align:left; margin-bottom: 0px;"><strong><%= comment.user.profile.first_name %> <%= comment.user.profile.last_name %>: </strong></h6>
<p style="text-align:left">
<%= comment.body %>
</p>
<% if current_user %>
<p style="text-align:left; font-size: 12px; margin-top: -10px"><%= link_to 'Delete', [comment.user, comment],
method: :delete,
data: { confirm: 'Are you sure?' } %></p>
<% end %>
<% end %>
comments_controller.rb
class CommentsController < ApplicationController
before_action :authenticate_user!
def create
#comment = #commentable.comments.new(comment_params)
#comment.user = current_user
if #comment.save
redirect_back(fallback_location: root_path)
end
end
def update
#comment.update(comment_params)
end
def destroy
#comment = Comment.find(params[:id])
#comment.destroy
redirect_back(fallback_location: root_path)
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
comment.rb
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
belongs_to :user
end
user.rb
class User < ApplicationRecord
before_create :add_role_to_user
ROLES = %w[admin member].freeze
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile
has_many :comments, dependent: :destroy
def add_role_to_user
self.role = 'member'
end
end
Change your view:
<% if current_user && current_user == comment.user %>
Change your controller:
def destroy
# ensure user only can find owner comment.
#comment = current_user.comments.find(params[:id])
#comment.destroy
redirect_back(fallback_location: root_path)
end
If you change <% if current_user %> in your view to <% if current_user && current_user == comment.user %> then the Delete link will only appear for the owner of that comment.
You should also check that the current_user matches the #comment.user in your destroy controller method.

How to create the backend to multiple user types displayed in radio buttons? Devise and Rails

I'm fairly new to rails. I took an idea from another user when creating multiple user types using STI. So, at this point, I have two user types, teachers and students, and they all are shown as
Class Teacher < User
Class Student < User
I used Devise which automatically create a signup (registrations path) and a user model.
<div class="authform">
<%= form_for #users(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :role => 'form'}) do |f| %>
And then skipping ahead to the radio buttons:
<div>
<%= "I am a..." %>
<%= radio_button_tag(:user, "student") %>
<%= label_tag(:student, "student") %>
<%= radio_button_tag(:user, "teacher") %>
<%= label_tag(:teacher, "teacher") %>
</div>
<%= f.submit 'Sign Up', :class => 'button right' %>
<% end %>
</div>
How do I attach this to backend and user types? Here is my user.rb
class User < ActiveRecord::Base
has_many :posts, dependent: :destroy
def skip_confirmation!
self.confirmed_at = Time.now.utc
end
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :user
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
#:confirmable,
:recoverable, :rememberable, :trackable, :validatable
def to_param
username
end
end
And here is my user controller:
class UsersController < ApplicationController
before_action :authenticate_user!
after_action :verify_authorized
def show
#user = User.find(params[:id])
authorize #user
#posts = #user.posts
end
def new
#user = User.new
authorize #user
redirect_to 'classrooms#index'
end
def update
#user = User.find(params[:id])
authorize #user
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
user = User.find(params[:id])
# authorize #user
user.destroy
redirect_to users_path, :notice => "User deleted."
end
private
def secure_params
params.require(:user).permit(:role)
end
end
I'm not sure what you were trying to do.
If you were trying to send data which is from radio button, you can add a form_tag.
Before that, we should probably define our controller.
For example, I define a create_user_type in user_controller.rb
def create_user_type
byebug ## it will bind your termianl and help you to see the parameter
end
Then edit routes.rb, add this line.
post 'users/create_user_type' => 'users/create_user_type'
Finally, change your form.html.erb
<%= form_tag users_create_user_type_url do |f| %>
<%= radio_button_tag(:user, "student") %>
<%= label_tag(:student, "student") %>
<%= radio_button_tag(:user, "teacher") %>
<%= label_tag(:teacher, "teacher") %>
<%= submit_tag "Save!"%>
<% end %>
So, in your browser, you can see this
After you click the save button, you should notice your terminal is binding.
Now you can print the params to see what's the data from the radio button.

Undefined method in User_Controller - To do List

I'm working on a to-do list in rails and I'm getting the following: undefined method items for nil:NilClass in my users_controller.rb.
The program was working to the point where I could delete and create the list and have it take me to the new_list_path. However, after I came back a day later, I got the undefined method.
Currently, the user is logged in and there is no list. I tried to add a list via rails console but that didn't work.
users_controller.rb
class UsersController < ApplicationController
def show
return redirect_to new_list_path unless current_user
#list = current_user.list
#items = #list.items
end
end
I am directing everything to go the View/Users/Show page with some partials:
users/show.html.erb
<h1><%= #list.title %></h1>
<%= link_to "Delete List", #list, method: :delete %>
<h2 class="media-heading"><%= current_user.name %></h2>
<%= render partial: 'items/form'%>
<%= render partial: 'items/item', collection: #items %>
Partials are here
items/_form.html.erb
<%= form_for [#list, #list.items.new] do |f| %>
<div class="form-group">
<h4>Add an Item:</h4><br/>
</div>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control', placeholder: "Add an Item:" %>
</div>
<div class= "form-group">
<%= f.submit "Create Item", class: 'btn btn-success' %>
</div>
<% end %>
items/_item.html.erb
<small class="media-heading">
<p><%= item.name %></p>
<%# time_ago_in_words(item.created_at) %>
</small>
Here are my other two controllers:
lists_controller.rb
class ListsController < ApplicationController
before_action :authenticate_user!
def new
#list = List.new
end
def create
#list = List.new(list_params)
#list.user = current_user
if #list.save
flash[:notice] = "List was saved."
redirect_to current_user
else
flash[:error] = "There was a problem saving your list."
redirect_to user_path(current_user)
end
end
def destroy
#list = List.find(params[:id])
if #list.destroy
redirect_to new_list_path
else
redirect_to current_user
end
end
def edit
end
private
def list_params
params.require(:list).permit(:title)
end
end
items_controller.rb
class ItemsController < ApplicationController
def show
#items = Item.all
end
def create
#list = List.find(params[:list_id])
#item = Item.new(item_params)
#item.list = #list # after initializiation, before saving
if #item.save
flash[:notice] = "Item was saved."
redirect_to current_user
else
flash[:error] = "There was a problem saving your item."
redirect_to current_user
end
end
private
def item_params
params.require(:item).permit(:name)
end
end
I'm wondering how it's broken when it worked previously.
Models are as follows:
list.rb
class List < ActiveRecord::Base
belongs_to :user
has_many :items
end
user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_one :list
end
item.rb
class Item < ActiveRecord::Base
belongs_to :list
end
In the users_controller.rb, maybe you should define:
def show
#user = User.find(params[:id])
#list = #user.lists
#items = #list.items
end
You need to find the specific user first so it know what lists to show. I realize you are trying to do that with devise helpers, but it isn't really necessary.

Comment functionality in Rails. undefined method `first_name' for nil:NilClass

One thing I could never do properly is implement a comment feature. I'm not leaving my computer until I learn to do it.
The error is thrown on this line:
<strong><%= comment.user.first_name %></strong>
Apparently user is nil; but why? And what do I have to do to get this to work?
A comment should belong to a guide and a user. Users and guides both have many comments.
I started with
rails g scaffold comment body:text guide:references user:references
and then migrated the database. I completed the model associations as well.
Here is my guides controller show action:
def show
#guide = Guide.find(params[:id])
#comment = #guide.comments.build
end
Here is the part of the Guide show view that deals with comments:
<h3>Comments</h3>
<% #guide.comments.each do |comment| %>
<div>
<strong><%= comment.user.first_name %></strong>
<br />
<p><%= comment.body %></p>
</div>
<% end %>
<%= render 'comments/form' %>
Here is the comment form partial:
<%= simple_form_for(#comment) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :body %>
<%= f.association :user %>
<%= f.association :guide %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
User.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, #:recoverable,
:rememberable, :trackable, :validatable
validates :first_name, presence: true
validates :email, presence: true
validates :email, uniqueness: true
validates :runescape_username, presence: true
has_many :guides
has_many :comments
acts_as_voter
def user_score
self.guides.inject(0) { |sum, guide| sum += guide.score }
end
end
Comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :guide
end
Comments controller create action:
def create
#comment = Comment.new(comment_params)
respond_to do |format|
if #comment.save
format.html { redirect_to #comment, notice: 'Comment was successfully created.' }
format.json { render action: 'show', status: :created, location: #comment }
else
format.html { render action: 'new' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
Replace the line
#comment = Comment.new(comment_params)
with
#comment = current_user.comments.build(comment_params)
in your Comments#create action.
You get this error because you don't assign current_user to created Comment. That's why comment.user returns nil.
As stated by AndreDurao, you can also validate user_id presence in Comment model, like this:
class Comment
validates_presence_of :user
# ...
end
for getting rid of that error try this <%= comment.user.try(:first_name) %>

Resources