Coopting String interpolation

We all know the #{} operator for String interpolation.
It’s used like this:

name=“Aure”
greeting=“hi, #{name}”

and greeting evaluates to “hi, Aure”. But I would like to change it.
Is there a pure Ruby way to force “hi, #{name}” to evaluate to “hi,
AURE” (get the uppercase of the original interpolated String).
Something like:

class String

do some magic here

end

name=“Aure”
greeting=“hi, #{name}”

and now greeting evaluates to “hi, AURE”. Is there a method in String
to override in order to take control of the String interpolation?

Thanks in advance,
Aureliano.

irb(main):005:0> greeting="hi, #{"Ms “+name.capitalize}”

=> “hi, Ms Aure”

sorry, was too fast on the last example; should be…

irb(main):006:0> greeting="hi, #{name=“Anne”;"Ms “+name.capitalize}”
=> “hi, Ms Anne”

On 17 mayo, 01:33, Peña, Botp [email protected] wrote:

irb(main):005:0> greeting="hi, #{"Ms “+name.capitalize}”

=> “hi, Ms Aure”

sorry, was too fast on the last example; should be…

irb(main):006:0> greeting="hi, #{name=“Anne”;"Ms “+name.capitalize}”
=> “hi, Ms Anne”

I’m trying to do some metaprogramming and I need to apply some
operation to all the strings that are interpolated. But I can’t change
the interpolation. In the example, I would like that “hi, #{name}” to
evaluate to “hi, AURE” instead of “hi, Aure”. I know that inside the
#{} “operator” I can put any ruby code, so “hi, #{name.capitalize}”
would evaluate to what I want.

But I want it to execute code that I DON’T write there. Ideally, there
should be a method hook or something to change the way the
interpolation works. But I couldn’t find it :(. May be a different
example may be more clear. How can I do to write to a file all the
strings generated via interpolations (id est, all the strings that are
generated evaluating the different #{} “operators” in a program)?

Thanks,
Aureliano.

Hi,

At Thu, 17 May 2007 13:20:14 +0900,
aurelianito wrote in [ruby-talk:251879]:

class String

do some magic here

end

name=“Aure”
greeting=“hi, #{name}”

and now greeting evaluates to “hi, AURE”. Is there a method in String
to override in order to take control of the String interpolation?

If the value isn’t a String, to_s is called for it to get a
String. Otherwise, the content is interpolated directly.
There is not a hook you want.

From: aurelianito [mailto:[email protected]] :

Is there a pure Ruby way to force “hi, #{name}” to evaluate to "hi,

AURE" (get the uppercase of the original interpolated String).

Something like:

pols. think simple, then just do it.

interpolation can be any expression…

irb(main):001:0> name=“Aure”
=> “Aure”
irb(main):002:0> greeting=“hi, #{name}”
=> “hi, Aure”
irb(main):003:0> greeting=“hi, #{name.upcase}”
=> “hi, AURE”
irb(main):005:0> greeting="hi, #{"Ms “+name.capitalize}”
=> “hi, Ms Aure”
irb(main):006:0>

On 17.05.2007 07:09, aurelianito wrote:

I’m trying to do some metaprogramming and I need to apply some
strings generated via interpolations (id est, all the strings that are
generated evaluating the different #{} “operators” in a program)?

Frankly, it has not become clear to me what you are up to. Can you
maybe just state which problem you are trying to solve?

As for a solution to your original example, you can do something like
this - but it’s a hack and not very reliable:

class Env
def initialize(vals)
@vals = vals
end

def process(&b)
instance_eval(&b)
end

def method_missing(s,*a,&b)
super unless a.empty?
@vals[s.to_sym].upcase
end
end

irb(main):015:0> e=Env.new(:name=>‘foo’)
=> #<Env:0x7ff6d388 @vals={:name=>“foo”}>
irb(main):016:0> e.process { “bar #{name}” }
=> “bar FOO”
irb(main):017:0>

Kind regards

robert

example may be more clear. How can I do to write to a file all the
strings generated via interpolations (id est, all the strings that are
generated evaluating the different #{} “operators” in a program)?

Frankly, it has not become clear to me what you are up to. Can you
maybe just state which problem you are trying to solve?

NDAs are a bitch. I can’t state the exact problem (bah! I can, but I
could be fired, in a trial, and I also might inhibit my coworkers to
publish a paper with their findings). I’m working in a research
project and I need to do some “clever stuff” (that’s the thing I can’t
disclose) to all the Strings. This thing is “different” depending on
how strings are composed. All the ways I know for string composition
but string interpolation (<<, +, concat, gsub, etc.) can be overridden
redefining methods in the string class. I’m looking for a way to
intercept the string expansion to do my thing. Does ruby internally
call some overridable method to compose the strings used in a
interpolation?

Now I see that if I can transform all the <“a#{expression}c”> in <“a”

  • (expression) + “c”> in a ruby source code string, I could change
    expression with “a#{expression}c” in “a” + clever_stuff(expression) +
    “c” and use my changed + method in Strings. Is there an easy way to
    manipulate ruby code in ruby to do this?

Thanks for your time,
Aureliano.

On 2007-05-17 21:27:56 +0900 (Thu, May), Aureliano C. wrote:

Now I see that if I can transform all the <“a#{expression}c”> in <“a”

  • (expression) + “c”> in a ruby source code string, I could change
    expression with “a#{expression}c” in “a” + clever_stuff(expression) +
    “c” and use my changed + method in Strings. Is there an easy way to
    manipulate ruby code in ruby to do this?

Maybe this will be of some use:

irb(main):005:0> “asd_#{raise caller.to_s}_”
RuntimeError: /usr/lib/ruby/1.8/irb/workspace.rb:52:in
`irb_binding’/usr/lib/ruby/1.8/irb/workspace.rb:52
from (irb):5

RuntimeError: /usr/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding’/usr/lib/ruby/1.8/irb/workspace.rb:52
from (irb):5

Oh, no - it’s stupid. Sorry :wink:

On May 17, 6:27 am, “Aureliano C.” [email protected]
wrote:

Now I see that if I can transform all the <“a#{expression}c”> in <“a”

  • (expression) + “c”> in a ruby source code string, I could change
    expression with “a#{expression}c” in “a” + clever_stuff(expression) +
    “c” and use my changed + method in Strings. Is there an easy way to
    manipulate ruby code in ruby to do this?

Can you make life easier by changing to use ERB?
You cannot change Ruby string interpolation in Ruby. You can hack the
source code if you really need to. You can manually change your
strings, or you can (as below) use gsub to change the string before
evaluating.

def clever_stuff( str )
str.upcase
end

require ‘erb’
def clever_template( str )
ERB.new( str.gsub( /<%=(.+?)%>/, ‘<
%=clever_stuff(\1)%>’ ) ).result
end

name1, name2 = %w|Gavin K.|

str1 = “Hello there, <%=name1%> <%=name2%>! Nice to meet you!”
puts clever_template( str1 )
#=> Hello there, GAVIN KISTNER! Nice to meet you!

str2 = “Hello there, <%=name1 + ’ ’ + name2%>! Nice to meet you!”
puts clever_template( str2 )
#=> Hello there, GAVIN KISTNER! Nice to meet you!

Aureliano C. wrote:

Now I see that if I can transform all the <“a#{expression}c”> in <“a”

  • (expression) + “c”> in a ruby source code string, I could change
    expression with “a#{expression}c” in “a” + clever_stuff(expression) +
    “c” and use my changed + method in Strings. Is there an easy way to
    manipulate ruby code in ruby to do this?

Maybe you can take a look at ParseTree to decompose a dynamic string
into its components:

require “rubygems”
require “parse_tree”
class Foo
def foo
“interpolation: #{42}!”
end
end
ParseTree.new.parse_tree(Foo)
=> [[:class, :Foo, [:const, :Object], [:defn, :foo, [:scope, [:block,
[:args], [:dstr, "interpolation: ", [:lit, 42], [:str, “!”]]]]]]]

Daniel

require “parse_tree”
class Foo
def foo
“interpolation: #{42}!”
end
end
ParseTree.new.parse_tree(Foo)
=> [[:class, :Foo, [:const, :Object], [:defn, :foo, [:scope, [:block,
[:args], [:dstr, "interpolation: ", [:lit, 42], [:str, “!”]]]]]]]

Thanks!
I’ll take a look at it.

Aureliano C. schrieb:

Now I see that if I can transform all the <“a#{expression}c”> in <“a”

  • (expression) + “c”> in a ruby source code string, I could change
    expression with “a#{expression}c” in “a” + clever_stuff(expression) +
    “c” and use my changed + method in Strings. Is there an easy way to
    manipulate ruby code in ruby to do this?

Aureliano, is it right that you don’t need to manipulate the code at
runtime, and you can work with the source code instead? This can be more
or less hard to do, depending on the complexity of the expressions which
are allowed inside the strings. For simple expressions a regex solution
might be enough. Otherwise you’d need to use one of the Ruby parser
libraries.

If this is for a research project, then maybe you could patch the Ruby
interpreter to add the hook you need. This shouldn’t be too difficult.

Regards,
Pit

On May 16, 2007, at 9:20 PM, aurelianito wrote:

We all know the #{} operator for String interpolation.
It’s used like this:

name=“Aure”
greeting=“hi, #{name}”

and greeting evaluates to “hi, Aure”. But I would like to change it.
Is there a pure Ruby way to force “hi, #{name}” to evaluate to “hi,
AURE” (get the uppercase of the original interpolated String).

I just released my Jig package that might be helpful to you.

require ‘rubygems’
require ‘jig’

Create a named ‘gap’ that upcases everything

upcase = Jig::Gap.new(:name) { |x| x.upcase }

Create a jig consisting of a string, and the customize ‘upcasing’ gap.

greeting = Jig["hi, ", upcase]

Fill the gap with data to generate a new jig.

puts calls Jig#to_s to convert the new jig to a string

puts greeting % { :name => “Aure” } # => hi, AURE
puts greeting % { :name => “Gary” } # => hi, GARY

On 17.05.2007 14:27, Aureliano C. wrote:

example may be more clear. How can I do to write to a file all the
strings generated via interpolations (id est, all the strings that are
generated evaluating the different #{} “operators” in a program)?

Frankly, it has not become clear to me what you are up to. Can you
maybe just state which problem you are trying to solve?

NDAs are a bitch. I can’t state the exact problem (bah! I can, but I
could be fired, in a trial, and I also might inhibit my coworkers to
publish a paper with their findings).

You can tell us - we won’t tell anybody else. :wink:

I’m working in a research
project and I need to do some “clever stuff” (that’s the thing I can’t
disclose) to all the Strings. This thing is “different” depending on
how strings are composed. All the ways I know for string composition
but string interpolation (<<, +, concat, gsub, etc.) can be overridden
redefining methods in the string class. I’m looking for a way to
intercept the string expansion to do my thing. Does ruby internally
call some overridable method to compose the strings used in a
interpolation?

Likely but also likely not accessible to pure Ruby code. But in your
case (research project) it might be ok to hack the interpreter if you
need to catch all string interpolations. Did you look into this yet?

Now I see that if I can transform all the <“a#{expression}c”> in <“a”

  • (expression) + “c”> in a ruby source code string, I could change
    expression with “a#{expression}c” in “a” + clever_stuff(expression) +
    “c” and use my changed + method in Strings. Is there an easy way to
    manipulate ruby code in ruby to do this?

You could even change all “a#{b}c” into “a#{your_magic b}c” which is
considerably easier to do than a complete parse. But when I think about
it it may break for some b’s. Whether that’s ok or not, I don’t know.

Kind regards

robert

On Fri, May 18, 2007 at 05:05:07PM +0900, Robert K. wrote:

Likely but also likely not accessible to pure Ruby code. But in your
case (research project) it might be ok to hack the interpreter if you
need to catch all string interpolations. Did you look into this yet?

Here’s a simpler idea. Given that string interpolation calls to_s, you
can
use this as your hook if you wrap your objects in a proxy object. You
need
to ensure that operations like ‘+’ also return an instance of this proxy
object, so you can do all the operations you want whilst keeping the
string
wrapped. Then the only case that to_s is called is when the
interpolation
takes place, which gives you the hook you’re looking for (as long as
you’re
not calling to_s in any other context)

Example:

class WrapString
def initialize(str)
@str = str
end

def str
@str
end
protected :str

def +(other)
self.class.new(str + (other.str rescue other.to_s))
end

rinse and repeat

Here is your interpolation hook:

def to_s
str.to_s * 2
end
end

a = WrapString.new(“abc”)
b = WrapString.new(“def”)

puts “Answer is #{a + b}”

a = WrapString.new(“abc”)
b = “def”

puts “Answer is #{a + b}”

The result of a + b is <WrapString @str=“abcdef”>, and then the
interpolation calls to_s which in this case just doubles it to
“abcdefabcdef”

There’s probably a neater implementation of the above using
method_missing
or a delegation pattern rather than enumerating all the methods of
String,
but you get the idea.

Just a thought?

Brian.

Likely but also likely not accessible to pure Ruby code. But in your
case (research project) it might be ok to hack the interpreter if you
need to catch all string interpolations. Did you look into this yet?

I’ll see if I can get away with it. I’ll provide a patch to have an
interpolation hook if I manage to implement it.

Thanks,
Aureliano.

On 18.05.2007 14:49, Brian C. wrote:

Likely but also likely not accessible to pure Ruby code. But in your

protected :str
end

The result of a + b is <WrapString @str=“abcdef”>, and then the
interpolation calls to_s which in this case just doubles it to
“abcdefabcdef”

There’s probably a neater implementation of the above using method_missing
or a delegation pattern rather than enumerating all the methods of String,
but you get the idea.

Just a thought?

Not bad! Question is: what does it do to the rest of the code? Just
think about using obj.to_s for Hash keys. Also, I am not sure what
happens in core code if to_s returns something other than a string:

15:51:07 [~]: irb
irb(main):001:0> o=Object.new
=> #Object:0x7ef9e3f8
irb(main):002:0> puts o
#Object:0x7ef9e3f8
=> nil
irb(main):003:0> def o.to_s() “foo” end
=> nil
irb(main):004:0> puts o
foo
=> nil
irb(main):005:0> def o.to_s() 123 end
=> nil
irb(main):006:0> puts o
#Object:0x7ef9e3f8
=> nil

Seems to be there could be wired effects…

Kind regards

robert