[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

binding - how to explain it?

Giles Bowkett

8/2/2007 5:22:00 PM

There was a thread here recently about this idiom in Rails:

eval(IO.read(file), binding, file)

I changed it to a method:

def eval_file_with_binding(file)
eval(IO.read(file), binding, file)
end

It appeared twice in Rails, so refactoring it was kind of overkill,
but I have code OCD. Plus, the thread on this list came from somebody
trying to puzzle out what the code did, and it's very repetitive code,
so I wanted to make it simpler and clearer.

Anyway, the tests pass, but the patch hasn't been applied, because
there's a question raised, which is why does it send the correct
binding when it's defined in some other method. On the one hand the
answer seems obvious, because it's a method on Kernel, but honestly I
can't understand it as well as I think I do, because I can't explain
it beyond that "obvious" point. How do I explain how this code works?
How does this code work? I know it works, but I can't actually explain
it.

--
Giles Bowkett

Blog: http://gilesbowkett.bl...
Portfolio: http://www.gilesg...

13 Answers

David A. Black

8/2/2007 5:32:00 PM

0

Giles Bowkett

8/2/2007 6:10:00 PM

0

> Can you produce a complete working example? I'm having trouble
> getting it not to not work.... Here's what I'm doing so far:

If you have edge Rails, you can use this diff:

http://dev.rubyonrails.org/attachment/ticket/9128/eval_fil...

And then run these tests:

railties/test/plugin_load_test.rb
railties/test/initializer_test.rb

In terms of the code in your mail:

> a = 1
>
> def x
> eval(DATA.read, binding)
> end
>
> x # error: a undefined
>
> __END__
> puts a
>
>
> Is that analogous to what's in the original?

I think it is, and I can't get that to run either. But then why do the
tests pass?

I'm doing something very similar:

<macbook of doom:giles> [08-02 10:58] ~
! irb
>> def eval_var_with_binding(var)
>> eval(var, binding)
>> end
=> nil
>> a = :xyz
=> :xyz
>> eval_var_with_binding("puts a")
NameError: undefined local variable or method `a' for #<Object:0x349f4>
from (irb):2:in `eval_var_with_binding'
from (irb):2:in `eval_var_with_binding'
from (irb):5

The only thing that's different is the third arg to eval(). e.g.:

> eval(IO.read(file), binding, file)

vs.

> eval(var, binding)

But it turns out that passing the filename as a third arg only affects
error reporting, according to the Pickaxe.

Here's something else from the Pickaxe:

> When using eval, it can be helpful to state explicitly the context in
> which the expression should be evaluated, rather than using the
> current context. You can obtain a context by calling Kernel#binding
> at the desired point.
>
> def get_a_binding
> val = 123
> binding
> end
>
> val = "cat"
> the_binding = get_a_binding
>
> eval("val", the_binding) # 123
> eval("val") # "cat"
>
> The first eval evaluates val in the context of the binding as it
> was as the method get_a_binding was executing. In this binding,
> the variable val had a value of 123. The second eval evaluates
> val in the toplevel binding, where it has the value "cat".

If the tests are passing but the binding doesn't operate as I expected
it to, I think this may mean that the binding method is entirely
unnecssary at this point, and only passed at all so that you can also
pass the third arg and get the file-specific error-reporting.

--
Giles Bowkett

Blog: http://gilesbowkett.bl...
Portfolio: http://www.gilesg...

ara.t.howard

8/2/2007 6:44:00 PM

0


On Aug 2, 2007, at 12:09 PM, Giles Bowkett wrote:

>
> I think it is, and I can't get that to run either. But then why do the
> tests pass?
>

the obvious answer is that the original code did nothing. i've
looked at that very code in the rails base and my perception, as with
a lot of the rails base, is that it's superfluous and simply cruft
left over from something. i could easily be wrong - but the fact
that your tests are passing is a strong indicator...

cheers.

a @ http://draw...
--
we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama




ara.t.howard

8/2/2007 6:47:00 PM

0


On Aug 2, 2007, at 12:09 PM, Giles Bowkett wrote:

> If the tests are passing but the binding doesn't operate as I expected
> it to, I think this may mean that the binding method is entirely
> unnecssary at this point, and only passed at all so that you can also
> pass the third arg and get the file-specific error-reporting.
>

i think that's correct - at least that's how i read it a few weeks
ago. what's really interesting then is that

eval IO.read(path), Kernel.binding, path

is pretty much exactly

load path

only so much more exotic ;-)

a @ http://draw...
--
we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama




Giles Bowkett

8/2/2007 6:54:00 PM

0

> ago. what's really interesting then is that
>
> eval IO.read(path), Kernel.binding, path
>
> is pretty much exactly
>
> load path
>
> only so much more exotic ;-)

I think I'll see what happens if I make that change. do the tests blow
up, or is it really that simple? stay tuned for the next exciting
adventure blah blah blah.

--
Giles Bowkett

Blog: http://gilesbowkett.bl...
Portfolio: http://www.gilesg...

ara.t.howard

8/2/2007 7:27:00 PM

0


On Aug 2, 2007, at 12:54 PM, Giles Bowkett wrote:

> I think I'll see what happens if I make that change. do the tests blow
> up, or is it really that simple? stay tuned for the next exciting
> adventure blah blah blah.

yeah - it's surely complicated by the fact that rails has overidden
Kernel.load too... i'll be interested to see what happens...


a @ http://draw...
--
we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama




Giles Bowkett

8/2/2007 9:37:00 PM

0

> > I think I'll see what happens if I make that change. do the tests blow
> > up, or is it really that simple? stay tuned for the next exciting
> > adventure blah blah blah.
>
> yeah - it's surely complicated by the fact that rails has overidden
> Kernel.load too... i'll be interested to see what happens...

Hmm, I didn't know that. The initializer test passes but the plugin
loader test explodes.

--
Giles Bowkett

Blog: http://gilesbowkett.bl...
Portfolio: http://www.gilesg...

Ezra Zygmuntowicz

8/2/2007 9:38:00 PM

0


On Aug 2, 2007, at 12:27 PM, ara.t.howard wrote:

>
> On Aug 2, 2007, at 12:54 PM, Giles Bowkett wrote:
>
>> I think I'll see what happens if I make that change. do the tests
>> blow
>> up, or is it really that simple? stay tuned for the next exciting
>> adventure blah blah blah.
>
> yeah - it's surely complicated by the fact that rails has overidden
> Kernel.load too... i'll be interested to see what happens...
>
>
> a @ http://draw...
> --
> we can deny everything, except that we have the possibility of
> being better. simply reflect on that.
> h.h. the 14th dalai lama


Rails uses that eval(IO.read(...), binding) on the init.rb of
plugins so that you have the config local variable defined so you can
use it just like you use config.blah = whatever in the
Rails::Initializer block in environment.rb. As far as I know that's
the only reason it exists.

So when you wrap that call in a method without passing in the
binding, you defeat the purpose of this hack. If you did this instead
the tests will still pass and the right binding will be used:

# Change this:
def eval_file_with_binding(file)
eval(IO.read(file), binding, file)
end

# To this:
def eval_file_with_binding(file, bind=binding)
eval(IO.read(file), bind, file)
end

Cheers-
-- Ezra Zygmuntowicz
-- Founder & Ruby Hacker
-- ez@engineyard.com
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)



Morton Goldberg

8/3/2007 2:07:00 AM

0

On Aug 2, 2007, at 1:32 PM, dblack@rubypal.com wrote:

> Can you produce a complete working example? I'm having trouble
> getting it not to not work.... Here's what I'm doing so far:
>
> a = 1
>
> def x
> eval(DATA.read, binding)
> end
>
> x # error: a undefined
>
> __END__
> puts a
>
>
> Is that analogous to what's in the original?

I'm kind of late to this party, but is it relevant that each of the
following works?

<code>
a = 1

def x
eval(DATA.read, TOPLEVEL_BINDING)
end

x

__END__
puts a
</code>

<code>
def x(a)
eval(DATA.read, binding)
end

x(42)

__END__
puts a
</code>

Regards, Morton

Giles Bowkett

8/3/2007 9:39:00 PM

0

Hi Morton - actually, that is entirely relevant. I think it indicates
that you get the binding from the calling context, rather than the
defining context. But it seems to contradict what David Black posted
earlier, and I saw the same thing in my own irb.

I posted Ezra's version in the Rails ticket, but I think the whole
thing's gotten so confusing that the ticket might not necessarily go
anywhere. :-
It's a pity, I think refactoring Rails is a pretty worthwhile task. I
think they took out a lot of the method_missing stuff because it was
too resource-intensive. (Actually that reminds me of an error message
I wanted to change.)

--
Giles Bowkett

Blog: http://gilesbowkett.bl...
Portfolio: http://www.gilesg...


On 8/2/07, Morton Goldberg <m_goldberg@ameritech.net> wrote:
> On Aug 2, 2007, at 1:32 PM, dblack@rubypal.com wrote:
>
> > Can you produce a complete working example? I'm having trouble
> > getting it not to not work.... Here's what I'm doing so far:
> >
> > a = 1
> >
> > def x
> > eval(DATA.read, binding)
> > end
> >
> > x # error: a undefined
> >
> > __END__
> > puts a
> >
> >
> > Is that analogous to what's in the original?
>
> I'm kind of late to this party, but is it relevant that each of the
> following works?
>
> <code>
> a = 1
>
> def x
> eval(DATA.read, TOPLEVEL_BINDING)
> end
>
> x
>
> __END__
> puts a
> </code>
>
> <code>
> def x(a)
> eval(DATA.read, binding)
> end
>
> x(42)
>
> __END__
> puts a
> </code>
>
> Regards, Morton
>
>