Simple 'or' isn't working as expected

159 views Asked by At

I'm running into an interesting issue with the relatively simple assignment below. Each of the parenthesized chunks in the beginning evaluate to nil, leaving Rubygame::Surface.new as the value that @image ought to be assigned to. Unfortunately on the next line where I set @rect, it throws a NoMethodError because @image is nil.

@image = (image unless image.nil?) or 
         (Rubygame::Surface.autoload(image_file) unless image_file.nil?) or 
         (Rubygame::Surface.autoload("#{@name}.png") unless @name.nil?) or 
         Rubygame::Surface.new([16, 16])
@rect = Rubygame::Rect.new [0, 0], [@image.width, @image.height]

Similar tests run through IRB work as expected, so I'm pretty sure the 'or' statement is well-formed, but I can't figure out why it isn't returning the new Surface when everything else is nil.

3

There are 3 answers

2
molf On BEST ANSWER

The or and and keywords in Ruby have very, very low precedence. Even lower than the assignment operator =. So simply replace them with || and && respectively (both binding tighter than =), and it should work as you expect. Ruby's operator precedence is listed here.

In addition to that, I would say your code is very dense. Consider refactoring it to something like the following, which I think conveys the intent of your code much better.

@image = case
  when image then image
  when image_file then Rubygame::Surface.autoload(image_file)
  when @name then Rubygame::Surface.autoload("#{@name}.png")
  else Rubygame::Surface.new([16, 16])
end

@rect = Rubygame::Rect.new [0, 0], [@image.width, @image.height]
0
horseyguy On

Why are you using RubyGame? The Gosu game development framework for Ruby is faster and more popular.

2
amphetamachine On

Have you tried further levels of parentheses?

@image = ((image unless image.nil?) or 
         (Rubygame::Surface.autoload(image_file) unless image_file.nil?) or 
         (Rubygame::Surface.autoload("#{@name}.png") unless @name.nil?) or 
         Rubygame::Surface.new([16, 16]))