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?
# 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:
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'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.
···
On 17 mayo, 01:33, Peña, Botp <b...@delmonte-phil.com> 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"
On 17 mayo, 01:33, Peña, Botp <b...@delmonte-phil.com> 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)?
> 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)?
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?
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
···
On 2007-05-17 21:27:56 +0900 (Thu, May), Aureliano Calvo 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?
--
No virus found in this outgoing message.
Checked by 'grep -i virus $MESSAGE'
Trust me.
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.
On May 17, 6:27 am, "Aureliano Calvo" <aurelianoca...@gmail.com> 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?
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.
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, "!"]]]]]]]
> 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)?
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.
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.
On 2007-05-17 21:27:56 +0900 (Thu, May), Aureliano Calvo 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
--
No virus found in this outgoing message.
Checked by 'grep -i virus $MESSAGE'
Trust me.
> 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, "!"]]]]]]]
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.
···
On Fri, May 18, 2007 at 05:05:07PM +0900, Robert Klemme wrote:
>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?
> 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?
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.
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
···
On 18.05.2007 14:49, Brian Candler wrote:
On Fri, May 18, 2007 at 05:05:07PM +0900, Robert Klemme wrote:
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?
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.