Reputation: 930
This code is provided as an example in for use with devise and OmniAuth, it works in my project.
class User < ActiveRecord::Base
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
end
I don't know why it's a single equals sign as apposed to a double equals sign, which I thought was necessary for if
-statements. My IDE "intelliJ IDEA" agrees with my concerns.
Upvotes: 8
Views: 1557
Reputation: 2354
Ruby returns the value when an assignment is made:
foo = data = 'Chalupa'
puts "foo value #{foo}"
foo value Chalupa
this can be used as a "trick" to verify that some code is executed only when the assignment was successful because you can use the new variable inside the if "block":
if klass = Object.const_get('String') rescue nil
puts "The class indeed exists: #{klass.class}"
end
but as mentioned, this approach is not recommended since it's hard to read and likely Ruby has more robust and standard ways to be sure a truthy variable is in fact there, like a simple Guard Clause.
Upvotes: -1
Reputation: 369458
In Ruby, a single equals sign is used for assignment. The expression
data = session["devise.facebook_data"]
assigns the result of evaluating session["devise.facebook_data"]
to a local variable named data
.
If the session
hash doesn't have a "devise.facebook_data"
key, it will return nil
and data
will be assigned nil
. Assignments evaluate to the value being assigned, so the assignment will evaluate to nil
as well. nil
is considered falsey in a boolean context, so the right operand of the &&
will not be evaluated. That way, you won't get a NoMethodError
trying to call nil["extra"]["raw_info"]
.
If the session
hash does have a "devise.facebook_data"
key, data
will be set to the value associated with it. Any value other than nil
and false
is considered truthy, therefore the right-hand operand of the &&
operator will be evaluated.
If the condition is truthy, the then
clause will be evaluated, which uses the data
variable assigned in the condition.
Note: I believe one could also use the data
variable within the right-hand side of the &&
operator, i.e. the condition could read like this instead:
if data = session["devise.facebook_data"] && data["extra"]["raw_info"]
But I'll have to check that.
Upvotes: 2
Reputation: 106430
The only necessary thing for an if
statement to be valid is a boolean expression. In this case, since =
returns the result of the assignment, what's actually being tested is the falsiness of session["devise.facebook_data"]
.
IntelliJ has a good point to lodge a complaint about code like this, as it's difficult to read without knowing a thing or two about Ruby. A recommendation would be to move that to an explicit assignment statement instead. This has the added benefit of DRYing up a reference to it twice.
class User < ActiveRecord::Base
def self.new_with_session(params, session)
super.tap do |user|
data = session["devise.facebook_data"]
if data && data["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
end
Upvotes: 9
Reputation: 2999
Ruby doesn't care about types in conditionals, unlike Java. As long as the value is neither nil or false then it will pass.
In your example you actually discriminate against nil: the if conditionnal ensures that data actually exists and isn't nil, so we can use it, assuming it's a hash. This is a common pattern in Ruby.
Upvotes: 0
Reputation: 311308
A assignment operator (=
) returns the assigned value, which is then evaluated by the if
. In ruby, only false
and nil
are considered as false
. Everything else evaluates to true
in a boolean context (like an if
).
Upvotes: 1