I've written a mailer that wroks prooperly. But i'm having problems with getting the action that invokes the mailer to work.
When a comment is created, within the comment body, for every mention of #someusername, if that user exits, they are sent an email to their email address.
Here is what I have:
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
#comment.user = current_user
if #comment.save
flash[:notice] = "Successfully created comment."
redirect_to #commentable
end
end
else
render :action => 'new'
end
This is the error I keep getting:
undefined method `email' for nil:NilClass
end
Here is where the error is coming from(inside the mailer):
#user = user
#url = 'http://domain.com'
mail(to: #user.email)
end
end
email is being called on a nil #user, and #user is going to always be nil.
Looking at this line:
unless #user_check = nil
Using a single equals is assignment, not an equality comparison.
You'll want to do:
unless #user_check == nil
or the more idomatic Ruby:
unless #user_check.nil?
Taking unless #user_check = nil statement for inspection:--
= is assignment and == is equality comparison operator. I guess you just missed using == because of typo.
Use ruby .nil? method instead.
like unless #user_check.nil?
Use delayed_job to send emails.
Related
I am using the same commands that I previously used in successful projects but now I suddenly can't validate any updates to the object(in this case, User). Everything else works fine but any attempt to check my validations for an update results in this error-
'undefined method `valid?' for # '
It is finding successfully finding the user and if I skip the .valid? statement then it will update, just without checking any of my model validations. I recently switched from SQLite to PostgreSQL, I am not sure if that's giving me the problem. I am new to Ruby but I couldn't find anything on this specific problem.
Please let me know if I should include the entirety of my controller or any of my model but as my create works fine, I feel like all the relative code is just in this little section-
class UsersController < ApplicationController
def update
#user = User.find(params[:id])
puts "#Is this working???!! #{#user}" ///prints #Is this working???!! #<User:0x00000001f24468>
#user = User.update(user_params)
if #user.valid?
redirect_to "/users/#{#user.id}"
else
flash[:errors] = #user.errors.full_messages
redirect_to "/users/#{#user.id}/edit"
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password)
end
Your problem is here:
#user = User.update(user_params)
If you put in your check after, you would see: #Is this working???!! true, which would ironically enough inform you that it's not working.
That's because User.update(user_params) returns true or false depending on whether it is successful or not. This means your #user object is now simply either true or false, which you can't call valid on.
If you want to handle successfully updating / failing to do so, try:
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to "/users/#{#user.id}"
else
flash[:errors] = #user.errors.full_messages
redirect_to "/users/#{#user.id}/edit"
end
end
ActiveRecord update(id, attributes)
Updates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.
Then, you can do this checking if #user.update(user_params)
def update
#user = User.find(params[:id]) //Find user
if #user.update(user_params) // Update user if validations pass
redirect_to "/users/#{#user.id}"
else
flash[:errors] = #user.errors.full_messages
redirect_to "/users/#{#user.id}/edit"
end
end
Or, you can call the update method directly in your model class, but the first argument must be the user ID
User.update(params[:id], user_params)
Thank you both for your quick answers. I was replying to tell you that I already tried that and it worked but did not validate. But as two of you told me the same thing, I decided to test it out again and the validations did indeed work this time so thank you (although I definitely have a user with an email of 'asdf' from last time).
Intestering enough, I found another answer although I have no idea why it worked. I added another puts statement after the update and realized my object had been converted to an array so I came up with this other (worse) alternative answer-
def update
#user = User.find(params[:id])
puts "#Is this working???!! #{#user}"
#user = User.update(user_params)
puts "#Is this working???!! #{#user}" ///prints #Is this working???!! [#<User id: 2, name: "James Dean", etc..>]
if #user[0].valid?
redirect_to "/users/#{#user[0].id}"
else
flash[:errors] = #user[0].errors.full_messages
redirect_to "/users/#{#user[0].id}/edit"
end
end
Trying to learn ruby on rails following Michael Hartl's tutorial, when I try to go to the registration page in the book I get this:
NoMethodError in UserController#register
undefined method `save' for nil:NilClass
here is the code from the user_controller.rb file:
class UserController < ApplicationController
def index
#title = "RailsSpace User Hub"
end
def register
#title = "Register"
if request.post? and params[:user]
#user = User.new(user_params)
end
if #user.save
flash[:notice] = "User #{#user.screen_name} created!"
redirect_to :action => "index"
end
end
private
def user_params
# Add the user attributes that you sent with post (form) to the permit
method.
params.require(:user).permit(:name, :screen_name)
end
end
It's complaining about line 11 where it says: if #user.save I'm just following the tutorial I don't know what's going on.
Try to understand what the error message is saying. You're trying to call .save on #user but where you are calling it, #user may not be defined. The problem is you can't call .save on a nil object so it throws NoMethodError.
You're logic is incorrect so you must first make sure #user is instantiating a new User instance. It should be more like this:
def register
#title = "Register"
if request.post? && params[:user]
#user = User.new(user_params)
if #user.save
flash[:notice] = "User #{#user.screen_name} created!"
redirect_to :action => "index"
else
# handle erorrs here
flash[:alert] = "Please fix errors: #{#user.errors.full_messages.inspect}"
redirect_to :back #this may need to change depending, just an example.
end
end
end
NoMethodError in UserController#register undefined method 'save' for nil:NilClass
The error is thrown because you are calling save on a nil; and it is because nil object does not have this method. the save is a instance method belonging to ActiveRecord::Base class.
The reason #user is nil is because #user is an instance variable in the UserController class which you have not given it any value. Any variable starting with # inside a class is an instance variable in Ruby.
To solve this problem, you should set a value to #user, and in this case it should be a User instance. You can either create a new user object by doing #user = User.new(user_params) or you fetch a record from the Database by doing #user = User.find(<some_id>)
On the update controller action, rails returns following error,
undefined method `user' for nil:NilClass
Issue is supposed to be in following,
def update
#profile = Profile.find_by(user_id: params[:id])
if #profile.user == current_user
#profile.update(profile_params)
flash[:info] = "Profile successfully updated"
else
It doesn't make sense why this is an issue, since I used the exact same code to find profiles in my other actions and they worked perfectly.
Profile.find_by is returning nil ie: no user was found.
So, when you try #profile.user, you are trying to access user on a nil.
Make sure the profile exists with the user_id matching params[:id] and that params[:id] isn't blank or nil.
A way to get rid of the fatal error would be to check if #profile is nil or not before using it:
def update
#profile = Profile.find_by(user_id: params[:id])
if #profile.present?
if #profile.user == current_user
#profile.update(profile_params)
flash[:info] = "Profile successfully updated"
else
end
else
# Profile not found
end
end
Or, you could use the bang variation of find_by:
Profile.find_by!
Which will throw an exception if nothing is found (which you can then rescue from)
Why am I getting undefined method `read' for nil:NilClass when I am trying to email an uploaded attachment?
listings_controller.rb
def send_resume_email
#listing = Listing.find(params[:id])
#user = User.find_by_id(params[:id])
UserMailer.new_resume(params[:resume].read(), params[:resume].original_filename, #user, #listing).deliver
redirect_to findjobs_path, notice: 'Message sent'
end
user_mailer.rb
def new_resume(user, listing, file, filename)
#listing = listing
#user = user
attachments[filename] = file
#url = 'http://www.new.com'
mail(to: listing.user.email, subject: 'Thanks for the awesome site')
end
I really appreciate your help.
There are several issues with your code.
First, you are trying to find two resources using the same id param. The params[:id] should belong to either the user or the listing. Which one is it?
On another note, User.find_by_id(params[:id]) is redundant. User.find(params[:id]) should suffice, since the default lookup value for find is id.
#listing = Listing.find(params[:id])
#user = User.find_by_id(params[:id])
Finally, you are calling the method read() on a params hash.
UserMailer.new_resume(params[:resume].read(), params[:resume].original_filename, #user, #listing).deliver
Where is the hash coming from? And how does params[:resume] have an instantiated object that you can call read() on? Usually param hashes are a result of form submissions or a browser request, so it is unlikely to contain an instantiated object that you can call methods on.
And this is the source of your error. The error tells you that params[:resume] is nil. Therefore calling any method on it will raise an error.
The error is just what it says:
params[:resume].nil?
would evaluate to true, and of course, nil:NilClass has no method read.
You can check that your parameter is being populated by
unless params[:resume].blank?
r = params[:resume].read
end
The action bellow creates a new comment.
A user has many statuses
A status has many comments
How can optimize this action so that head 401 and return is not repeated many times.
def create
#user = User.where(id: params[:user_id]).first
if #user
if current_user.friend_with?(#user) or current_user == #user
#status = #user.statuses.where(id: params[:status_id]).first
if #status
#comment = #status.comments.build(params[:comment])
#comment.owner = current_user
if #comment.valid?
#comment.save
current_user.create_activity(:comment_status, #comment, #user)
else
head 401 and return
end
else
head 401 and return
end
else
head 401 and return
end
else
head 401 and return
end
end
Thank you.
When do you want to return 401?
when a user has not been found
when a user is not a current user or is not a friend of that user
when a status has not been found
when new comment has not been successfully created
Instead of using so many conditionals, you can use methods that raise exceptions. When you do so, you can rescue from that exceptions with the desired behavior (rendering 401).
So my suggestions for listed conditions are:
use find! instead of where and then first.
raise something, preferably custom exception (NotAFriendError)
same as 1., use find!
use create!, it's an equivalent to new and then save! which will raise ActiveRecord::RecordInvalid exception if it fails on validation.
Here's the result:
def create
begin
#user = User.find!(params[:user_id])
raise unless current_user.friend_with?(#user) || current_user == #user
#status = #user.statuses.find!(params[:status_id])
#comment = #status.comments.
create!(params[:comment].merge(:owner => current_user))
rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordInvalid
head 401
end
# everything went well, no exceptions were raised
current_user.create_activity(:comment_status, #comment, #user)
end
You have a lot of excessive checking and branching in your code, so it can be simplified to this:
def create
success = false
#user = User.find(params[:user_id])
current_user_is_friend = current_user.friend_with?(#user) || current_user == #user
if #user && current_user_is_friend && #status = #user.statuses.find(params[:status_id])
#comment = #status.comments.build(params[:comment])
#comment.owner = current_user
if #comment.save
current_user.create_activity(:comment_status, #comment, #user)
success = true
end
end
render(status: 401, content: '') unless success
end
A few things I did:
Combine a lot of the if conditions, since there was no need for them to be separate.
Change where(id: ...).first to find(...) since they're the same. Note that, if the find fails, it will give a 404. This may make more sense, though (I think it does)
Don't call #comment.valid? right before #comment.save, since save returns false if the object wasn't valid.
Use || instead of or for boolean logic (they're not the same).
Use render(status: ..., content: '') instead of head ... and return.
Use a boolean variable to track the success of the method.
I would advise that you try and pull some of this logic out into models. For example, User#friend_with should probably just return true if it's passed the same User.
def create
#user = User.where(id: params[:user_id]).first
if #user
if current_user.friend_with?(#user) or current_user == #user
#status = #user.statuses.where(id: params[:status_id]).first
if #status
#comment = #status.comments.build(params[:comment])
#comment.owner = current_user
if #comment.valid?
#comment.save
current_user.create_activity(:comment_status, #comment, #user)
everythingOK = true
end
end
end
end
head 401 and return unless everythingOK
end