NilClass definitions - ruby-on-rails

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.

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.

Am I abusing "rescue" for nil checks?

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.

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.

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.

defined? method in Ruby and Rails

I have a quite old templating system written on top of ERB. It relies on ERB templates stored in database. Those are read and rendered. When I want to pass data from one template to another I use the :locals parameter to Rails render method. For setting default variables of those variables in some templates I use the defined? method which simply tells me if local variable has been defined and if not I initialize it with default value like this:
unless defined?(perex)
perex = true
end
I am upgrading the app to latest Rails and I see some weird behavior. Basically this sometimes works (sometimes perex is undefined) and sometimes it does not (perex is defined and set to nil). This happens without anything else changing.
I have two questions:
Is there any better way other than using defined? which is proving unreliable (was reliable for several years on top Rails 1.6)? Such a way should not result in me rewriting all the templates.
I have been going through Ruby docs and was not able to find anything about defined? method. Was it deprecated or am I just plain blind?
Edit: The actual issue was caused by what seems to be a Ruby/eRB bug. Sometimes the unless statement would work, but sometimes not. The weird thing is that even if the second line got executed perex stil stayed nil to the rest of the world. Removing defined? resolved that.
First: actually, defined? is an operator.
Second: if I understand your question correctly, the way to do it is with this Ruby idiom:
perex ||= true
That'll assign true to perex if it's undefined or nil. It's not exactly what your example does, since yours doesn't evaluate the assignment when the value is nil, but if you are relying on that then, in my opinion, without seeing it, you're not writing clear code.
Edit: As Honza noted, the statement above will replace the value of perex when it's false. Then I propose the following to rewrite the minimum number of lines:
perex ||= perex.nil? # Assign true only when perex is undefined or nil
The safest way of testing if a local is defined in a Rails template is:
local_assigns[:perex]
This is documented in the Rails API together with the explanation that defined? cannot be used because of a implementation restriction.
Per mislav's answer, I went looking for that documentation in the Rails API, and found it in Class ActionView::Base (under the heading "Passing local variables to sub templates"). It was hardly worth the search, though, since it barely said anything more than mislav did. Except that it recommends this pattern:
if local_assigns.has_key? :perex
Taking into considerationg mislav's original answer and KenB's elaboration, I think the following is the absolute best approach (though I'm open to opinion). It utilizes Ruby's Hash#fetch method to fallback on an alternate value if the key does not exist in the original hash.
perex = local_assigns.fetch(:perex, true)
This is even better than the ||= method that most users will suggest since sometimes you will want to allow false values. For example, the following code will never allow a false value to be passed in:
perex = local_assigns[:perex] || true

Resources