I try to compare flags in my Ruby application.
I have this code :
if self.flag &~ flag == self.flag
return false
But it won't run. I've narrowed the problem to this :
irb(main):020:0> my_user.flag
=> 1
irb(main):021:0> flag
=> 128
irb(main):022:0> my_user.flag.class
=> Fixnum
irb(main):023:0> flag.class
=> Fixnum
irb(main):024:0> my_user.flag &~ flag
TypeError: wrong argument type Fixnum (expected Proc)
That is really disturbing since it works like this :
irb(main):025:0> 1 &~ 128
=> 1
The difference between 1 &~ 128
and my_user.flag &~ flag
is that the second expression involves a dot-method call. That changes the way the subsequent tokens are interpreted.
Try this:
# works
my_user.flag() &~ flag
# also works
(my_user.flag) &~ flag
# best
my_user.flag & ~flag
You'll find that it works. This is because adding ()
or moving ~
changes the order of operations to be more in line with what you expected.
The original method call you're using is actually interpreted by Ruby as:
# bad
my_user.flag(&(~flag))
This order of operations first flips the bits in flag
by applying the ~
operator, then attempts to call to_proc
on the resulting Fixnum
because of the application of the &
(cast-as-block) operator, and then finally attempts (had that not thrown a TypeError
) to pass it as a block argument to the User#flag
method.