A common Ruby/Rails idiom used in views looks like the following:
<% if @posts.empty? -%>
There are no posts.
<% else -%>
<ul>
<%= render :partial => 'post', :collection => @posts -%>
</ul>
<% end -%>
Say that was from /posts
or PostsController#index
.
Alternatively, I’ve seen this:
<% if @posts.any? -%>
<ul>
<%= render :partial => 'post', :collection => @posts -%>
</ul>
<% else -%>
There are no posts.
<% end -%>
Testing for the positive first instead of the negative first.
The following is also valid, but its not legal in my book because it’s trash:
<% unless @posts.empty? -%>
<ul>
<%= render :partial => 'post', :collection => @posts -%>
</ul>
<% else -%>
There are no posts.
<% end -%>
I bet that took a couple takes before you got that. ‘unless … else’ is a terrible Ruby construct.
Sorry got sidetracked on my ‘unless … else’ hate.
Take a look at the following Ruby:
[1, 2].any? => true
[nil, nil].any? => false
[1, 2].empty? => false
[nil, nil].empty? => false
I never liked using #any?
without a block, it felt/feels too strange. Now if
you don’t pass #any?
a block you get a default block, which looks like this:
[1, 2].any? {|each| each}
Now #any?
is a method that asks is there anything in this collection that’s
true?. In Ruby, the existence of an object is considered true; so calling
#any?
without a block is saying is there something that’s not nil or false in
this collection?.
So the view code from above:
<% if @posts.any? -%>
<ul>
<%= render :partial => 'post', :collection => @posts -%>
</ul>
<% else -%>
There are no posts.
<% end -%>
Isn’t it really saying is there anything in this posts collection? Because that
collection could contain nil
. And nil
is an object, its an instance of
NilClass
; so the collection actually does have something in it. Using #any?
is this context, basically as way of saying ‘not empty’, is incorrect because
it’s relying on Ruby’s ugly feature of ‘object existence implies truth’ to test
if there’s anything in a collection.
The correct code, when testing the positive first, should be:
<% if ! @posts.empty? -%>
<ul>
<%= render :partial => 'post', :collection => @posts -%>
</ul>
<% else -%>
There are no posts.
<% end -%>