Null Objects:

A while back, Rails 4 introduced the concept of a Null Scope. Taking the 
form of Person.none (or some_company.people.none), it builds a Null 
Relation that behaves like a "real" Active Record relation, allowing scope 
chaining and other interface methods available from a scope (like #count), 
avoiding need for brittle nil checks. I'd like to discuss extending this a 
general concept of Null Forms in Active Record.

Let's start with the obvious issue of mapping SQL NULLs to Ruby nils. There 
is a conceptual mismatch here. SQL NULL means "unknown value", while Ruby 
nil means "no value". This manifests itself rather painfully in anything 
from JOIN to NOT IN (…) queries. To make things worse, saving data from a 
form-based request (the most common case of introducing data into Active 
Record in most Rails apps) will pass blank strings from an empty form. So 
now you have a mix of NULLs and blank strings in your database, depending 
on whether or not a form ever updated the record, even if it was saved 
without any intentional user input.

Null Form primitives:

It may be possible to design the database to simply not allow any NULL 
attribute 
values and instead default the appropriate Null Form:

   - '' for varchars
   - 0 for ints
   - [] for serialized arrays
   - {} for Hstore or other serialized hashes

Etc.

One could argue, though, that such a design introduces excessive logic and 
performance penalty on the database layer, and that instead, it should be 
Ruby which uses the Null Form object whenever the database contains a NULL 
value. 
So, for example, if I have a database record in the users table with id=1, 
name=NULL, and I do User.find(1).name, I'd prefer to get '' instead of nil.

So essentially this is a question of whether Active Record type casting 
should cast NULL values into their Null Form object of the appropriate 
type, rather than nil. But I see the obvious issues with this approach as 
well, notably whether #attributes should return the raw nil or use the 
casted Null Form, especially for APIs, so perhaps manually using database 
default values with null: false constraints is still the best way to 
accomplish this.

Null Form associations:

NULL-to-nil mismatches are yet more painful when dealing with associations, 
like document.project.account.name. We need to do nil checks everywhere, or 
use try everywhere, or delegate ... allow_nil: true hacks. How much nicer 
would it be if there was a Null Form association? Imagine the following:
document.project_id => nil
document.project => <#Project id=nil>
document.project.persisted? => false
document.project.null? => true
document.project.account_id => nil
document.project.account.null? => true
document.project.account.name? => '' # (or `nil` if not also using Null 
Form primitives)


Similar to the Null Primitive concept, the idea is to avoid extraneous nil 
checks. 
If the context and data type is known beforehand (and it is in the case of 
ActiveRecord due to schema introspection, the same logic that allows Active 
Record to know whether an attribute should be casted to string or integer, 
for example), having Null Form logic would bring a lot of the benefit of 
types languages into Rails, while reducing (rather than increasing) the 
maintenance burden and logical complexity of code. In the case of 
associations, this information could be inferred from has_many / has_one / 
belongs_to definitions, and/or introspected from used foreign keys (added 
in Rails 4.2).

In fact, existing code (notably accepts_nested_attributes) already *expects* 
you 
to manually build a new child instance if it is nil and you want an inline 
child object form, making you write code like @user.build_profile if 
@user.profile.nil? .

Discussion:

Both nil primitives and nil associations can be dealt with manually, via 
either explicit nil checks at point of invocation, or by using the above 
suggestions (such as database null: false, default: '' for blank string, 
controller @user.build_profile if @user.profile.nil? for blank 
associations, etc.) But all of these approach feel tedious and brittle, and 
(more importantly) add unnecessary complexity to understanding and 
reasoning about code.

I wonder whether anyone had experience implementing some kind of 
programmatic Null Form behavior for either attribute primitives, 
associations, or both? Did you only use application-level solutions similar 
to above? Or did you try some kind of framework-level approach to 
dynamically build Null Form objects for handling database NULL values? How 
well did the rest of your code, as well as Rails handle it? For example, 
there is no way in Ruby to override the truthiness of a Null Object for `if 
object { a } else { b }` type comparisons, so any object other than nil would 
behave differently, including any custom Null Form.

Or, perhaps, there is already some literature you know, or ideas you have, 
about the best practices to better handle nil primitives/associations? I'd 
love reading about it.

Thanks!

-- 
You received this message because you are subscribed to the Google Groups "Ruby 
on Rails: Talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/rubyonrails-talk/82c60e70-49d9-4720-8771-e985e6a04df2%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to