any? != ! empty?

any? != ! empty?

A common Ruby/Rails idiom used in views looks like the following:


  <% if @posts.empty? -%>
    

There are no posts.

<% else -%>
    <%= render :partial => 'post', :collection => @posts -%>
<% end -%>

Say that was from ‘/posts’ or PostsController#index.

Alternatively, I’ve seen this:


  <% if @posts.any? -%>
    
    <%= render :partial => 'post', :collection => @posts -%>
<% 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? -%>
    
    <%= render :partial => 'post', :collection => @posts -%>
<% 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 existance 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? -%>
    
    <%= render :partial => 'post', :collection => @posts -%>
<% else -%>

There are no posts.

<% end -%>

Isnt’t 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? -%>
    
    <%= render :partial => 'post', :collection => @posts -%>
<% else -%>

There are no posts.

<% end -%>
Jared Carroll Developer

Sharpen your programming skills by completing coding exercises that are reviewed by other developers at Upcase today.