As for why this is a common problem, "u.profiles.thumbnails.large" is is probably a chain of Rails/ActiveRecord model associations[1], which generally map to table rows . If there the "profiles" table doesn't have any rows "WHERE profiles.user_id = users.id", then ActiveRecord will leave a nil[2] (or empty array) in user.profiles. This is true for all foreign key relationships, so you often end up having to do something to protect against null values (no rows) every time you move between models.
Sometimes it is possible to work around this by always creating the associated model when the parent object is created, that only works in a few cases. Note: I recommend against doing tricks to auto-build the associated model on first access: that kind of "clever" solution can lead to confusing bugs later on. (I learned that one the hard way)
[1] http://guides.rubyonrails.org/association_basics.html
[2] ibid, 4.1.1.1 "The association method returns the associated object, if any. If no associated object is found, it returns nil."
Again excuse my ignorance of the subject and technical details in Ruby.
"nil" in Ruby is an instance of NilClass. NilClass has a few methods, but e.g. "thumbnail" is not amongst them, so Ruby looks for a "method_missing" method, doesn't find that either, and then throws an exception, just as if you'd called another non-existent method.
To answer a question you didn't ask, though, you can easily write classes to wrap your objects to get pretty much the effect you are asking about. E.g.:
class Maybe
attr_reader :value
def initialize value
@value = value
end
def method_missing *args
return self if @value.nil?
Maybe.new(@value.send(*args))
end
end
p Maybe.new(nil).i.can.call.anything.without.exceptions
p Maybe.new("Hello foo").gsub("foo","world").value
The first call to "p" will print a respresentation of a "Maybe" instance holding "nil" in "@value".The second will print "Hello world", as "gsub" will be called on "Hello foo" since the "Maybe" has a non-nil value.
There are any number of implementations of helper classes like this for Ruby, with various levels of popularity. The benefit of course is that you can easily customize the behaviour for your specific needs (e.g. you could create one that will return a default value instead of nil, or you can create one that short-circuits on other values than nil, or any number of alternatives)
The advantage of introducing .? as proposed, though, is that it is easier to optimize (no new class or lots of extra objects to create and destroy).
Let's get back to the example, let's say that checking for «u» would return «nil», wouldn't checking for «u.profiles» would result in an exception thrown nonetheless?
Almost nothing is a primitive value in Ruby. Not even true/false/numbers are "traditional" primitive values.
> Let's get back to the example, let's say that checking for «u» would return «nil», wouldn't checking for «u.profiles» would result in an exception thrown nonetheless?
No, because `u.?profile` would return `nil`, then, when you try `nil.?thumbnail` on nil again, it would return nil. So, doing the following, with `u` being `nil` is legal:
if u.?profile.?thumbnail
# doesn't run if u is nil
endThat includes nil, true, false (instances of NilClass, TrueClass and FalseClass respectively).
A common way of explicitly checking for nil, as opposed to nil or false is to do "foo.nil?", for example, which explicitly calls the method nil? on foo, which will usually return false for objects other than nil (nothing stops you from overriding it if you want your class to pretend to be nil - e.g. maybe I'd want my "Maybe" class to return true for "nil?" if the value it holds is nil).
As for the example the whole point is that like my naive "Maybe" implementation, this new operator would check if the target is nil before trying to call a method. Unlike my naive implementation, it doesn't need to wrap objects, and it can short-circuit evaluation if it meets a nil (you can implement versions that will short-circuit evaluation too, but I can't see any obvious ways of both letting you short-circuit and getting "clean" syntax if you want to support method arguments).
Nils will get into data structures and figuring out when and where they got there will become more work. You'd end up writing a lot more logic testing for nil and throwing an exception when it's found, to avoid the nil propagating further.
>> class NilClass
• def foo!
• puts "bar?"
• end
• end
=> :foo!
>> nil.foo!
bar?
Rails uses[1] this with the very-useful #blank? method.[1] https://github.com/rails/docrails/blob/master/activesupport/...
I'd call it marginally convenient, because you have to type less, but it doesn't increase usability, and the readability is pretty much the same (or worse, because someone would have to know NilClass responds to blank?, if they don't they might've expected an empty array, or an empty string).
Also, why does FalseClass respond to blank? with `true`, but not TrueClass (I didn't know it did this for those two until now)? How is false less blank than true?
The entire point behind #blank? is that it provides a cross-class[2] method that works even when the object doesn't exist (and you get nil instead). This was designed for Rails to simplify situations such as the params[3] (parsed URL query string), which might not have a particular value.
# when handling "/action?foo=bar"
params[:foo] # => "bar"
# when handling "/action?foo="
params[:foo] # => ""
# when handling "/action"
params[:foo] # => nil
With #blank?, you only need one function call to handle both the empty string and nil cases. if params[:foo].blank?
# render a static placeholder (never looks at params)
else
# render Foo's markup, using the value in params[:foo]
end
If you cared about the empty states other than as a generic "it's blank", other methods may be more appropriate.> why does FalseClass respond to blank? with `true`, but not TrueClass
That's a good question. I would have assumed both true and false as not-blank (#blank? => false), as those could both be entered (#present?) values. I suspect it's related to HTML forms only including successful controls[4] in the query string.
[1] https://github.com/rails/docrails/blob/master/activesupport/...
[2] see: duck-typing
[3] http://api.rubyonrails.org/classes/ActionController/Paramete...
[4] http://www.w3.org/TR/html401/interact/forms.html#h-17.13.3.2
> I suspect it's related to HTML forms only including successful controls
Yeah, this is a good point, since an unchecked checkbox isn't transmitted in a standard HTML form, but Rails does add a hidden input element with the value false; so I guess #blank? does somewhat increase readability here, but this could've and should've been fixed in a better way (a few ideas come to mind).
Don't get me wrong, I probably used #blank? (and might in the future) just for the sake of convenience. However, it makes me feel guilty when I do, because it could subtly introduce and hide bugs due to lack of more adequate checks.