0

I don't know why these two pieces of code behave different in Ruby 1.8.7 since one seems to be the single line version of the other.

The first piece of code (it works as it should):

if @type.present?
  type = @type
  orders = Order.where{type.eq(type)}
end

The single line version (it doesn't work at all, no error but seems no execution too):

orders = Order.where{type.eq(type)} if (type = @type).present?

NOTE: I'm using the squeel gem, that is the reason a block follows the where method. Also the variable type has to capture the instance variable @type since the execution context changes inside the block and the instance variables are not shared between the main context and the block context.

NOTE 2: I have to use Ruby 1.8.7 for legacy reasons.

Any idea? Thank you!

dguti
  • 1

3 Answers3

5

There is a problem with the order of parsing of your code. Variables need to be defined before they are used.

Even though variables defined inside if statement clauses "leak" out into the current scope, they do not leak "backwards" in Ruby code.

Ruby is a little bit curious in that way that variables need to be defined before the parser parses the code. The parsing is done from top to bottom and left to right.

Hence since the variable type is defined after your block code where you use it, it will not be available in the block.

Example:

>> 3.times { puts x } if (x = 123)
NameError: undefined local variable or method `x' for main:Object

The reason you don't get any error message is that in Ruby 1.8 type is a method that is a synonym for Object#class.

So what your code is really doing is (probably):

orders = Order.where{type.eq(this.class)} if (type = @type).present?

To fix it you have to define type before you use it. Therefore you can't really turn that into a one-liner unless you simply do this instead:

orders = Order.where{type.eq(@type)} if @type.present?

All in all it's not a good idea in Ruby 1.8 to use type as a variable in Rails models, because of the Object#class issue it will most likely bring you headaches in the long run.

Casper
  • 33,403
  • 4
  • 84
  • 79
  • The issue is not variable **scope**. The issue is the **order of evaluation**. Your first sentence is misleading. – sawa Jul 26 '13 at 09:03
  • @sawa - Hmm. I didn't think of it like that. You're right. But is it really order of evaluation, or order of parsing? – Casper Jul 26 '13 at 09:04
  • It's interaction of both. Maybe order of parsing may be more appropriate. – sawa Jul 26 '13 at 09:05
  • @sawa Y. Would need to dig into Ruby source to really find out I guess. Corrected the post. Thx. – Casper Jul 26 '13 at 09:11
  • 1
    @sawa Actually. It must be parsing order, because execution order is definitely the `if` statement before the block. – Casper Jul 26 '13 at 09:14
0

similar problem that i got was that 'type' is a keyword in database, it allows for our model to have the field as 'type' but it works strangely in different conditions. if changing the name is an option for you then check after changing it, worked for me...

dirtydexter
  • 1,063
  • 1
  • 10
  • 17
0

gem Squeel uses instance_eval method when calls block which passed to where. So, there is no any @type in squeel instance. If you want to use methods or variables from another context, try to wrap it into method my with block

orders = Order.where { type.eq my { @type } } if @type.present?

PS sorry for my English

Semjon
  • 983
  • 6
  • 17