Delayed quote expansion

K

Kevin Olbrich

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
 
G

gwtmp01

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
 
E

Eero Saynatkari

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"

You can delay evaluation like this:

string = '"#{foo}"'

# ...

value = eval string # Add a binding if you want


E
 
P

Paolo Capriotti

On 1/18/06 said:
I would like to do something like this...

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

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

Try this:

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

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

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

Kevin Olbrich

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'


_Kevin
 
J

James Edward Gray II

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:
=> "My number is 42."

James Edward Gray II
 
K

Kevin Olbrich

James said:
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.

_Kevin
 
B

Bob Showalter

Kevin said:
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.
 
G

geoffjacobsen

Kevin said:
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...
 
A

Andrew Johnson

On 19 Jan 2006 13:45:14 -0800, (e-mail address removed)
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
 

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

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top