Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Delayed quote expansion

1 view
Skip to first unread message

Kevin Olbrich

unread,
Jan 18, 2006, 12:29:56 AM1/18/06
to
I'm sure the answer to this will seem simple once I see it. Right now
it eludes me.

How can one have a string object like this...

a = '#{some_value}'

and have it get evaluated as a double quoted string (with the value for
some_value being automatically replaced) at a later time?

I would prefer that it is evaluated in context from which it is called.

I would like to do something like this...

def func(param)
test = 42
puts param.quote_substitution
end

func('my number is #{test}') #=> "my number is 42"

_Kevin

--
Posted via http://www.ruby-forum.com/.


gwt...@mac.com

unread,
Jan 18, 2006, 1:35:16 AM1/18/06
to

On Jan 18, 2006, at 12:29 AM, Kevin Olbrich wrote:
> I would prefer that it is evaluated in context from which it is
> called.
>
> I would like to do something like this...
>
> def func(param)
> test = 42
> puts param.quote_substitution
> end
>
> func('my number is #{test}') #=> "my number is 42"


How about:

template = lambda { |x| "my number is: #{x}" }

# some time passes

template.call(42) # "my number is: 42"


Gary Wright

Eero Saynatkari

unread,
Jan 18, 2006, 2:51:30 AM1/18/06
to

You can delay evaluation like this:

string = '"#{foo}"'

# ...

value = eval string # Add a binding if you want

> _Kevin


E


Paolo Capriotti

unread,
Jan 18, 2006, 5:09:39 AM1/18/06
to
On 1/18/06, Kevin Olbrich <kevin....@duke.edu> wrote:
[snip]

> I would like to do something like this...
>
> def func(param)
> test = 42
> puts param.quote_substitution
> end
>
> func('my number is #{test}') #=> "my number is 42"

Try this:

class String
def evaluate(*args)
eval inspect.gsub(/\\#/, '#'), *args
end
end

def func(str)
x = 42
str.evaluate(binding)
end

puts func('the answer is #{x}')

--

Paolo


Kevin Olbrich

unread,
Jan 18, 2006, 7:48:11 AM1/18/06
to
Lots of good responses here, but the one that turns out to be the
easiest is...

def func(param)
some_value = 42
value = eval %Q{%Q{#{param}}}
end

func('my number is #{some_value}') #=> 'my number is 42'

James Edward Gray II

unread,
Jan 18, 2006, 8:43:09 AM1/18/06
to
On Jan 18, 2006, at 6:48 AM, Kevin Olbrich wrote:

> Lots of good responses here, but the one that turns out to be the
> easiest is...
>
> def func(param)
> some_value = 42
> value = eval %Q{%Q{#{param}}}
> end
>
> func('my number is #{some_value}') #=> 'my number is 42'

What you really want here is ERB:

>> require "erb"
=> true
>> def expand( template )
>> some_value = 42
>> ERB.new(template).result(binding)
>> end
=> nil
>> expand "My number is <%= some_value %>."
=> "My number is 42."

James Edward Gray II


Kevin Olbrich

unread,
Jan 18, 2006, 8:51:50 AM1/18/06
to
James Gray wrote:
> What you really want here is ERB:
>

A good suggestion, but I would rather avoid unnecessary dependencies if
there is an adequate pure ruby way to do it.

Bob Showalter

unread,
Jan 18, 2006, 9:06:02 AM1/18/06
to
Kevin Olbrich wrote:
> James Gray wrote:
>
>>What you really want here is ERB:
>>
>
>
> A good suggestion, but I would rather avoid unnecessary dependencies if
> there is an adequate pure ruby way to do it.

erb *is* pure Ruby. It is part of the standard library, so every Ruby
installation has it.


Kevin Olbrich

unread,
Jan 18, 2006, 11:12:57 AM1/18/06
to
Bob Showalter wrote:

> erb *is* pure Ruby. It is part of the standard library, so every Ruby
> installation has it.

So it is, my bad.

geoffj...@gmail.com

unread,
Jan 19, 2006, 4:45:14 PM1/19/06
to
Kevin Olbrich wrote:
> Lots of good responses here, but the one that turns out to be the
> easiest is...
>
> def func(param)
> some_value = 42
> value = eval %Q{%Q{#{param}}}
> end
>
> func('my number is #{some_value}') #=> 'my number is 42'

This solution has the problem that it doesn't work for:

func('{ #{1+2}')

A solution I have used in the past is:

param.gsub(/\#\{([^\}]*)\}/) { eval($1) }

but that wont work for:

param='#{"12#{1+2}4"}'

I would be very interested if anyone has a robust solution...

Andrew Johnson

unread,
Jan 20, 2006, 4:36:18 AM1/20/06
to
On 19 Jan 2006 13:45:14 -0800, geoffj...@gmail.com
<geoffj...@gmail.com> wrote:
[snip]
> but that wont work for:
>
> param='#{"12#{1+2}4"}'
>
> I would be very interested if anyone has a robust solution...


The problem is that the #{code} construct can contain arbitrary
code, including being multi-line code, with comments, other blocks,
nested strings ...

If using Oniguruma is allowed (with subexpression calling and cuts) then we
can make a bit of progress hacking up a rudimentary parser that can try to
grab #{}-like things for evaluation:

class Parse
QLIKE = %r/(?:'(?:\\'|[^'])*' |
"(?:\\"|[^"])*" |
%[qQrxw]\!(?:\\!|[^\!])*\! |
%[qQrxw]\#(?:\\\#|[^#])*\# |
%[qQrxw]\|(?:\\\||[^|])*\| |
%[qQrxw]\/(?:\\\/|[^\/])*\/ |
%[qQrxw](?<qb>\[(?:(?>\\[\]\[]|[^\[\]])|\g<qb>)*\]) |
%[qQrxw](?<qa>\<(?:(?>\\[<>]|[^<>])|\g<qa>)*\>) |
%[qQrxw](?<qc>\{(?:(?>\\[{}]|[^{}])|\g<qc>)*\}) |
%[qQrxw](?<qp>\((?:(?>\\[()]|[^()])|\g<qp>)*\)) )/x

COMMENT = %r/(?:#[^\n]*\n)/
SBLOCK = %r/(?<block>\{(?:(?>#{COMMENT} |
#{QLIKE} |
[^{}]
) |
\g<block>)*
\})/x

def Parse.expand str
test = "ple"
str.gsub!(/(?<!\\)##{SBLOCK}/){eval $1.gsub(/\A\{|\}\z/,'')}
end
end

param='#{"12#{1+2}4"}'

Parse.expand(param)
puts param

test = 42
param = 'this is \#{not} a #{"sil".succ + # blah } blah comments
(%Q!}! << lambda{?b - ?5}.call) + # }}more comments{{}
"{#{test}" # yet more (*&@QI#{J$)*DF}{Junk
} exercise' # <- string ends down here
Parse.expand(param)
puts param

__END__

results in:

1234
this is \#{not} a sim}-{ple exercise


Is this robust? It is only a small bit of required parsing (for instance,
there are more potential delimiters for quote-like expressions) -- However,
it does make failure-prediction less obvious, which some might substitute
for robustness :-)

cheers,
andrew

--
Andrew L. Johnson http://www.siaris.net/
I don't want to achieve immortality through my work; I want to
achieve immortality through not dying.
- Woody Allen

0 new messages