Have me a conundrum.
k = Class.new() do
class X; end
end
k::X.object_id
results in
(irb):7: warning: toplevel constant X referenced by #<Class:
0x00000001954380>::X
I don’t want X to be toplevel. I want it to be under k
. To
complicate matters I don’t have control over the block, as it actually
comes from a testing procedure defined by an end user. The actual code
is this:
# Create a sub-case.
#
def context(desc=nil, &block)
cls = Class.new(TestCase, &block)
cls.desc(desc) if desc
cls
end
Is there any way to isolate X to k? And then apply it to the more
general dynamic case?
Thanks.
You could do this:
k = Class.new do
class_eval(“class X; end”)
end
puts k::X.object_id
On Jul 5, 10:45am, Mike B. [email protected] wrote:
You could do this:
k = Class.new do
class_eval(“class X; end”)
end
puts k::X.object_id
Thanks, but unfortunately that only works b/c it’s evaluating a string
and not a block.
I tried this thinking it should do the trick:
Class.new do
instance_eval(&block)
end
But no go
Thomas S. wrote in post #1009163:
Have me a conundrum.
k = Class.new() do
class X; end
end
k::X.object_id
results in
(irb):7: warning: toplevel constant X referenced by #<Class:
0x00000001954380>::X
I don’t want X to be toplevel. I want it to be under k
.
As I understand it, constants are scoped statically, i.e. at parse time,
unless you force them to be dynamically looked up (such as self::X)
To
complicate matters I don’t have control over the block, as it actually
comes from a testing procedure defined by an end user.
If the user is doing something like this:
your_code {
class X; end
}
then I believe they are explicitly requesting X to be created at the top
level, or whatever module they are lexically within, e.g.
module Foo
your_code {
class X; end # note
}
end
[note] is parsed as Foo::X, and I don’t think you can override this,
because the block is build before your code is called.
string eval is fine because you’re parsing the code from scratch, rather
than taking an already-built block.
def context(&block)
before_constants = self.class.constants
k = Class.new(&block)
after_constants = self.class.constants
new_constants = after_constants - before_constants
new_constants.each do |const|
k.const_set(const, k.const_get(const) )
end
Object.class_eval do
new_constants.each do |const|
remove_const(const)
end
end
k::X.greet
puts k::PI
puts Object::X
end
context do
PI = 3.14
class X
def self.greet
puts ‘hello’
end
end
end
–output:–
hello
3.14
ruby.rb:20:in context': uninitialized constant X (NameError) from ruby.rb:23:in
’
Entering the block scope does not automatically change the current
context for constant definition (the core developers call it the
`cref’), nor does instance_eval and class_eval.
In other words, blocks don’t change the scope, and that is why you can
do things like this:
x = 10
[1,2,3].each do |num|
puts num * x
end
–output:–
10
20
30
Or:
x = 10
Object.class_eval do
define_method(‘show’) do
puts x
end
end
show()
–output:–
10
On the other hand, the keywords ‘class’, ‘module’, and ‘def’ create new
scopes, and in the case of ‘class’ and ‘module’ they also create new
namespaces.
On 7/5/2011 10:15 AM, Intransition wrote:
0x00000001954380>::X
[…]
Is there any way to isolate X to k? And then apply it to the more
general dynamic case?
Thanks.
You can use Module#const_set.
const_set :X, Class.new
Entering the block scope does not automatically change the current
context for constant definition (the core developers call it the
`cref’), nor does instance_eval and class_eval.
7stud – wrote in post #1009251:
context do
PI = 3.14
class X
def self.greet
puts ‘hello’
end
end
end
That is the code that is causing the problem. If the user writes that
code, then the “current class” when X is parsed is Object(i.e. its the
same “current class” that exists outside the block). Furthermore,
class definitions nest inside the current class/module.
For constant lookups, it doesn’t matter what the current class is when
the the “do block” actually executes.
Whoops. Let’s try that again:
def context(&block)
before_constants = self.class.constants
k = Class.new(&block)
after_constants = self.class.constants
new_constants = after_constants - before_constants
Object.class_eval do
new_constants.each do |const|
k.const_set(const, const_get(const) )
remove_const(const) #private method, so need self=Object
end
end
k::X.greet
puts k::PI
puts Object::X
end
context do
PI = 3.14
class X
def self.greet
puts ‘hello’
end
end
end
–output:–
hello
3.14
ruby.rb:17:in context': uninitialized constant X (NameError) from ruby.rb:20:in
’
On Jul 5, 7:57pm, Su Zhang [email protected] wrote:
Entering the block scope does not automatically change the current
context for constant definition (the core developers call it the
`cref’), nor does instance_eval and class_eval.
I’ve been reading up on this[1]. Looks like the was a period (1.9.1)
when it didn’t work this way. I am inclined to think it a bug. At the
very least there has to be work around.
[1]http://jfire.posterous.com/constant-lookup-in-ruby