What is the difference between find and find_by? - ruby-on-rails

I know from the rails documentation that find will result in a RecordNotFound error. However, the find_by method simply returns nil if a record is not found. Returning nil seems more intuitive to me and safe in general, so I am wondering if there is any benefit in using the find method over the find_by method?
For example, what is the difference between the following:
myRecord.find(1)
and
myRecord.find_by(id: 1)
If the only difference is that find raises an error when a record isn't found, I don't really see the benefit in using find.
EDIT
For all the people that jumped on my question and said that it had been already answered, you are wrong. I clearly stated that I knew find returns an error when a record is not found (which is what everyone else emphasizes in their answers) and that find_by returns nil. I want to know if there are any other differences.

In your specific example there is little difference between the two of them other than the error vs nil which you mention. Whether you want to handle an error or nil is totally up to you.
For a great explanation of understanding when an error is preferable to nil and vice versa read this.
If you are searching by an attribute other than id using find will not work as it can only access elements by their id.

Related

Better way than if foo.blank?

I am building a basic app that will retrieve a customers data and show it on screen, however some of the fields will be null and so I get a lot of undefined method 'registration_number' for nil:NilClass errors
Is there a better way to to deal with these other than lots of if statements
if #customer.registration_number.blank?
Do something
else
#customer.registration_number
end
Thanks in advance
If you want to actually do something on missing values, you can't avoid a conditional per value. (Especially if that "something" differs from value to value).
If you want to simply ignore missing values, you can use one of the nil-swallowing tricks.
#customer.try(:registration_number)
#customer&.registration_number
#customer.registration_number rescue nil # don't use this one, there are better ways
You can do a try there
#customer.try(:registration_number)
and if there is no value, it will just return nil

.where returns nil exception if user does not enter anything rails 4

I want to query some data from table based on user for submission.
Its working fine but if I post nothing in the the fields and post my form, it returns me nil exception.
Is there a way we can deal with nil exception, or do I need to change query?
question_options = question.question_options.where(id: self.option_id).first
The simplest solution is to use try. For example:
question_options = question.question_options.where(id: self.option_id).try(:first)
The documentation for try is here
For your issue multiple solutions exists. Your query returns nil and thats ok because nothing was found. The simplest solution is an if statement. if question_options.nil? do some thing else. Or you can use an unitialized object (or a NullObject) do work with it question_option = question.question_options.where(id: self.option_id).first || QuestionOption.new
The are other possibilites too. It depends on your requirement what way you choose.

How to DRY up a ruby conditional structure needed for Rails

I'm finding I often have to use a structure to avoid a Rails error of undefined method 'name' for nil:NilClass.
The structure looks like this:
if country.state
country.state.name
end
It seems like a classic case of repeating oneself with country.state appearing twice in one simple block. Is there any way to DRY this up?
Rails adds a try method to object that mimics object#send but does not raise an exception if the object returns nil.
I think the syntax is
country.try(:state).name
Well not really. One option is to install the andand gem, but introducing a dependency for this may be a little much.
Other than using the slightly more concise syntax of:
country.state.name unless country.state.nil?
I don't think there's a DRY way to do this with the information given. I would argue that if you can't be sure whether country.state is nil or not, you may want to look at the code responsible for setting that value and determine whether that's a normal case or whether a validator upstream should be catching that.

NilClass definitions

I am tired of trapping for nil when looking for a dependent record when most of the time a return of 0 will do nicely. What sort of trouble am I creating for myself by adding "id" to the NilClass thus
class NilClass
def id
0
end
end
What might the unintended consequences be? I know about exists?(), but somehow thought this might be cleaner.
Your thoughts?
If you really have a problem with this, you should use referential integrity inside your database.
If you must call methods on nil which may or may not exist or throw some kind of error, you should use either a check a la
> nil.id if nil
=> nil
or Object#try (which is part of ActiveSupport nowadays I believe?), be warned - I reckon it's kind of a code smell.
> nil.try(:id)
=> nil
That being said, it is less of a smell than modifying NilClass to do something unexpected, think of what a new developer who had to work on your project would expect.
Won't this mean you will need to check for "id == 0" to confirm existence?
Not to mention unintended consequences of overiding base Ruby functionality - it becomes really hard to predict behaviour of other libraries and core Rails APIs when you mess with language internals. Not saying it won't just work, but it's hard to be sure.
I would leave the default - it works quite nicely as Ruby allows "if object.association" expressions.

can't dup NilClass when using Sanitize gem

Alright this probably is the worst error I have found ever.
I have two projects, both using same code:
Sanitize.clean(string, Sanitize::Config::BASIC)
but one works and another fails.
Problem is similar to this poor guy's post: https://stackoverflow.com/questions/2724342/cant-dup-nilclass-how-to-trace-to-offender
Could anybody help please?
This will happen if you pass nil into clean() instead of a string. Make sure your string variable is really a string.

Resources