15

In Ruby on Rails, is there a difference between:

if obj
  # ...
end

and:

if obj.present?
  # ...
end

It seems they do the same thing and not using .present? would help keep the line of code shorter and potentially cleaner. I understand that .present? is the opposite of blank? but should we always be using it when trying to determine if an object is "truthy" or not?

Is there a performance difference of some kind?

ardavis
  • 9,842
  • 12
  • 58
  • 112
  • 1
    Possible duplicate of [A concise explanation of nil v. empty v. blank in Ruby on Rails](https://stackoverflow.com/questions/885414/a-concise-explanation-of-nil-v-empty-v-blank-in-ruby-on-rails) – Eyeslandic Aug 06 '19 at 14:41
  • Maybe, but I don't really need to know the difference between nil, empty, blank, present. I'm simply asking if it's worth calling `.present?` on an object when testing for truthiness. – ardavis Aug 06 '19 at 14:51
  • There is a performance cost to `present?` since adds a additional layer of calls, wether or not its significant is something you can test [with a benchmark](https://ruby-doc.org/stdlib-2.5.3/libdoc/benchmark/rdoc/Benchmark.html). But I do find that Rails noobs use it far too much - especially in cases where you are just checking if a variable is defined (not nil). – max Aug 06 '19 at 16:29

3 Answers3

13

The #present? method does a bit more in that it also returns false if the string was a real but empty string (i.e. "")

Which is useful as your forms might return empty strings instead of nils.

You can also use #presence which is a useful way of returning the value only if the value is #present?

name = params[:name].presence || 'ardavis'

The above wouldn't work if params[:name] was an empty string and you didn't use #presence

SteveTurczyn
  • 36,057
  • 6
  • 41
  • 53
  • But if I know my object is not a string then there's no real need for `.present?`, right? If I did `obj = MyModel.find(some_id)` then I know it's either nil or an instance of MyModel, right? – ardavis Aug 06 '19 at 14:54
  • @ardavis `find` will never return `nil` (`find_by` will though). If `find` fails it will raise `ActiveRecord::ResourceNotFound` – engineersmnky Aug 06 '19 at 14:55
  • Sorry, bad example. But yes, that's true. – ardavis Aug 06 '19 at 14:57
  • 1
    Yes, if I'm doing `my_model = MyModel.find_by(id: some_id)` I pretty much never use `my_model.present?` so I agree with you there. Note that `#present?` also is useful (hoiwever) for empy arrays, empty hashes, and even `ActiveRecord::Relation` as in `User.where(last_name: 'Moussolini').present?` – SteveTurczyn Aug 06 '19 at 14:59
  • Thanks, I appreciate your responses. – ardavis Aug 06 '19 at 15:35
7

They don't do the same thing at all.

In Ruby everything except nil and false are truthy. This is amazingly sane compared to the type casting schenigans in other popular dynamic languages.

irb(main):003:0> !!""
(irb):3: warning: string literal in condition
=> true
irb(main):004:0> !!0
=> true
irb(main):005:0> !![]
=> true
irb(main):006:0> !!{}
=> true
irb(main):007:0> !!Object.new
=> true
irb(main):008:0> !!nil
=> false
irb(main):009:0> !!false
=> false

present? and presence are a part of ActiveSupport which can be used to test for nil and false but are actually more useful when dealing with user input:

irb(main):010:0> "".present?
=> false
irb(main):011:0> [].present?
=> false
irb(main):012:0> {}.present?
=> false

present? and presence are widely overused by Rails beginners that don't bother to learn Ruby first. Just use implicit truth checks (if foo) or foo.nil? if you just want to check if an argument is sent or not or if a variable is set.

And while .present? can be used on ActiveRecord collections but there are more idiomatically correct choices such as any? and none?.

max
  • 96,212
  • 14
  • 104
  • 165
1

If you are working with string only checking if the attribute or object exists will return true, but present method will return false.

Here are some examples:

# Double negation return the boolean value
!!""
=> true


"".present?
=> false

" ".present?
=> false

[].present?
=> false

nil.present?
=> false

true.present?
=> true

false.present?
=> false

{}.present?
=> false

person = {:firstName => "John", :lastName => "Doe"}
person.present?
=> true

5.present?
=> true
Júlio Campos
  • 377
  • 2
  • 14