wersimmon
wersimmon

Reputation: 2869

Simple 'or' isn't working as expected

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.

Upvotes: 1

Views: 161

Answers (3)

horseyguy
horseyguy

Reputation: 29915

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

Upvotes: -1

molf
molf

Reputation: 74985

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]

Upvotes: 5

amphetamachine
amphetamachine

Reputation: 30623

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]))

Upvotes: 1

Related Questions