Need help with with Binding.of_caller

S

Sascha Ebach

Hi,

(hoping that Florian is reading)

I am just in the process of patching ri18n[1] interpolation
functionality. Here is what I a trying to do:

ri18n offers a way to interpolate strings. This is for translation
purposes. The current version of the interpolation method looks like this:

class String
def interpolate(caller)
caller.instance_eval('"' << self.gsub('"', '\"') << '"')
end
...
end

original version is here:

http://svn.berlios.de/viewcvs/ri18n/trunk/lib/ri18n/standard_exts.rb?view=markup

This will correctly interpolate the following:

@color = 'red'
_i('#{@color} whine')

but not this:

color = 'red'
_i('#{color} whine')

So I thought I might try to use the Binding.of_caller technique. I came
as far as this:

class String
def interpolate(caller) # the param is not needed anymore
Binding.of_caller do |binding|
eval('"' << self.gsub('"', '\"') << '"', binding)
end
end
...
end

which will do exactly the opposite. Now I am able to eval local
variables, but not the instance vars.

this doesn't work:

@color = 'red'
_i('#{@color} whine')

but this does:

color = 'red'
_i('#{color} whine')

What do I need to do to capture the whole environment of the originating
_i() call. Is it at all possible?

Sascha Ebach

[1] http://ri18n.berlios.de/
 
F

Florian Groß

Sascha said:
(hoping that Florian is reading)

If in doubt CC, but I try to keep up with ruby-talk. (Not true for the
Rails list, though -- I tried creating a gmane RSS filter for my
identity so I wouldn't miss anything, but so far it seems not to have
worked.)
So I thought I might try to use the Binding.of_caller technique. I came
as far as this:

class String
def interpolate(caller) # the param is not needed anymore
Binding.of_caller do |binding|
eval('"' << self.gsub('"', '\"') << '"', binding)
end
end
...
end

which will do exactly the opposite. Now I am able to eval local
variables, but not the instance vars.

Trust me: I'm as shocked as you are.

I think I actually stumbled upon this behavior before but quickly
displaced the knowledge from my memory.

I think this is related to Ruby not setting the .self field of the BLOCK
struct for trace_func bindings. From what I see binding() has some extra
logic which might do exactly this.

Don't take my guess at this too literally -- the code for this is fairly
complex and I will not claim to understand how all parts of it work
together, but I think this might be a bug or a known limitation.

You're only chance of really finding out is if you get lucky and Nobu or
ts or someone else with deeper Ruby knowledge accidently reads all this.

Which would actually be cool because then I would know if this is by
design or not. Let's wait a bit and see if anyone bites. I will want to
eventually document this so an explanation would be very cool.
What do I need to do to capture the whole environment of the originating
_i() call. Is it at all possible?

It's a good question and I would like to say that coding interpolation
is evil, but it might make sense here.

Perhaps you can make interpolate a method on Kernel -- then you would be
able to do interpolate(@foo) and the self of the interpolate method and
the one of the caller would always be the same thus avoiding the whole
situation. Does this sound reasonable?
 
S

Sascha Ebach

Thanks for the fast response Florian.
Perhaps you can make interpolate a method on Kernel -- then you would b= e=20
able to do interpolate(@foo) and the self of the interpolate method and= =20
the one of the caller would always be the same thus avoiding the whole=20
situation. Does this sound reasonable?

I am not sure anymore if it is worth going through all the trouble. The=20
whole solution is a little evally. First it is a little hard to=20
understand, like you say yourself. Next there is the question of=20
performance. As I understand eval is not very fast. And this is a method=20
which is potentially being called very often. A Binding.of_caller=20
solution would certainly be the most elegant, but faster and more=20
reliable would be if the method would just do some stringfy thing.

_i('%s whine', @color)

or maybe to further simplify

_i(':color_of whine', :color_of =3D> @color)

which should be easier to work with (but less flexible) for the translato=
rs.

_Since I am not the author of the ri18n, he seems to be on a vacation at=20
the moment, I have to wait and talk to him first. But I am going to=20
suggest using the above instead of the eval magic. Unless of course some=20
of the real wizards come up with a magical and fast solution. Chances=20
are slim I guess.

Let's see.

Sascha Ebach
 

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

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top