undefined method `empty?' for nil:NilClass how to avoid it - ruby-on-rails

Hi Together I've got this code:
#coursesFound = #user.available_courses
#courses = []
for course in #coursesFound do
#courseInGroups = course.user_groups
for group in #courseInGroups do
#group = UserGroup.find group.id
if #group.users.map { |u| u.id }.include? #user.id
#courses << course
break
end
end
end
# Wenn ein Kurs keiner Gruppe hinzugefügt wurde
if #courseInGroups.empty?
#courses << course
end
on my debian vm it works fine but on my live system I got this error:
undefined method `empty?' for nil:NilClass
How can I avoid this?

If this #coursesFound = #user.available_courses returns an empty activerecord relation.
Then this won't execute
for course in #coursesFound do
#courseInGroups = course.user_groups
for group in #courseInGroups do
#group = UserGroup.find group.id
if #group.users.map { |u| u.id }.include? #user.id
#courses << course
break
end
end
end
Which means when you get here #courseInGroups is nil
if #courseInGroups.empty?
#courses << course
end
So your quick fix would be
if #courseInGroups && #courseInGroups.empty?
#courses << course
end

You can use the try method to Avoid this error:
#courseInGroups.try(:empty?)
This won't throw an error if #courseInGroups was nil.

And don't forget blank? when using rails. Here you find a good overview of all methods with or without rails.
I did not analyze your code, it's just for you, me and others that do not use this methods often, mix them up and then come here - just to remember: empty? is not blank?.

You need to properly initialize your object as well.
#courseInGroups = course.user_groups || []
You won't get nil:NilClass error any more if you initialize properly.
To get rid of nil:NilClass error you can use other answer. like try etc.

You can put the ? before the dot of the empty:
if #courseInGroups?.empty

Related

NoMethodError (undefined method `each' for nil:NilClass):

class Api::SurveyAnswersController < ApplicationController
def create
# #survey_answer = SurveyAnswer.new(survey_answer_params)
survey_answers = []
survey_id = params[:survey_id]
params[:questions].each do |q|
answer = {survey_id: survey_id, option_ids: [], question_id: q[:id],
title: q[:answer]}
if q[:options].present?
selected_options = q[:answer].split(',')
selected_options.each do |selected_option|
q[:options].each do |option|
if option[:title]== selected_option
answer[:option_ids] << option[:id]
#<todo add break when in this condition
end
end
end
survey_answers << answer
end
end
puts survey_answers
# #survey_answers = SurveyAnswer.create(survey_answers)
if SurveyAnswer.create(survey_answers)
render json: survey_answers
end
end
end
I have a survey model which has some questions. Each question contains answers. When I try to hit post request through postman to insert answers, it gives 505 internal server error with message "undefined method each for nil:nilclass". Can anybody tell what the problem is?
You are trying to run the .each loop an empty object.
Make sure that both
params[:questions]
and
q[:options]
are not empty (not equal to nil).
NoMethodError sometimes sounds very unrepresentative, especially if you're just starting off with Ruby.
Try to browse Stackoverflow next time, because this has been answered here.

Rails 5 ActiveRecord - check if any results before using methods

In my Rails 5 + Postgres app I make a query like this:
user = User.where("name = ?", name).first.email
So this gives me the email of the first user with the name.
But if no user with this names exists I get an error:
NoMethodError (undefined method `email' for nil:NilClass)
How can I check if I have any results before using the method?
I can think if various ways to do this using if-clauses:
user = User.where("name = ?", name).first
if user
user_email = user.email
end
But this does not seem to be the most elegant way and I am sure Rails has a better way.
You can use find_by, returns the object or nil if nothing is found.
user = User.find_by(name: name)
if user
...
end
That being said you could have still used the where clause if you're expecting more than one element.
users = User.where(name: name)
if users.any?
user = users.first
...
end
Then there is yet another way as of Ruby 2.3 where you can do
User.where(name: name).first&.name
The & can be used if you're not sure if the object is nil or not, in this instance the whole statement would return nil if no user is found.
I use try a lot to handle just this situation.
user = User.where("name = ?", name).first.try(:email)
It will return the email, or if the collection is empty (and first is nil) it will return nil without raising an error.
The catch is it'll also not fail if the record was found but no method or attribute exists, so you're less likely to catch a typo, but hopefully your tests would cover that.
user = User.where("name = ?", name).first.try(:emial)
This is not a problem if you use the Ruby 2.3 &. feature because it only works with nil object...
user = User.where("name = ?", name).first&.emial
# this will raise an error if the record is found but there's no emial attrib.
You can always use User.where("name = ?", name).first&.email, but I disagree that
user = User.where("name = ?", name).first
if user
user_email = user.email
end
is particularly inelegant. You can clean it up with something like
def my_method
if user
# do something with user.email
end
end
private
def user
#user ||= User.where("name = ?", name).first
# #user ||= User.find_by("name = ?", name) # can also be used here, and it preferred.
end
Unless you really think you're only going to use the user record once, you should prefer being explicit with whatever logic you're using.

Error while iterating through params

I get an error when I try to iterate through params
When running code below:
def create_score
#quiz = Test.find_by(password: session[:test_password])
#points = 0
#quiz.tasks.each_with_index do |task, index|
#task = Task.find_by(id: task)
#points += #task.score if #task.correct_answers.to_s == send("params[:test][:task#{index}]")
end
#score = Score.new(user_id: 2, name: "Test1", points: #points)
if #score.save
redirect_to root_url
else
redirect_to signup_path
end
end
I get:
undefined method `params[:test][:task0]' ...
at the
#points += #task.score if #task.correct_answers.to_s == send("params[:test][:task#{index}]")
Which means that it has problem with send method
Parameters look like this:
{"utf8"=>"✓",
"authenticity_token"=>"8h7rtv2yWio11DFo6kBKutdZl7RDBBaTrt7e8qel8fR5R5XsoXRhRrBeDQPPoZeuBlZ7N5PmqCxik06Z/gQLZQ==",
"test"=>{"task0"=>["4"], "task1"=>["0"], "task2"=>["10"]},
"commit"=>"Zakończ test",
"locale"=>"pl"}
Which means that there is params[:test][:task0], but still for some reason it fires an error, but I don't really know why. Any ideas why this happens?
You want to index with dynamic key, not call a method dynamically. Aka:
params[:test]["task#{index}"]
Should do. Note that params are have indifferent access for strings and symbols.
To give you more food for thought, here is how you might have done the same with #send:
params[:test].send(:[], "task#{index}")
And here is how to define a method that would have the name you are trying to call:
define_method("params[:test][:task#{index}]") do
puts 'WTF'
end
You're calling your params with a symbol but instead you should use a string.
This means you should use one of the following approaches:
params["test"]["task0"]
or
params[:test.to_s][:task0.to_s]
Hope that helped :)
You should use params object something likeparams[:test][:task]) instead of send("params[:test][:task#{index}]".

Is there a more ruby way of doing this

Ok so i have this helper
def current_company_title
(Company.find_by_id(params["company_id"]).name rescue nil) || (#companies.first.name rescue nil) current_user.company.name
end
Basically what I am achieving with this is the following ...
If the param["company_id"] exists then try to get the company and if not then
if #companies exists grab the first company name and if not then get the current users company name
This works but the rescues seem like a hack...any idea on another way to achieve this
Indeed rescue is kind of a hack, id' probably split it up into two methods and then use try to fetch the name if available: http://api.rubyonrails.org/classes/Object.html#method-i-try
def current_company
#current_company ||= Company.find_by_id(params[:company_id]) || #companies.try(:first) || current_user.try(:company)
end
def current_company_name
current_company.try(:name)
end
Company.find_by_id(params["company_id"]).name`
find and its derivates are meant to be used when you're sure-ish you'll have a positive result, and only in some cases (row was deleted, etc) errors. That's why it raises an exception. In your case, you're assuming it's gonna fail, so a regular where, which would return nil if no rows was found, would do better, and remove the first rescue
#companies.first.name rescue nil
could be replaced by
#companies.first.try(:name)
I'll let you check the api for more on the topic of try. It's not regular ruby, it's a Rails addition.
Less "magic", simple code, simple to read:
def current_company_title
company = Company.where(id: params["company_id"]).presence
company ||= #companies.try(:first)
company ||= current_user.company
company.name
end
Ps. Not a big fan of Rails' try method, but it solves the problem.
def current_company_title
if params["company_id"]
return Company.find_by_id(params["company_id"]).name
elsif #companies
return #companies.first.name
else
return current_user.company.name
end
end
The rescues are a hack, and will obscure other errors if they occur.
Try this:
(Company.find_by_id(params["company_id"].name if Company.exists?(params["company_id"]) ||
(#companies.first.name if #companies && #companies.first) ||
current_user.company.name
then you can extract each of the bracketed conditions to their own methods to make it more readable, and easier to tweak the conditions:
company_name_from_id(params["company_id"]) || name_from_first_in_collection(#companies) || current_user_company_name
def company_name_from_id(company_id)
company=Company.find_by_id(company_id)
company.name if company
end
def name_from_first_in_collection(companies)
companies.first.name if companies && companies.first
end
def current_user_company_name
current_user.company.name if current_user.company
end
[Company.find_by_id(params["company_id"]),
#companies.to_a.first,
current_user.company
].compact.first.name

Why is my update failing in a each loop?

users = User.all()
user.each do |u|
b = get_id_blah()
u.some_id = b.id
u.save
end
I get the error:
ruby-1.8.7-p302#rails3/gems/activemodel-3.0.1/lib/active_model/attribute_methods.rb:364:in `method_missing': private method `update' called for #<User:0x1017b8188> (NoMethodError)
Should I be calling save outside of the loop?
This might just be a typo, but it could explain your no-method-error
users = User.all()
user**s**.each do |u|
end
It should be something like
users = User.all
users.each do |user|
....
end
Or simply
User.all.each do |user|
...
end
Are you sure you this is the exact code? It sounds like you are calling object.update instead of just calling object.save
By the way, you don't need parentheses if you're calling functions in Ruby :-)

Resources