Where is the magic?


I

Ilya Lopatkin

I'm learning Ruby and have found some interesting behaviour which I
cann't understand myself yet.
Here's some code and the output:
class Foo
def self.new(*args)
obj = super
yield
obj
end
end

Foo.new do
p self
end

Struct.new("Foo", :foo, :bar) do
p self
end

=> main
=> Struct::Foo

The first output is quite understandable because self is bounded to the
top-level class of the file and is sent to the closure.
But in the second line it seems that scope of self has been changed
somehow.
I've had a fast look on the source file struct.c and haven't found
something special in the new method.
The same thing can also be found in the QtRuby bindings:
require 'Qt4'

Qt::Application.new(ARGV) do
Qt::Widget.new do

self.window_title = 'Hello QtRuby v1.0'
resize(200, 100)

button = Qt::pushButton.new('Quit') do
connect(SIGNAL :clicked) { Qt::Application.instance.quit }
end

label = Qt::Label.new('<big>Hello Qt in the Ruby way!</big>')

self.layout = Qt::VBoxLayout.new do
add_widget(label, 0, Qt::AlignCenter)
add_widget(button, 0, Qt::AlignRight)
end

show
end

exec
end

Please, could anybody explain me how does this work. Links will do too.
:)
 
Ad

Advertisements

R

Robert Klemme

I'm learning Ruby and have found some interesting behaviour which I
cann't understand myself yet.
Here's some code and the output:
class Foo
def self.new(*args)
obj = super
yield
obj
end
end

Foo.new do
p self
end

Struct.new("Foo", :foo, :bar) do
p self
end

=> main
=> Struct::Foo

The first output is quite understandable because self is bounded to the
top-level class of the file and is sent to the closure.
But in the second line it seems that scope of self has been changed
somehow.
I've had a fast look on the source file struct.c and haven't found
something special in the new method.
The same thing can also be found in the QtRuby bindings:
require 'Qt4'

Qt::Application.new(ARGV) do
Qt::Widget.new do

self.window_title = 'Hello QtRuby v1.0'
resize(200, 100)

button = Qt::pushButton.new('Quit') do
connect(SIGNAL :clicked) { Qt::Application.instance.quit }
end

label = Qt::Label.new('<big>Hello Qt in the Ruby way!</big>')

self.layout = Qt::VBoxLayout.new do
add_widget(label, 0, Qt::AlignCenter)
add_widget(button, 0, Qt::AlignRight)
end

show
end

exec
end

Please, could anybody explain me how does this work. Links will do too.
:)

Struct.new uses instance_eval internally:

irb(main):001:0> o = Object.new
=> #<Object:0x9a93150>
irb(main):002:0> o.instance_eval { p self }
#<Object:0x9a93150>
=> #<Object:0x9a93150>
irb(main):003:0> p self
main
=> main
irb(main):004:0>

An example more close to what Struct does:

irb(main):001:0> class Foo
irb(main):002:1> def initialize(&b)
irb(main):003:2> printf "in init: %p\n", self
irb(main):004:2> instance_eval(&b)
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> Foo.new { printf "in block: %p\n", self }
in init: #<Foo:0x8e742e8>
in block: #<Foo:0x8e742e8>
=> #<Foo:0x8e742e8>

Kind regards

robert
 
Ad

Advertisements

A

Albert Schlef

Ilya said:
Thanks for your response. I've almost grasped the idea. :)

Here's an explanation is other words:

When you create a block you create a parcel that contains three things:

- The code itself.
- The "binding" (links to the surrounding variables).
- The value of 'self' (this is how ruby knows which object to
invoke methods on).

When you normally 'yield' to a block, the block uses the value of 'self'
saved in that parcel. On the other hand, when you do
'somebody.eval_instance()', the 'self' in that parcel is set to
'somebody'.

It is common in GUI frameworks to use eval_instance() to make the code
look like DSL (google for "DSL").
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top