Comment functionality in Rails. undefined method `first_name' for nil:NilClass - ruby-on-rails

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) %>

Related

Nested attributes in rails 5.0 errored while saving the record

I have User model (for devise ) and then i have member which references User and then portfolio which references member .
i have created a user while signingup .
Now i want the signed up user to update his deatails which is members and portfolio tables .
The portfolio model is :-
class Portfolio < ApplicationRecord
belongs_to :member
validates_presence_of title:
end
class Member < ApplicationRecord
belongs_to :user
has_one :portfolio
accepts_nested_attributes_for :portfolio
end
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_one :member
validates_presence_of :name
validates :email, uniqueness: true
after_create :create_member_portfolio
def create_member_portfolio
puts "Test "
end
end
in my members_controller the code i have is :-
class MembersController < ApplicationController
before_action :set_member, only: [:show, :edit, :update, :destroy]
# GET /members
# GET /members.json
def index
#members = Member.all
end
# GET /members/1
# GET /members/1.json
def show
end
# GET /members/new
def new
#member = setup_member(Member.new)
end
# GET /members/1/edit
def edit
end
# POST /members
# POST /members.json
def create
p = member_params
byebug
#member = Member.new(p)
byebug
#member.user_id = current_user.id unless current_user.nil?
respond_to do |format|
if #member.save
format.html { redirect_to #member, notice: 'Member was successfully created.' }
format.json { render :show, status: :created, location: #member }
else
format.html { render :new }
format.json { render json: #member.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /members/1
# PATCH/PUT /members/1.json
def update
respond_to do |format|
if #member.update(member_params)
format.html { redirect_to #member, notice: 'Member was successfully updated.' }
format.json { render :show, status: :ok, location: #member }
else
format.html { render :edit }
format.json { render json: #member.errors, status: :unprocessable_entity }
end
end
end
# DELETE /members/1
# DELETE /members/1.json
def destroy
#member.destroy
respond_to do |format|
format.html { redirect_to members_url, notice: 'Member was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_member
#member = Member.find(params[:id])
end
def setup_member(member)
member.portfolio ||= Portfolio.new
member
end
# Only allow a list of trusted parameters through.
def member_params
# slice(*filter.keys).each{|k,v| puts "#{k}:#{v}"}
#byebug
params.require(:member).permit(
:dob,
:email,
:phone,
portfolio_attributes: [ :title, :subtitle ]
)
end
end
And the html.erb is as given below :-
<%= form_for(#member) do |f| %>
<% if #member.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(member.errors.count, "error") %> prohibited this member from being saved:</h2>
<ul>
<% #member.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-control">
<%= f.label :dob %>
<%= f.datetime_select :dob %>
</div>
<div class="form-control">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="form-control">
<%= f.label :phone %>
<%= f.text_field :phone %>
</div>
<div class="col-md-12">
<h2>Profile Details :</h2>
<div>
<%= f.fields_for :portfolio do |ff| %>
<div class="form-control">
<%= ff.label :title %>
<%= ff.text_field :title %>
</div>
<div class="form-control">
<%= ff.label :subtitle %>
<%= ff.text_field :subtitle %>
</div>
<% end %>
</div>
</div>
<div class="form-group">
<%= f.submit 'Save Member Item', class: 'btn btn-primary btn-block' %>
</div>
<% end %>
But when in the create controller method on save of member object it fails and says that Portfolio.member does not exist .
Although i an getting the portfolio_attributes as key with title and subtitle in the passed in params and those are validated too , but fails on save . Could you please let me know what did i do wrong here ?
The error i am gettng here adter member.save is called is as shown below :-
byebug) #member.errors
#<ActiveModel::Errors:0x00007fa011bceba8 #base=#<Member id: nil, fname: "asdsa", lname: "asdfsda", dob: "2020-06-16 18:59:00", email: "asddfsda#asdfdsa.com", religion_id: nil, phone: "", created_at: nil, updated_at: nil, user_id: 1>, #messages={:"portfolio.member"=>["must exist"]}, #details={:"portfolio.member"=>[{:error=>:blank}]}>
(byebug)
You might be able to isolate which field is causing the error by checking:
#member = Member.new(p)
#member.valid?
#member.errors
For more information, see the valid? and invalid? section of the Active Record Validations and Callbacks documentation.
It seems there is a bug in version rails 5.0 . and to move away from the default behavior(not allowing to save entry without the parent entry ) we'll have to add
class Portfolio < ApplicationRecord
belongs_to :member, optional: true
end
which should do the trick . To follow check this link :-
https://github.com/rails/rails/issues/18233

Rails update form throwing "undefined method `[]' for nil:NilClass"

I'm trying to create an update form on Rails, for an object that has a foreignkey to another. However, it throws this error. I'm still very greenhorn with Ruby on Rails and have just been following a video tutorial, so I'm not quite sure how to interpret this. I am current using rails 5.0.0
In travelers_controllers.rb, below line
#prf = update_prof_params["profiles_attributes"]["0"]
throws the error
undefined method `[]' for nil:NilClass
edit.html.erb
<div class="col-md-7 col-md-offset-3 main">
<% provide(:title, "Edit user")%>
<center><h1>Update your profile</h1></center>
<%= form_for(#person) do |f| %>
<%= render 'shared/error_messages' %>
<div class="col-md-12">
<%= render 'layouts/profilefields', f: f %>
<%= f.submit "Save Changes", class: "btn btn-large btn-primary" %>
</div>
<% end %>
</div>
_profilefields.html.erb
<%= f.fields_for :profiles do |prf|%>
<!--
<% if !#profileInfo["avatar"].blank? %>
<%= image_tag #contactInfo.avatar_url(:medium).to_s, :class=>"profilePhoto" %>
<% end %>
<div class="photoPreview">
<i class="fa fa-upload photoUpload"></i>
<p id="uploadClick">Click to Upload</p>
</div>
<%= prf.file_field :avatar, accept: 'image/png,image/gif,image/jpeg, image/jpg', id: 'uploadAvatar' %>
<p class="deletePhoto">Delete</p>
-->
<%= prf.label :about %>
<%= prf.text_field :about, :class => "form-control" %>
<%= prf.label :why %>
<%= prf.text_field :why, :class => "form-control" %>
<%= prf.label :goals %>
<%= prf.text_field :goals, :class => "form-control" %>
<%= prf.hidden_field :traveler_id, value: current_traveler.id %>
<% end %>
travelers_controller.rb
class TravelersController < ApplicationController
def edit
#person = Traveler.find(params[:id])
#profileInfo = Profile.find_or_initialize_by(traveler_id: params[:id])
##profileInfo[:email] = current_traveler.email
#This builds the form
#person.build_profile(#profileInfo.attributes)
end
def show
end
def update
#prf = update_prof_params["profiles_attributes"]["0"]
#prof = Profile.find_or_create_by(traveler_id: current_traveler.id)
if #prof.update_attributes(prf)
flash[:success] = "Profile updated"
redirect_to feed_path
else # Failed. Re-render the page as unsucessful
render :edit
end
end
private
def update_prof_params
params.require(:traveler).permit(profiles_attributes: [:about, :why, :goals,
:traveler_id])
end
end
and the models
class Profile < ApplicationRecord
belongs_to :traveler, foreign_key: "traveler_id"
end
class Traveler < ApplicationRecord
# Include default devise modules. Others available are:
# , :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile
end
In TravelersController, the method update should be used for update traveler, not profile, so you can use mass-update via nested attribute like this:
def update
#traveler = Traveler.find(params[:id])
if #traveler.update(update_prof_params)
flash[:success] = "Profile updated"
redirect_to feed_path
else # Failed. Re-render the page as unsucessful
render :edit
end
end
So the above allow you to create/update profile which belongs to traveler. Besides, ensure the nested attribute was defined in your model:
traveler.rb
class Traveler < ActiveRecord::Base
# Your code here
#....
# Make sure define this
accepts_nested_attributes_for :profile
end
Update: The permitted params should be:
def update_prof_params
params.require(:traveler).permit(profile_attributes: [:about, :why, :goals, :traveler_id])
end
As you see profile_attributes should be used instead of profiles_attributes because traveler has one profile only

Rails 4 - how to get user name from their id

I have a order show page shown below. I had this working but ever since I changed computers to work on this app, the name did not display.
If I change <%= #order.user.name %> to <%= #order.user.id %>, I get the correct id. Otherwise I get nothing displaying where as the name of the id should be posted.
#view/orders/show.html.erb
<p>
<strong>Code:</strong>
<%= #order.code %>
</p>
<p>
<strong>Client:</strong>
<%= #order.client.name %>
</p>
<p>
<strong>User:</strong>
<%= #order.user.name %>
</p>
<p>
<strong>Notes:</strong>
<%= #order.memo %>
</p>
<p>
<strong>Status:</strong>
<%= #order.status ? "Confirmed" : "In Progress" %>
</p>
<%= link_to 'Edit', edit_order_path(#order) %> |
<%= link_to 'Back', orders_path %>
#models/order.rb snippet
class Order < ActiveRecord::Base
belongs_to :user
end
#model/user.rb
class User < ActiveRecord::Base
has_many :orders, dependent: :destroy
has_many :addresses, dependent: :destroy
has_many :telephones, dependent: :destroy
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
enum role: [:user, :manager, :admin]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :user
end
end
#controllers/order_controller.rb snippet
def create
#order = Order.new(order_params)
#order.user_id = current_user.id
#order.status = TRUE
respond_to do |format|
if #order.save
format.html { redirect_to #order, notice: 'Order was successfully created.' }
format.json { render :show, status: :created, location: #order }
else
format.html { render :new }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
I think I just answered another question of yours, but I would recommend using the .delegate method like this:
#app/models/order.rb
Class Order < ActiveRecord::Base
belongs_to :user
delegate :name, to: :user, prefix: true #-> #order.user_name
end
You can delegate multiple variables if you need to.
This fixes Law of Dementer, as described here
As mentioned in comments, the value for the user name is empty and that is why it displays a blank when looking for the name.
Adding a name(string) to the user.name will solve the issue
When using a different database, make sure to also update the database with data, preferably the same data which initially tested with.
It's completely independent of the machine you use, so there have to be some changes you've made in your code that you don't remember.
Check what value is returned when you call this user from the console. Type rails c in the terminal and then type a command:
User.find(<<ID_OF_USER_WHOES_NAME_IS_NOT_DISPLAYED>>).name
If its name is empty, it could be a problem with creating a user or something.
Make update when you'll check it.

Undefined method, avatar and name for nil:NilClass

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

RoR : Nested forms with devise

I am new to RoR and I thought I could ask you some help. I didn't find the specific answer I am looking for.
I have a problem with a modelisation I want to make using Devise. Devise sets up a Member model, and I want to have a SuperMember model which have more attributes than Member, and some different views.
I want to set up a nested form to create a SuperMember, while automatically creating the Member account in the background.
Member.rb (generated by devise)
class Member < ActiveRecord::Base
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me
end
SuperMember.rb
class Supermember < ActiveRecord::Base
attr_accessible :first_name, :last_name
belongs_to :member, :dependent => :destroy
accepts_nested_attributes_for :member
end
Supermembers.controllers.rb
def new
#supermember = Supermember.new
#supermember.member = Supermember.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #supermember }
end
end
def create
#supermember = Supermember.new(params[:supermember])
respond_to do |format|
if #supermember.save
format.html { redirect_to #supermember, notice: 'Supermember was successfully created.' }
format.json { render json: #supermember, status: :created, location: #supermember }
else
format.html { render action: "new" }
format.json { render json: #supermember.errors, status: :unprocessable_entity }
end
end
I tried to create a nested form in order to generate both the member and the supermember :
<%= simple_form_for(#supermember) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :first_name %>
<%= f.input :last_name %>
</div>
<% # Devise member %>
<%= f.fields_for :member do |m| %>
<div class="form-inputs">
<%= m.input :email, :required => true, :autofocus => true %>
<%= m.input :password, :required => true %>
<%= m.input :password_confirmation, :required => true %>
</div>
<% end %>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
The problem is that when I submit this form, I get the following error message :
Can't mass-assign protected attributes: member_attributes
Application Trace | Framework Trace | Full Trace
app/controllers/supermembers.rb:44:in `new'
app/controllers/supermembers.rb:44:in `create'
I really don't understand how to fix it. Could you help me on this one?
Thank you very much!
You need to allow Supermember to accept mass assignment of the member attributes
class Supermember < ActiveRecord::Base
attr_accessible :first_name, :last_name, :member_attributes
...
end
If what you're trying to do is add attributes to member, then it is perfectly OK to do so. There's no need to create a supermember for that purpose only (of course, if you have some other agenda then go ahead...).
Device doesn't care if you add attributes to the model, even if it was generated by it.

Resources