I am speaking from what I observed, and may not necessarily the Rails-convention.
When we do rails g post title content:text
for example, you may remember that it does not have default: ''
for both title(string) and content(text). This is already a hint to me that there is no Rails-convention regarding this, or specifically speaking every attribute by default is allowed to be NULL or nil.
The advantage of using NULL is that you can identify which records have those attributes set up.
Let's say we have an API server. If a client creates a post to our API server, we know which attributes are intended to have a value. Let's say something like the following:
client-1's POST params:
post: {title: 'Foo bar'}
- if NULL-allowed, will create a
Post(title: 'Foo bar', content: nil)
- if default: '', will create a
Post(title: 'Foo bar', content: '')
client-2's POST params:
post: {title: 'Foo bar', content: ''}
- if NULL-allowed, will create a
Post(title: 'Foo bar', content: '')
- if default: '', will create a
Post(title: 'Foo bar', content: '')
From above, notice that if we have default: '', then we cannot know if the client is actually intending to have an empty content
value, because for both client-1 and client-2, the resulting content
value for the post will have '' (empty) anyway. But if we have NULL-allowed attributes, then we can still identify if the client intended to not have a content
value by not passing in the attribute in the parameters. This is an important use-case.
Depending on your project and the attribute's purpose, you may either use NULL-allowed or set default empty string for that attribute.
Now, the one main problem with NULL-allowed as you have encountered is that you cannot guarantee that every value will be a String
, therefore your <%= @user.profile.location.upcase %>
will raise an error in case location
is NULL or nil.
This can be a little annoying especially if you have a chain of methods like your example
<%= @user.profile.location.upcase.downcase %>
This will be a problem if @user.profile.location
is nil, because you'll have to gracefully ensure that it won't raise an error. And this will also be a problem if @user.profile
is nil (let's just say you are allowing @user.profile
to be nil). And normally, you'll do something like the following to make this work:
<% if [email protected]? && [email protected]? %>
<%= @user.profile.location.upcase.downcase %>
<% end %>
That if
condition can still go on very long as you have longer chained methods to gracefully ensure it won't raise any error.
Using .try()
will potentially "clean" this up. I use this a lot of times especially in template files. Solution will be cleaner like below, albeit potentially confusing for those who do not know:
<%= @user.profile.try(:location).try(:upcase).try(:downcase) %>
If either .location
or .upcase
is nil, it will return nil, and not raise any undefined method ... for NilClass
anymore.
NULL
), I always use thenilify_blanks
gem. I know this doesn't answer your question, but it is more of a nod of agreement. – Lottielotto