Am I abusing "rescue" for nil checks? - ruby-on-rails

I use rescue for everything, not just for "rescuing" exceptions. I mean, I just love the way it spares me verification and double checking data.
By example, lets say I have a model Item what may or may not have a User. Then, when I want to get the owner's name of the item I write:
item.user.name rescue ""
instead of something like
item.user.nil? ? "" : item.user.name
It makes the same think, since nil.name trigger an exception that I rescue with "", but I'm not pretty sure this is good practice. It makes what I want to, and it makes it with less code, but... I don't know, all that rescue words here and there makes me feel insecure.
Is it a bad practice or is it valid abusing of the rescue keyword?

I think you are abusing rescue a bit, though in Rails, there is a specific method for these issues: try. Documentation
In your case, item.user.try(:name) might be a nicer approach.

I would say this is not really a good habit to get into. I've never really used this feature in Ruby since it feels like I'm just hiding error cases. It's also worth noting that you're rescuing any and all exceptions without specifying any type of expected error. It's just always looked like something which is going to make debugging down the road harder than it needs to be, although, like I've said, I've never bothered to use it myself.

Like in most other languages, making the check yourself will run faster than using rescue.

As an alternative to your rescue abuse, check out the andand gem. It's similiar to the try another posted suggested, but nicer. andand lets you say:
item.user.andand.name
The expression will be nil if item.user is nil.

Related

Ok to Monkey Patch Object class to create custom data formatters?

My app has various data formatting rules such as if value == "-" then display "N/A" or if value == "NULL" then display "N/A" or if value.is_a? Numeric then value.round(2) and various other such rules. I currently have a helper method that does it as follows
def display_formatted
case
when '.'
#...
end
end
Is it ok to MonkeyPatch the Object class to create a formatter like
class Object
def my_app_format
case self
when is_a?(Numeric)
# some rules
when "-"
# some other rule
else
self
end
end
Currently the code is littered with display_formatted(value) in hundreds of places and I want to add different formatters so I can use it as follows and on any data type and in plain Ruby not Rails!
value.as_count_formatted
value.as_default_formatted
value.as_default_rounded`
Reason for patching it on the Object class is that the data type is not always known as the source data is not schematic so a field can have numeric or string values.
Yes and no. But mostly no.
As stefan mentions in the comment, "you're merely replacing foo_bar(obj) with obj.foo_bar". I agree that this assessment seems to generally sum up your changes.
This change would be "okay" in the sense that there are no obvious reasons for it to problematically change program behavior.
But there is a problem that monkey patching is generally frowned up and regarded as hacky. A big reason for this is that monkey patching can be done anywhere and it can be difficult to track down if you are not the person who wrote it. Even worse, it can also be the source of subtle and hard to track down bugs if replacing an existing function (although that is not the case here).
Ruby gives almost unlimited flexibility to programmers, but care must be taken with that flexibility.
On a purely technical level (will it work and keep correct functionality?) the answer is yes. On a more opinionated level (is this considered good practice?) I would say no - and that would be my advice (don't do it). In general, best practice is to avoid monkey patching unless there is a very good reason for it.

What is so bad with throw :async

I'm trying to find out how to create Rails API which could perform asynchronous responses. Many comments below the articles and the tutorials say something like:
That throw :async really scares me
throw :async makes me sad
So apparently using throw :async is considered as the bad practice but I haven't figured out why.
So I'll appreciate if anyone explain me why is it so bad, what are alternative approaches and so on.
People are making comments like:
That throw :async really scares me
because using throw is akin to writing a GOTO.
This is a big topic for discussion/debate, but the short answer is that GOTOs are generally frowned upon as "badly structured" code.
It is always possible to solve a programming problem without GOTOs, and seeing them in code typically indicates a hack that will cause issues down the line.
Here is an example of throw being used in ruby code:
def my_method
catch(:escape) do
outer_list.each do |item1|
inner_list.each do |item2|
throw :escape if item2 == "something"
end
end
puts "something was not found"
return
end
puts "something was found!"
end
The above throw is needed to break from a nested loop... But as you can hopefully see, there is a much simpler/cleaner way of writing the above code.
A GOTO is a weak way out when you have weak design.
Perhaps the two most commonly used arguments against GOTOs are that code is more difficult to understand, and that there are more ways to arrive at a particular point in code.
This does not mean you should never use throw statements - but simply that they are generally discouraged, and you should think carefully about your design if you find yourself needing them.

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.

Rails.cache.fetch, Symbols, & Memcached

I have a rails 2.3.4 app and a line that looks like:
temp = Rails.cache.fetch(:temp_id) { User.find_by_name('Temp').id }
and everything worked fine, until I decided to switch the caching layer to memcached by adding the following to my environment.rb:
config.cache_store = :mem_cache_store
Now the line which used to work fine gives me the following error:
undefined method 'length' for :temp_id:Symbol
/usr/local/lib/ruby/gems/1.8/gems/activesupport-2.3.4/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb:645:in 'get_server_for_key'
I understand the error, but I would imagine this common case would have been quickly discovered by a rails test case, so I am wondering if I am doing something wrong. Otherwise, I'm sure I can monkeypatch this issue to convert the symbol to a string.
Thanks
Just use string keys if you can. All the documentation examples use string keys. Although it's not explicitly mentioned as far as I can see, other keys are not supported.
The key arguments are passed directly to the cache implementation, so the different caching flavours may disagree on whether or not they accept anything other than strings.
Because the caches are external with the exception of in-memory cache, I'm not sure that supporting symbols would be useful apart from preventing cases like yours. The key will actually be written to some output somewhere (it's not just internal to your Ruby app), so conceptually the key should be a string.
Edit in reaction to comment: yes, it is of course possible and perfectly reasonable in this case to create a monkey patch to circumvent having to change all calls. What you're suggesting is this (copied into the answer for readability):
class MemCache
def get_server_for_key_with_symbols(key, options = {})
key = key.to_s if key.is_a? Symbol
get_server_for_key_without_symbols(key, options)
end
alias_method_chain :get_server_for_key, :symbols
end
I would also consider just doing a project wide search-and-replace for \.fetch(:\w+) and replace it with \.fetch("$1") (repeat for read and write if necessary). This should probably cover 95% of all cases and a subsequent run of your test suite should catch the rest of the errors.
In general: While the documentation of Rails is pretty good these days, a lot of assumptions are unfortunately still implicit. It's generally a good idea to take a good look at the examples that are given in the documentation, and use the same style. The documented examples are always how the framework was intended to be used.
FWIW, it's canonically Rails.cache.read and Rails.cache.write.

Resources