10
>> current_user.first_visit
=> 0
>> if current_user.first_visit
>> puts "test"
>> end
test
=> nil

Why does it print test?

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
Matt Elhotiby
  • 43,028
  • 85
  • 218
  • 321
  • It looks like your question has been answered. But you should stop using numeric values for true/false. Use the keywords true and false if your language supports them – colithium Sep 24 '10 at 04:02
  • Rails has extended the integer type. You can also use "if current_user.first_visit.zero?" – vise Sep 24 '10 at 05:49
  • 1
    @vise: `.zero?` is plain old ruby: [APIdock.com link](http://apidock.com/ruby/v1_8_6_287/Numeric/zero%3F) – Andrew Grimm Sep 24 '10 at 10:27
  • This question is probably a duplicate of [What are the Ruby Gotchas a newbie should be warned about?](http://stackoverflow.com/questions/372652/what-are-the-ruby-gotchas-a-newbie-should-be-warned-about) – Andrew Grimm Sep 24 '10 at 10:28

3 Answers3

16

In Ruby only nil can be considered as analogue to false. There is no assumption, that 0, "" or [] means false, so you must use == 0, .blank?, .empty?, .zero?, etc.

But nil doesn't always behave as false. For example, in string interpolation, the .to_s method is applied to #{} contents, that works differently for FalseClass and NilClass:

irb(main)> "qwe #{false} rty"
=> "qwe false rty"
irb(main)> "qwe #{nil} rty"
=> "qwe  rty"
Nakilon
  • 34,866
  • 14
  • 107
  • 142
  • 1
    Please note that `.blank?` , `.empty?` are extensions provided by Rails framework (through `activesupport`) and not available in Ruby Standard or Core Lib. Also the different behaviour is because "#{false}" calls the `to_s` method. – Swanand Sep 24 '10 at 05:49
  • 2
    @swanand: `zero?` and `empty?` are there in core lib (not sure which classes you had in mind, though). – Mladen Jablanović Sep 24 '10 at 09:17
  • @pdom's edit was correct. IDK, why others rejected it. http://stackoverflow.com/review/suggested-edits/7845176 – Nakilon Apr 27 '15 at 15:25
6

You can think of Ruby's if as testing either a boolean, or for the availability of data (vs nil). The implicit conversion from 0 to false supported by C and (hence other languages like C++) was more an historical artefact from days before C had a distinct boolean type. There, it relies on 0 being a convenient sentinel value or having an intuitive meaning. For many things (e.g. some country where clothing sizes range from 0 to 8, POSIX libC function call results) zero does not convert nicely to a logically equivalent boolean, so it's not a bad thing that Ruby goes its own way.

From this perspective, the issue with your code is that current_user.first_visit - the name of which implies a boolean type - actually holds 0 and not false. Alternatively, if you had the clearly numeric current_user.visit_counter, it would be natural and correct to use one of:

current_user.visit_counter > 0
current_user.visit_counter >= 1
current_user.visit_counter != 0
Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • I'd say that this is very inconvenient especially when you come from other languages, 0 is almost always treated as falsy value, just makes it harder to work with ruby – Vedmant Feb 12 '19 at 12:50
1

You can try this

>> current_user.first_visit
=> 0
>> if current_user.first_visit != 0
>> puts "test"
>> else
>> puts "fail"
>> end
fail
=>nil

When checking for numeric values you also need to match it with the expected value

Rohit
  • 5,631
  • 4
  • 31
  • 59