Why is the .try method terminating my program? - ruby-on-rails

I am capturing a string date from params that should be in the form of YYYY-DD-MM. If the parameter is an invalid date, I want nil to be assigned to minimum. Like if the param was 2013-40-50 for example. I am able to do so successfully with :
minimum = Date.try(:parse, (params[:newer_than])) if params[:newer_than].present?
If the date is of the correct format, it is assigned to minimum, otherwise nil is. However, in the case where it is nil, the program terminates and I get ArgumentError - invalid date.
The whole idea of me using .try is that if it in fact fails, it's okay, move along. I am handling the case if it were nil afterward. The error is thrown on the line assigning to minimum.
How can achieve that?

It's the way you're using the call.
The try call is not like a begin/rescue. It just tries to call the function if it exists on that object. If the function does not exist, no biggie, it won't call the function and just proceed. Otherwise, it calls it and works as it normally would.
In this case, calling try does you no good, as you know for sure that function exists for date. What's happening is the parse function is throwing an error.
What you should do instead is go ahead and call Date.parse within a begin/rescue block, and catch those particular errors (ArgumentError) that could be thrown by the parse function. You could also pre-validate the string (using regex, for example) to ensure it's in the proper format before calling parse.
Either way, parse does not do any validation for you, so either you can check it before hand or begin/rescue the errors it throws.

You can use rescue for your goal:
minimum = begin
Date.parse(params[:newer_than])
rescue ArgumentError, TypeError
nil
end
According to docs, try just like the regular Ruby Object#send if methos defined on object, so it does not prevent errors like ArgumentError and TypeError.

Related

Why isn't there a NilReferenceError in ruby?

Why is NoMethodError not differentiated for nil in Ruby?
Calling a method on nil is an extremely common error and is usually caused by incorrect data being provided to the program. A NoMethodError on any other class usually implies an error in the code itself (e.g. why were you calling reconnect on a Document? There is likely an error in the code).
What problems are created if I add the following code to my project?
NilReferenceError = Class.new(NoMethodError)
class NilClass
def method_missing(symbol, *args)
raise NilReferenceError, "undefined method `#{symbol}' for nil:NilClass", caller
end
end
I want to do this because when I am triaging exceptions, a NilReferenceError is likely to be caused by bad data and the root cause is likely in another location (validation of input, for example). By contrast, a NoMethodError is likely to be a programming error rooted exactly at the line of the exception (easier to fix and also highly likely to happen 100% of the time).
What are the negative effects of adding code like that to my project?
I think this is just habits from other programming languages. In ruby, nil is a first class object, like an integer, a hash or your own class object.
After you see the "NoMethodError: undefined method xxx for nil:NilClass" error once or twice, you get used to it.
There is nothing wrong with monkeypatching nil to show a more descriptive error message, but it's not going to solve the root cause of the problem, which is coding practice that permits and propagates nil values.
Consider the following very contrived example:
def do_something(input)
object = fetch_something_with(input[element])
do_something_with(object)
end
A couple of ways this might blow up:
input hash does not contain element, and passes nil into fetch_something_with
fetch_something_with returns nil (by design or upon failure), which gets passed into do_something_with
An alternative approach might be:
def do_something(input)
object = fetch_something_with(validated_input)
object && return do_something_with(object)
end
def validated_input(input)
input.fetch(element) # fetch raises an exception if the value is not present
end
A bit more code, but it gives us peace of mind that this code isn't going to silently pass nil down the line to fail at some later point.
Of course, it doesn't make sense to be this paranoid in every single method, but it is good practice to have well thought out boundaries in your code, either at method or object level. Having nil slip by frequently is a sign that these borders need some strengthening.
Do you mean that, when doing something like b=nil; b.say_hello;, ruby will give you "undefined method `say_hello' for nil:NilClass (NoMethodError)" instead of something like (as in your claim) "undefined method `say_hello' for nil:NilClass (NilReferenceError)"?
Ruby is not wrong for this since nil is an object like other objects. It has methods like to_s, so ruby can't ban any call by just raising an exception saying "because it is nil, you cannot do anything. I will give you a NilReferenceError".
You can surely do as your code above if you know that what you are doing may prevent ruby's default behavior.

Rails ActiveRecord callbacks

I'm having an issue with a date format. I have a time picker that has the date in a funky format (well, it's a nice format, actually, but not to the computer). I'm trying to have Chronic parse the date so that it can be saved properly.
At first, I was doing, in the create action of my controller:
params[:event][:start] = Chronic.parse(params[:event][:start])
but if and when validation fails, it sends the parsed value back to the view, and my datetimepicker is all botched, then.
So, I thought... callback? In my model, I added:
private
def date_change
self.start = Chronic.parse(self.start)
end
I tried before_save, before_validation, after_validation... but nothing seems to get that date formatted correctly.
As it stands, I keep getting ArgumentError in EventsController#create - Argument out of range. I assume that's because the database is expecting a properly formatted datetime object.
Any idea on how I can accomplish my goal, here, of not changing the params, but still being able to save a properly formatted object?
I'm guessing that the problem is occurring the the start= mutator method that ActiveRecord supplies. If you're doing things like this in your controller:
#event.update_attributes(params[:events])
#event = Event.create(params[:event])
#...
then create and update_attributes should call start= internally. That should allow you to put the Chronic stuff in your own start=:
def start=(t)
super(Chronic.parse(t))
end
You might need to adjust that for non-String ts, I'm not sure what Chronic.parse(Time.now), for example, would do. You could also call write_attribute(:start, Chronic.parse(t)) or self[:start] = Chronic.parse(t) if you didn't want to punt to super.
Note that before_validation and similar handlers will be called too late to bypass whatever default string-to-timestamp conversion ActiveRecord is doing but a mutator override should happen at the right time.
Alternatively, you could parse the time in the controller with something like this:
event = params[:events].dup
events[:start] = Chronic.parse(events[:start])
#event = Event.create(event)
Assumption is the mother of all mess ups :)
are you sure the callback is hit? Because if it would, and the error occurred (like it did), wouldn't it still send back the incorrect data (because parsed) back to the view? In case of doubt: log something to make sure it is hit.
are you sure which field causes the Argument out of range error.
Most cases bugs are so hard to find/fix because we assume we know the error, but we are looking at the error in the wrong way.
Easy ways to test which attribute causes the error:
open rails console, build an object with the parameters, save it, and ask the errors. Something like
e = Event.new(params[:event]) # copy params verbatim from your logfile
e.save
e.errors
and that will display which field causes the error.
Alternatively: use pry and add a line binding.pry just after the save, so you inspect the errors (more info)
Answer (assuming your assumption was correct)
I see two options to do what you want:
use the after_validation callback, if you are sure the data will always be correct, and correctly parsed by Chronic. This way if validation is passed, then convert the field and normally nothing can go wrong anymore, and the value is never sent to the browser again.
Note: if some other attribute is causing the error, this callback is never hit, of course. Because it does not pass the validation.
use a virtual attribute, e.g. start_str, which is a visual representation of your start, and
before_save convert it to start. It does not really matter that much here, because if validation fails, you just show start_str and not the "real" start field.

Why does is_a? fail to match type, and results in split throwing an exception?

While running RSpec and FactoryGirl, it automatically converts any :id param into a fixnum, whereas, in real life, it gets passed as a string. Code I have that processes that :id as a composite primary key tries to call split on params[:id]. That results in an exception, like so:
NoMethodError:
undefined method 'split' for 234:Fixnum
When trying to call something like this:
#myinstance = MyClass.find(params[:id].split("-").map{|x| x.to_i})
In an attempt to get around this issue, I have added a simple type check:
if params[:id].is_a? Fixnum
#myinstance = MyClass.find(params[:id])
else
#myinstance = MyClass.find(params[:id].split("-").map{|x| x.to_i})
end
Yet, this does not work as I expect it to. The if statement evaluates to false, and the split is still attempted, resulting in the same error. How is it possible that Ruby recognizes params[:id] as a Fixnum in the "else" logic, yet fails to evaluate to true in the if statement?
It's sheer logic: params[:id] is a String or nil.
Oh... I must edit my answer:
When you do controller specs, the params you send are sent as-is and not stringified...
That's a pain but it should be fixed sooner or later.
So in your specs, stringify your params to be closer to reality.
That's a really important point: don't adapt your code to your specs, do the opposite.

When / why would I ever want to raise an exception on a method?

I've noticed that some methods in rails use the ! operator to raise an exception if invalid. For example, User.create!
Why and when would I want to use something like this?
Thanks.
I could want exceptions for various reasons
I might want to make sure that the method succeeds but I don't want to do any actual error handling here. But I might be fine with the request blowing up (e.g producing an HTTP 500) if something fails
I might want to have some error handling elsewhere, e.g some way up in the call chain. Generally, it's way more verbose and nasty to pull some error state with you a long way towards error handling. Structured error handling, i.e., begin ... rescue ... end make that clearer.
Exceptions can have additional data attached (e.g. an error message)
If you have different error types, it often much clearer to represent those with different exception classes instead of some magic id values (as is common in C)
There are good reasons to not use exceptions but status flags when the error states are few in number and are fully handled directly after the call. But every technique has its place and time.
And btw, the save method internally calls save!, handles the potential exception and returns false if there is any. See the code for the implementation.
ActiveRecord will roll back a transaction if an exception is thrown while a transaction is active.
So the methods that throw exceptions are nice to ensure the database doesn't commit a transaction when an exceptional condition occurs. When you can "handle" the problem yourself -- or, if it isn't actually a problem -- then you can use the variant without the !, check the return value for error conditions, and handle them yourself.
For something specific like User.create:
You might use the User.create method to determine if a user-supplied username is not yet picked and provide a prompt for the user to select another name, if it is already in use.
You might use the User.create! method when finally submitting the request, so that your integrity checks can fail the create and rollback the transaction in case the user attempts bypassing your friendly name check.

How to capture errors from ActiveRecord::Base.connection.execute in Rails?

I want to run a raw SQL query as following:
ActiveRecord::Base.connection.execute(some_query);
Can I capture any errors that happen while executing the query? If yes, how?
Does execute returns anything? It doesn't say in the documentation.
Cheers
You can rescue errors as normal. For example:
begin
ActiveRecord::Base.connection.execute(some_query)
rescue
# do stuff with exception
end
Have a look at the MySql (for example) adapter's code to see what's going on.
In this case, execute returns a MySql::Result object.
execute method is typically implemented by respective database adapters and returns Result object from respective database libraries. So, if you are using Mysql the return value will be of type Mysql::Result.
Typically, if there is an error, the method will simply raise an exception which can be rescued.
I think I found the answer. Execute statement returns the error that it receives from the database and that can be captured in a variable and displayed.

Resources