Ruby on Rails Exceptions - ruby

I am a junior rails developer and was advised to use Class.find(id) to query the database instead of Class.find_by_id(id) which I previously had. The reason I was told is because the former would raise an exception while the latter would return nil. I realize this happens but I am wondering what the high level conceptual logic is for doing it this way. Why do I want the exception? Is this a rails standard where I would always prefer a method that returns an exception as opposed to nil?

You typically want the exception because you're typically doing Foo.find(id) based on data input coming from the user, such as clicking on a link.
For example, you show the user a list of items. There are links like this:
http://example.com/items/100
http://example.com/items/101
http://example.com/items/102
The user clicks the first link, and expects to see item 100.
Your code does this:
Item.find(100)
You expect to find the item, because app created the item link. You'd be surprised if the item didn't exist.
(Corner case surprises are possible: perhaps the item was deleted, or perhaps a hacker is sending in missing ids, etc. Using exceptions helps you handle this as an exceptional circumstance.)
Exceptions are preferred to nil for this, because you want the code to fail immediately so you don't accidentally send the nil on to some other method.
Ruby nil objects can be confusing because they evaluate to falsey and also because nil.id == 4 because of how Ruby uses C. Error messages show up like "Warning: Object#id will be deprecated" or "undefined method for 4:Fixnum".

Nils are problematic as a return type in Ruby in general. There's a great (paid) screencast by Gary Bernhardt that explains why you want to avoid returning nil from methods, but in a nutshell: when a method returns nil, and that nil gets passed up through a chain of method calls and something goes wrong somewhere, it can be extremely difficult to figure out where the actual problem occurred.
Say, for example, you have something like this:
foo_model = MyModel.find_by_name('foo')
# some more lines of code
do_something(foo_model)
and a method:
def do_something(model)
# some stuff stuff
some_other_method(model)
end
Now, if MyModel.find_by_name('foo') returns nil, that nil will be carried along without any errors until it actually has to do something. Say, in some_other_method, you actually try to call something on model, say model.save, you will get an error:
undefined method 'save' for nil:NilClass (NoMethodError)
The trace will carry you back up the method calls, but it will not mention the line that was actually problematic, where you assign MyModel.find_by_name('foo') (which evaluates to nil) to foo_model.
You can imagine that in a real application, the code can be much more complex, and returning nil can make it much more difficult to figure out the source of an error.
An exception, in contrast, tells you immediately where the problem is, and the trace will go back to the line where it occurred. That's one reason (there are others, I imagine) why in general, returning nil is not a good idea.
Hope that helps.

Related

What to put in rspec's expect for a globally available object?

I am writing a custom matcher for my logging output, mostly so that I can customize the error output to be more readable and helpful.
The thing being examined for the test is the array returned by LoggingSpecHelper.log_events, a module class method (i.e. not a module instance method). Therefore, it is available without the need for it to be passed as a parameter.
LoggingSpecHelper.log_events is kind of long to specify in each expectation, and anyway I'd prefer to hide that implementation detail from the caller, in case that implementation changes. That leads to my using expect(:logging), where :logging is a dummy value which has no meaning and is not examined. This, however, is awkward and confusing, leaving the reader scratching their head thinking "he's examining a symbol?"
Here is an example of how it is currently called; in this case I'm looking to see if a fatal error occurs in the log that contains 'something is misconfigured' (not a real production message):
expect(:logging).to have_log_output_match(:fatal, 'something is misconfigured')
The long form of this, and what is happening in the matcher, is below, but if I do this instead of calling the matcher, I don't get to see what is in the log if an error occurs (the matcher includes log content in the failure message):
expect(LoggingSpecHelper.log_events.count do |event|
event.level == :fatal && /something is misconfigured/.match?(event.message)
end).to be == 1
How would you suggest I handle this? I don't think shared examples are what I want here because this log expectation might not be the only expectation in the example.
As an aside, I should probably rename the matcher so that it is clear that I am testing for 1 and not >=1 occurrences.

Is there a way to return nil message to user but not interrupt the app in ruby?

I'm trying to find a way to write in GUI for user that there is nothing in the button they just clicked
I've tried a method like `
if #album[#user_choice_album].tracks[10].name == nil
return
`
But it interrupt the program and gave an message instead, while i was expecting it to interrupt the procedure and go to the next one.
test.rb:192:in `draw_now_playing': undefined method `name' for nil:NilClass (NoMethodError)
if (!(#album[#user_choice_album].tracks[10].name))\
It looks like you have tracks stored in an array (without knowing any details about your data structure and model). And when the user requests a track that doesn't exist, it returns nil.
That means there is no point in asking if the name of that track is nil when the whole track is nil.
I guess this should work for you:
if #album[#user_choice_album].tracks[10].nil?
return
Or simplified:
return unless #album[#user_choice_album].tracks[10]

Ruby .include? method is not accepted as a valid method

I have this Ruby code in a script:
$dev_input=gets.chomp.downcase!
if $dev_input.include? "/"
check_developer_commands()
else
puts ">>Invalid Command<<"
continuing_dev_mode()
end
The problem is, whenever I try and run the script containing this, I get an error spat back at me that says :
dev_continue_main.rb:3:in 'continuing_dev_mode': undefined method 'include?' for nil:NilClass (NoMethodError)
Any idea what this error might be? I'm pretty sure that this is the proper way to use the .include? method. I've done some research, looked at tutorialspoint.com and some other sites, but they agree that this is the proper way to use this method.
I checked the error message and it confirmed that the third line in this script/my example is the source of the problem, so it's not some other instance of this method throwing an error.
Any thoughts? Please Help!
The problem is that $dev_input is nil. That stems from applying downcase! in defining $dev_input. I don't know why you want to possibly assign nil to $dev_input, and at the same time claim that calling include? on it is the right way. I don't get your intention for doing that, but if you instead had $dev_input = gets.chomp.downcase, then it wouldn't cause such error.

Contact info empty/not parsed for domains from some registrars

I'm starting to use whoisrb and I'm noticing domains from some registrars return nil contact information.
For example:
domain_name = ARGV[0]
r = Whois.whois(domain_name)
t=r.registrant_contact
if t == nil
puts 'Registrant Contact is empty.'
end
Will return "Registrant Contact is empty." Trying to access the contact attributes results in an error, like undefined method 'id' for nil:NilClass (NoMethodError).
If I check the raw record that's being returned, puts r, I can see it's getting the thick record, so the contact information is there in the unparsed raw record.
The two registrars I've noticed this for, so far, are onlinenic.com and namesilo.com. If you try to run whois for those two domains, you'll see what I mean.
I'm checking the ICANN compliant sample here:
https://www.icann.org/resources/pages/approved-with-specs-2013-09-17-en#whois
against onlinenic.com and namesilo.com, and I don't see any substantial differences (maybe I'm missing something, though).
Any ideas why it's having trouble parsing these, or pointers on what I could check to fix it? Thanks.
It happens when the registrar has no parser associated, or the parser doesn't have the definition required to parse the contacts.
In other words, unless a parser exists, it's possible that the registrar details are in the response but the library can't find them.
In that case, the solution is to either add/update the parser corresponding to the specific registrar/registry.
Since this behavior is confusing to whoever is not familiar with the internals of the library, also note that the new release 4 will raise an error in this case (instead of silently returning nil). In this way it will be clear when the value is nil vs the value is unknown.
r = Whois.whois(domain_name)
The r here is a Whois::Record object and you can find the available methods here. registrant_contact is not one of them. You probably have to parse it out yourself.

Ruby Koans about_methods line 123 object loop

Each time I add in the correct code, it gives me the same error due to AboutMethods:0x00000101841a28 number changing each time. It's like its stuck and I don't know how to get out this loop. It worked once, then I went on to the next step, but then it triggered an error after that.
I must not be inputting the correct line of code given from the console?
def test_calling_private_methods_with_an_explicit_receiver
exception = assert_raise(NoMethodError) do
self.my_private_method
end
assert_match "private method `my_private_method' called for #<AboutMethods:0x000001008debf8>", exception.message
end
The AboutMethods:0x000001008debf8 changes each time, not sure how to approach this problem?
AboutMethods:0x... is the output of the inspect method, which usually (and in this case) includes the class name (AboutMethods) and the object id (0x...). The object id is related to the objects location in memory, so it will change every time.
In my experience, there is very little value to checking the string from an exception (it's brittle). However, if you feel the need, use a regex:
assert_match /private method `my_private_method' called for \#\<AboutMethods:.*/

Resources