2

Here's a question to bamboozle your noodle.

It seems there's a difference between method declaration and constant declaration in a block that is evaluated using class_eval with a class as the receiver. Here's some code that demonstrates the discrepancy:

module X
  def save_block(&block)
    @block = block
  end
end

module Y
  extend X
  save_block do
    SOME_CONSTANT = 1
    def meth
      "a method"
    end
  end
  def self.included(m)
    m.class_eval &@block
  end
end

class Z
  include Y
end

class W
  include Y
end

Executing this code, at least in Ruby 1.9.3, results in the following error:

warning: already initialized constant SOME_CONSTANT

Which I didn't expect. Consider first where meth is located:

Z.instance_methods(false)
=> [:meth]

W.instance_methods(false)
=> [:meth]

And Y doesn't have any instance methods:

Y.instance_methods(false)
=> []

This makes sense because the block is executed with Z and W as the receivers of class_eval. However, the constants are different, hence the error message, as shown here:

Y.constants(false)
=> [:SOME_CONSTANT]

Z.constants(false) 
=> []

W.constants(false)
=> []

Here, the constant ends up getting defined in Y (for some bizarre reason) and thus when the block is executed a second time, the constant, SOME_CONSTANT is already defined.

I very much look forward to some enlightenment.

Update (2012/11/19):

As @phoet pointed out below (the the response to my comment to his answer), constants are defined in the lexical scope of the block, that is to say, in the scope where the block is originally defined. In my example, this would be Y.

Kevin Bullaughey
  • 2,556
  • 25
  • 37

1 Answers1

1

as described in this question Accessing a constant constant handling is within it's "lexical scope", meaning the context they are defined, not the context they are executed.

Community
  • 1
  • 1
phoet
  • 18,688
  • 4
  • 46
  • 74
  • Constants are definitely not created at parase time. For example, if I execute `Y.constants(false)` before Z or W are defined, but after Y is defined, then I get the empty array `[]`. The block must be evaluated for the constant to be defined. I'm just not sure why the constant gets attached to Y instead of Z and W individually. – Kevin Bullaughey Nov 20 '12 at 20:26
  • 1
    yeah, you are right. it's about scoping: http://stackoverflow.com/questions/10078744/accessing-a-constant – phoet Nov 20 '12 at 21:24
  • If you update your answer, to reflect this, I'll accept it (I already added a note to my original question). – Kevin Bullaughey Nov 20 '12 at 21:56