I has a model "modela" that has a has_many_through relationship with model "submodelb". In a controller I want to check if modela has any submodelb associated with it. I have tried the two code examples below; however, the both throw the error "undefined method `submodelbs'" if modela does not have any submodelbs. Please help me see what I am doing wrong.
Sample 1:
if !#modela.submodelbs.nil?
#submodelbs = #modela.submodelbs
else
#submodelbs = []
end
Sample 2:
if !#modela.submodelbs.empty?
#submodelbs = #modela.submodelbs
else
#submodelbs = []
end
You can use .present? which is the opposite of blank?
#submodelbs = #modela.submodelbs.present? ? #modela.submodelbs : []
But I think your problem is that #modela may be nil or you may have not defined associations correctly in the model.
The reader method produced by has_many_through always returns something that looks like an Array, so it should never return nil. So, can't you just return #modela.submodelbs always?
I use blank?
unless #modela.submodelbs.blank?
#modela has submodelbs
end
but error messege suggests that you may have something wrong with association definition...
Also what you are trying to achieve can be done with one-liner
#modela.submodelbs ||= []
if submodelbs are nil empty array will be assigned.
Why not just put your checks in a begin...rescue...end block?
Related
I am probably misunderstanding the concept of method and I want your help if you are willing to.
I am facing the problem that this method below returns String perfectly, but when I try to use it in a method in a different class, it returns NilClass.
class FruitConverter
def fruit_converter(fruit)
return if fruit.nil?
find_favorite_fruit = list.find{|k,v| k == fruit}
find_favorite_fruit ← when I print it it returns "りんご" if fruit == "apple"
end
def list
{
"りんご" : "とっても美味しいりんご",
"オレンジ" : "とっても美味しいオレンジ"
}
end
end
↑this works fine here but when I use it in a method in a different class, the result turned to be NilClass like this.
def fruit
fav_fruit = #target_person.fruit
fruit = FruitConverter.new.fruit_converter(fruit)
fruit ←when print fruit.class it returns nilClass.
end
I have a bunch of almost same instance methods and those could work. One of them is like this.
class SizeConverter
def size_converter(size)
return if size.nil?
find_size = list.find{|k,v| k == size.to_sym}
find_size
end
def list
{
small : "Ohkii",
medium : "chukurai"
}
end
end
this works fine. so maybe the error of the first code is because of its result.
but I don't know how to fix it.
I would love you to tell me any clues.
Thank you.
I am sorry. It seems that there is no problem in the beginning.
the only problem was that I gave a fruit that is not existing in the list to fruit_converter. That is why it returns nil. But when I gave true fruit it worked. Thank you all anyways.
Say I have a User object, which has an email property, and I need the upper cased last letter of their email:
u = User.find(1)
letter = u.email.upcase.last
If u or email is nil in this chain, then I get a NoMethodError: undefined method 'blah' for nil:Nilclass. I should be able to work around it in most cases, but sometimes, a nil gets where it shouldn't or its hard to contain. One way would be verbose:
u = User.find(1)
letter = nil
if u && u.email
letter = u.email.upcase.last
end
But this gets annoying and hazardous in a view, or in a long chain of a.bunch.of.properties.down.a.hierarchy. I read about try method in Rails:
u = User.find(1)
letter = u.try(:email).try(:upcase).try(:last)
This is less verbose, but I feel icky writing all those tries. And once I put try in the chain, I have to use them all the way down. Is there a better way?
I like to use the Null Object Pattern. Avdi has a great post explaining this, but the basic idea is you have a little class that can stand in for an object and respond reasonably to the messages you might pass the original object. I've found these are useful not only for avoiding NoMethodErrors but also for setting default values/nice messages.
For instance, you could do:
class NilUser
def email
"(no email address)"
end
end
u = User.find(1) || NilUser.new
u.email.upcase.last # => No errors!
I just wanted to update this thread with one more option: Ruby now (as of 2.3) gives us a safe navigation operator, the &. syntax.
So:
u.email.upcase
Would become:
u.email&.upcase
Similarly to Rail's try method, the whole chain will return nil if it encounters NoMethodError on a nil.
User.find(1)
Will raise exception if user with id 1 not exist so you don't need to worry about nil here
u.email
If you have in your model
validates :email, presence: true
You don't need to worry about nil because User without email cant be in database
But I think you are asking about general way of handling nils in ruby code. Lately I'm using Null Object pattern
http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/
http://robots.thoughtbot.com/post/20907555103/rails-refactoring-example-introduce-null-object
Rails: replacing try with the Null Object Pattern
https://github.com/martinciu/nullobject
You could also map the result of find
[User.find(1)].map{|u| (u != nil ? u.mail : "no mail")}[0]
I want to check the existence of any HABTM relationships in an array and return true if any exist.
At present, the only way I can see to do this is:
result = false
[1,2,3,4].each do |i|
if user.parents.exists?(i)
result = true
break
end
end
I tried passing in an array as follows, but I get an exception
result = true if user.parents.exists?([1,2,3,4])
NoMethodError: undefined method `include?` for 1:Fixnum
Is there a better way of doing this?
[1,2,3,4].inject(false) {|res, i| res ||= user.parents.exists?(i)}
Pretty much the same logic, just more ruby-ish code using inject syntax.
UPDATE:
Haven't tested it. but this might also work:
user.parents.exists?(:id => [1,2,3,4])
In my rails3.1 application, I'm trying to apply the following logic in one of my order model.
def digital?
line_items.map { |line_item| return false unless line_item.variant_id = '102586070' }
end
I've created a separate variant called prepaid_voucher which has id = 102586070. Despite this, the result is false...
Order has many line_items
LineItem belongs to order and variant
Variant has many line_items
Is this the best way to perform such a task and how can I fix?
First of all I think you want a double == here line_item.variant_id = '102586070', then I rather go for something like that (If I understand what you want)
def digital?
line_items.select{|line_item| line_item.variant_id == '102586070'}.any?
end
But it's hard to understand what you really want, what is the expected behavior if the id is not found?
def refresh_menu
#menu_pages = []
$menu_items.each do |id|
#menu_pages[id - 1] = Page.find(id)
end
end
$menu_items is just an array [1,2]. Obviously what I want to do is populate #menu_pages with all the pages found as per $menu_items.
Mind you,
#menu_pages = Page.all
works just fine. So how come I can't add them one-by-one with Page.find(id)?
The error returned:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each
Why not just do?...
def refresh_menu
#menu_pages = Page.where(:id => $menu_items)
end
And in relation to the error, where is $menu_items defined?
nowk, thanks for the neat trick.
To those who might find this through search: don't forget to restart the server when you make changes to initializers.
Where are you defining $menu_items? From your error message, it looks like the refresh_menu method can't see it - and thus thinks it's nil.