tags:

views:

101

answers:

3

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.

+1  A: 

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]))
amphetamachine
A good general thing to try in any Ruby situation with those and/ors. This has been the solution to 90% of my conditional problems.
Justin L.
I just remember from Perl that `or` has a lower precedence than `||` and `=`. Wrote code flooded with parentheses until I figured that out.
amphetamachine
+5  A: 

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]
molf
Operator precedence was the issue in my version, but I attempted to use your case version and I'm still getting _nil_ returned. The method arguments satisfy the first when-condition (`image = Rubygame::Surface.new([640, 480])`), but if you omit the argument to a case statement, what is it comparing against?
echoback
The way `case` is used here, without argument, simply tests each expression after `when` for truthiness. It stops when it finds the first truthy value. In the example I gave, the only way to get `nil` assigned to `@image`, is if `Rubygame::Surface.autoload` or `Rubygame::Surface.new` return `nil` in a particular situation.
molf
A: 

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

banister