This is a post about basic things and if you are an experienced user you might want to skip this one but I see a lot of beginners and also advanced users which are not using them.
First you should know that Ruby only knows of two falsy values which are nil
and false
. Anything else is evaluated to true.
Secondly the "or" operator in Ruby ||
always returns one of the values you are comparing. In PHP for example $var = $foo || "bar"
would result in $var
being a boolean value but in Ruby it's much nicer! var = foo || "bar"
would result in var
being foo
if it is not falsy or the last value in the chain. The following two lines are basically identical:
foo = a || b || c || "other"
foo = a ? a : b ? b : c ? c : "other"
If you're using ||
you might think can't I just use or
since that reads nicer?. You can't. ||
isn't the same as or
and &&
isn't the same as and
in terms of precendence.
Consider using presenters (aka. decorators). They are really cool, even with these tricks.
nil?
I see beginners doing this:
link_to(user.job.nil? ? "Unemployed" : user.job, jobs_path)
Since nil
is already falsy you could omit this explicit check. A string attribute in ActiveRecord only returns either nil or the string. Checking #nil?
explicitely is rarely used and you wont see this very often. So get rid of it:
link_to(user.job ? user.job : "Unemployed", jobs_path)
But since we know how ||
works we could make it even shorter:
link_to(user.job || "Unemployed", jobs_path)
Cool eh?
blank? and present? (and presence)
Side note: blank?
is the exact opposite of present?
and all three methods are defined on Object so you can use them on any object (there are some exceptions but this is another topic).
What if the job is an empty string? This will happen by default when your form field is left empty. We'd had to check for job being present. So we'd end up again with something like this:
link_to(user.job.present? ? user.job : "Unemployed", jobs_path)
But fortunately there is presence
which will return the object when it's present?
or nil
otherwise. So we can use ||
again:
link_to(user.job.presence || "Unemployed", jobs_path)
try, try!
try
is a really cool helper to remove conditional code. The functionality has changed in Rails/ActiveSupport 4 so if you are using v3 then your try
behaves like try!
in version 4.
Try calls a method on an object (like send
) but only when it is truthy and returns the result. You can chain these together but keep the chain of responsibility in mind.
So from the above example assume that the job is a relation and has an attribue "name". We now need additional conditions:
link_to((user.job ? user.job.name.presence : nil) || "Unemployed", jobs_path)
But with try it's as easy as:
link_to(user.job.try(:name).presence || "Unemployed", jobs_path)
When there is no related job record user.job
would be nil so try
wouldn't call name
on it and return nil
instead.
With Rails 4 try
also won't call the method if the object is truthy but doesn't respond to the method. If you don't want this use try!
. Also take a look at the documentation.
"".try(:each) { "we never get here" }
"".try!(:each) # NoMethodError undefined method `each' for "":String
tap
This is a Ruby core method and functions like the meanwhile gone returning
method from ActiveSupport. It just returns the object it is called on after calling the block passed to the tap
method. It sometimes can be really handy but don't overuse it. In some cases it's not even necessary.
The reason why it is so useful is that ruby always passes references to objects around. So it can be useful when working with mutable objects (which are most types). You cannot however change the type of the variable.
It is also good for inject logging in chained method calls. Actually that is the intended , documented use for it which also has a good example.
Now an example for how it could be used with mutable objects:
def foo opts = {}
result = []
result << "foo" if opts[:foo]
result << "bar" if opts[:bar]
result
end
def foo opts = {}
[].tap do |result|
result << "foo" if opts[:foo]
result << "bar" if opts[:bar]
end
end
You can do this with strings, arrays, hashes and other objects on higher levels. Numeric values, symbols, nil or boolean values are not mutable and can't be changed in this manner. But note that many high level objects like the ActiveRecord model object are supporting this already:
Post.new do |p|
p.foo = "bar"
p.save!
end
@user.comments.build do |c|
c.subject = "…"
c.message = "…"
end