Does if call == method? - ruby

I just found out that Ruby provides no way to override boolean conversion method.(How to create an Object who act as a False in Ruby)
I am trying to create an object, and track whenever it is passed to an if condition.
However, when I executed the following piece of code, 2222222222222222 wasn't being printed.
class Null
def ==(other)
p 2222222222222222
p caller
super
end
end
null = Null.new
if null
end
Does this mean that if doesn't call ==? Is there a method that if calls in an object to evaluate the Truth-yness of it?
Alternatively, is there a way to track where if object is used for object?
I'm making a large scale change and it would be unreasonable to string search all if conditions in our code base. I'd much rather create a special object and log/alert whenever if object is used.

There are only ever two objects considered "falsey": nil and false. Everything else is truthy.
So no, if my_obj doesn't call my_obj#== nor anything else. As long as the object is neither nil nor false, it's evaluated as true.
As far as I can tell, there's no way to track if an object is "evaluated as boolean", or used in if or unless.
An equivalent of if my_obj would be:
unless my_obj.equal? nil || my_obj.equal? false
# code
end

Related

Is the order of the equality operator important in Ruby?

I have used the bcrypt library in my Ruby program. I noticed that the order of the equality operator seems to be important. Depending on which variable is left or right of the '==' I get a different result.
Here is an example program:
require 'bcrypt'
my_pw = "pw1"
puts "This is my unhashed password: #{my_pw}"
hashed_pw = BCrypt::Password.create(my_pw)
puts "This is my hashed password: #{hashed_pw}"
20.times{print"-"}
puts
puts "my_pw == hashed_pw equals:"
if (my_pw == hashed_pw)
puts "TRUE"
else
puts "FALSE"
end
puts "hashed_pw == my_pw equals:"
if (hashed_pw == my_pw)
puts "TRUE"
else
puts "FALSE"
end
Regards
schande
Yes, there is a difference.
my_pw == hashed_pw calls the == method on the my_pw string and passes hashed_pw as an argument. That means you are using the String#== method. From the docs of String#==:
string == object → true or false
Returns true if object has the same length and content; as self; false otherwise
Whereas hashed_pw == my_pw calls the == method on an instance of BCrypt::Password and passes my_pw as an argument. From the docs of BCrypt::Password#==:
#==(secret) ⇒ Object
Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
This doesn't really have anything to do with equality. This is just fundamentals of Object-Oriented Programming.
In OOP, all computation is done by objects sending messages to other objects. And one of the fundamental properties of OOP is that the receiver object and only the receiver object decides how to respond to this message. This is how encapsulation, data hiding, and abstraction are achieved in OOP.
So, if you send the message m to object a passing b as the argument, then a gets to decide how to interpret this message. If you send the message m to object b passing a as the argument, then it is b which gets to decide how to interpret this message. There is no built-in mechanism that would guarantee that a and b interpret this message the same. Only if the two objects decide to coordinate with each other, will the response actually be the same.
If you think about, it would be very weird if 2 - 3 and 3 - 2 had the same result.
That is exactly what is happening here: In the first example, you are sending the message == to my_pw, passing hashed_pw as the argument. my_pw is an instance of the String class, thus the == message will be dispatched to the String#== method. This method knows how to compare the receiver object to another String. It does, however, not know how to compare the receiver to a BCrypt::Password, which is what the class of hashed_pw is.
And if you think about it, that makes sense: BCrypt::Password is a third-party class from outside of Ruby, how could a built-in Ruby class know about something that didn't even exist at the time the String class was implemented?
In your second example, on the other hand, you are sending the message == to hashed_pw, passing my_pw as the argument. This message gets dispatched to the BCrypt::Password#== method, which does know how to compare the receiver with a String:
Method: BCrypt::Password#==
Defined in: lib/bcrypt/password.rb
#==(secret) ⇒ Object
Also known as: is_password?
Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
Actually, the problem in this particular case is even more subtle than it may at first appear.
I wrote above that String#== doesn't know what to do with a BCrypt::Password as an argument, because it only knows how to compare Strings. Well, actually BCrypt::Password inherits from String, meaning that a BCrypt::Password IS-A String, so String#== should know how to handle it!
But think about what String#== does:
string == object → true or false
Returns true if object has the same length and content; as self; false otherwise […]
Think about this: "returns true if object has the same length and content". For a hash, this will practically never be true. self will be something like 'P#$$w0rd!' and object will be something like '$2y$12$bxWYpd83lWyIr.dF62eO7.gp4ldf2hMxDofXPwdDZsnc2bCE7hN9q', so clearly, they are neither the same length nor the same content. And object cannot possibly be the same content because the whole point of a cryptographically secure password hash is that you cannot reverse it. So, even if object somehow wanted to present itself as the original password, it couldn't do it.
The only way this would work, is if String and BCrypt::Password could somehow "work together" to figure out equality.
Now, if we look close at the documentation of String#==, there is actually a way to make this work:
If object is not an instance of String but responds to to_str, then the two strings are compared using object.==.
So, if the author of BCrypt::Password had made a different design decision, then it would work:
Do not let BCrypt::Password inherit from String.
Implement BCrypt::Password#to_str. This will actually allow BCrypt::Password to be used practically interchangeably with String because any method that accepts Strings should also accept objects that respond to to_str.
Now, as per the documentation of String#==, if you write my_pw == hashed_pw, the following happens:
String#== notices that hashed_pw is not a String.
String#== notices that hashed_pw does respond to to_str.
Therefore, it will send the message object == self (which in our case is equivalent to hashed_pw == my_pw), which means we are now in the second scenario from your question, which works just fine.
Here's an example of how that would work:
class Pwd
def initialize(s)
#s = s.downcase.freeze
end
def to_str
p __callee__
#s.dup.freeze
end
def ==(other)
p __callee__, other
#s == other.downcase
end
end
p = Pwd.new('HELLO')
s = 'hello'
p == s
# :==
# "hello"
#=> true
s == p
# :==
# "hello"
#=> true
As you can see, we are getting the results we are expecting, and Pwd#== gets called both times. Also, to_str never gets called, it only gets inspected by String#==.
So, it turns out that ironically, the problem is not so much that String#== doesn't know how to deal with BCrypt::Password objects, but actually the problem is that it does know how to deal with them as generic Strings. If they weren't Strings but merely responded to to_str, then String#== would actually know to ask them for help.
Numeric objects in Ruby have a whole coercion protocol to make sure arithmetic operations between different "number-like" operand types are supported even for third-party numerics libraries.
The expressions would be equivalent if both operands were, for instance, of type String. In your case, one operand is a String and the other one is a BCrypt::Password. Therefore my_pw == hashed_pw invokes the equality method defined in the String class, while hashed_pw == my_pw invokes the one defined in BCrypt::Password.
I have never worked with BCrypt::Password, but would expect that you get false for the former and true for the latter.
In Ruby you can override the equality method for a given class or instance:
class Test
define_method(:==) do |_other|
true
end
end
Test.new == 'foo' # => true
Test.new == nil # => true
Test.new == 42 # => true
'foo' == Test.new # => false
nil == Test.new # => false
42 == Test.new # => true
Generally speaking, it's considered bad practice to override equality without making it symetric, but you sometimes see it in the wild.

Ruby defined?( 42[0][:foo] ) && defined?( 93[0]["bar"] ) == true. Why?

Short story:
"Why does defined?(59[0][:whatever]) evaluate to true?"
Long story:
I came across some strange behaviour lately which threw me off.
I was developing a method that did some washing of the data:
#Me washing input data:
def foo(data)
unless data && defined?(data[0]) && defined?(data[0][:some_param])
method2(data[0][:some_param])
else
freak_out()
end
end
I usually write tests where I spew in all kinds of junk data to make sure nothing strange happens:
describe "nice description" do
it "does not call method2 on junk data" do
expect(some_subject).to_not receive(:method2)
random_junk_data_array.each do |junk|
some_subject.foo(junk)
end
end
end
Well, method2 was called here. It happened when junk was a fixnum.
I am using ruby 2.1.0, and I can see that Fixnum has a #[] method which fetches the bit at that position, nice.
But why is fixnum[0][:some_param] considered to be defined?
defined? expression tests whether or not expression refers to anything recognizable (literal object, local variable that has been initialized, method name visible from the current scope, etc.). The return value is nil if the expression cannot be resolved. Otherwise, the return value provides information about the expression.
Let me explain with an example :-
defined?("a") # => "expression"
# this returns "method", as there is a method defined on the class String. So, the
# method invocation is possible, and this is a method call, thus returning `"method"`.
defined?("a".ord) # => "method"
# it return nil as there is no method `foo` defined on `"a"`,
# so the call is not possible.
defined?("a".foo) # => nil
Now coming to your point :-
As you said data[0] gives a Fixnum instance, and of-course Fixnum#[] exist. Thus fixnum_instance[:some_param] is also a valid method call. It just testing if the method is defined or not. If defined, it will tell yes this is a "method" expression. Otherwise nil. Not actually will check if the method call succeeded or not.
In Ruby all objects has truthy values except nil and false, thus "method" being a string object also has the truthy value, thus your condition got succeeded.

`non-object-ness` of `nil` in ruby

From one of the online resource of Ruby I found the below statement:
The special object nil is, indeed, an object (it’s the only instance of a class called
NilClass). But in practice, it’s also a kind of non-object. The boolean value of nil is
false, but that’s just the start of its non-object-ness.
While nil responds to method calls as below,like other objects,what non-objectness author tried to say :
nil.to_s #=> ""
nil.to_i #=> 0
nil.object_id #=> 4
nil.inspect #=> "nil"
Can anyone help me here to understand the philosophy - non-object-ness of nil ?
nil is equivalent with null in other languages. Usually, null should not treated as a sane value.
However - as you may noticed - the syntax of Ruby language does everything over the method calls on objects, a lot more things than Python. Determining a sanity of values is a part of it.
Consider the following example
def foobar(arg)
if arg < 1
return nil
else
return "Oh, hi!"
end
end
res = foobar(rand(2))
puts res unless res.nil?
As you see, in the last line I check the nil-ness of the result with calling a nil? method. This is a most effective way to do it, because comparation operators can be overloaded and can do a very different things. The nil? returns with true only if the value can be treated as nil (usually, if the value is nil - but nil? method is overridable too, even if it is highly discouraged. Developers usually are not override this method).
Another useful property of nil it is has a to_s method, so you can x = "#{nil} and it results an empty string.
If nil weren't be an object, you cannot call nil? or other useful functions on that, but you can faced with a NullPointerException like in Java or a segmentation fault like in C/C++. And usually it is pointless.

Simple Detect Solution Causing Difficulties

I have the following code, which is supposed to provide a simple true-false wrapper over Array#detect, which is nil-element.
class Array
def any &expr
if (self.detect expr)
return true
else
return false
end
end
end
For some weird reason, no matter what is passed to &expr, it ALWAYS returns true! Why is this?
The documentation for Enumerable#detect says that it can optionally take one argument. If it doesn't find the element that matched your block, it returns this argument. In your case, you're passing a Proc object, expr to detect, and not passing a block. This causes detect to return an enumerator, which won't be interpreted as a "falsy" value.
I think instead you want self.detect &expr to pass an actual block instead of a Proc.

Double ampersand in Ruby

I am using the authlogic gem with Ruby on Rails, and I have been using the following to obtain the id of the user that is currently logged in:
current_user = UserSession.find
id = current_user && current_user.record.id
I'm not understanding how current_user && current_user.record.id returns the current user id. I would think this would return a boolean. Can someone explain how this works?
There is no Boolean type in Ruby; Ruby has a rather simple view of truth (or more precisely, it has a rather simple view of falsehood).
the false object, which is the singleton instance of FalseClass is considered falsy
the nil object, which is the singleton instance of NilClass is falsy
every other object is truthy (including, obviously, the true object, which is the singleton instance of TrueClass)
[BTW: this means that a lot of objects that are considered falsy in some other languages, are actually truthy in Ruby, like the integer 0, the real value 0.0, the empty string, the empty array, the empty hash, the character 'F']
So, the Boolean operators &&, ||, and and or do not return Boolean values. Instead they return the first object that determines the outcome of the expression.
(They are also short-circuiting, which means that they only evaluate the minimum sub-expressions that are needed to determine the outcome of the expression. So, an alternate formulation would be that they return the result of the last expression that was evaluated. Which, in turn, is analogous to what blocks, methods, class bodies and module bodies do.)
So, what does it mean to return the first object that determines the outcome? That's simple, really: the result of the expression
a && b
is truthy if both a and b are truthy, otherwise it is falsy. So, if a is falsy, it is completely irrelevant what b is: the result will be falsy either way. So, we might just as well simply return a. (Remember, a doesn't have to be false, it could also be nil and the programmer might want to know which one of the two it was.)
If, OTOH, a is truthy (IOW it is neither nil nor false), then the result of the whole expression is solely dependent on b: if b is truthy, the whole result will be truthy, otherwise if b is falsy, the whole result will be falsy. So, we might just as well return b itself, instead of first converting it to a Boolean.
|| and or are analogous or more precisely dual to && and and.
You posted this example:
id = current_user && current_user.record.id
Here, the author isn't even expecting current_user to be a Boolean value! Instead, he expects it to be either a User or nil. But that's perfectly fine, because a User is truthy and nil is falsy, so they still can be used in a Boolean expression.
The basic intention is that the author wants to prevent a NoMethodError exception being raised, if he tries to call #record on nil.
An alternative way of expressing this would be
id = current_user.record.id unless current_user.nil?
If you want all the gory details, check out Section 11.1 (page 36) of the Draft ISO Ruby Specification or the excutable specifications of the RubySpec project. (Here's the one for &&.)
I wrote a pure Ruby implementation of Ruby's Boolean operators and conditional expressions once for fun. The meat of the implementations is these two mixins.
The logical and is short circuiting. That means that if the construct is X && Y, and X is false then Y never gets checked because the whole thing is certainly going to be yield false.
That code is saying, essentially:
if (current_user is TRUE) {
id = current_user.record.id;
]
Here's some console output showing you get the second value if the first is true:
irb(main):005:0> true && 9
=> 9
and nil if the first is nil:
irb(main):008:0> nil && 9
=> nil

Resources