Interpolation question

Why does

text = <<ENDTEXT
this is an wibble
of stuff bobble
ENDTEXT

wibble = 2.0
bobble = 3.0

newtext = text.gsub( /(.*?)</field>/, “#{\1}” )

puts newtext

throw this error:
./test.rb:13: syntax error, unexpected $undefined
newtext = text.gsub( /(.*?)</field>/, “#{\1}” )

and not interpolate to produce
this is an 2.0
of stuff 3.0

?

And how can I do that interpolation? (adding \s to \1, e.g. \1
doesn’t help)

Many TIA,
Craig

On 27 févr. 2010, at 19:58, Dudebot wrote:

newtext = text.gsub( /(.*?)</field>/, “#{\1}” )

newtext = text.gsub( /(.*?)</field>/, ‘\1’ )

Thanks, Luc, but that’s not what I need. That produces

this is an wibble
of stuff bobble

instead of

this is an 2.0
of stuff 3.0

I need it to interpolate the value of the expressions, not the
expressions themselves. Is this not possible in Ruby?

First, don’t top-post (like this). It’s annoying.

On Saturday 27 February 2010 01:25:02 pm Dudebot wrote:

I need it to interpolate the value of the expressions, not the
expressions themselves. Is this not possible in Ruby?

It’s certainly possible, but not with gsub. Think about what you’re
asking.:

newtext = text.gsub( some_regex, some_string )

By the time gsub sees those arguments, both some_regex and some_string
will
have been evaluated. That is, the standard Ruby string interpolation
will have
already resolved. As an example:

count = 0
newtext = text.gsub( /(.*?)</field>/, “#{count+=1}” )

You’ll just get 1, not 1,2 as you might expect. Think about why that
happens.

It happens that way because by the time gsub gets called, “#{count+=1}”
has
already been replaced by the results of evaluating that expression, and
is now
just a string.

What you want is to replace it with the results of evaluating a block,
which
is an entirely different animal. It’s possible gsub can do that, but
before I
provide any kind of solution there, are you sure this is how you want to
do
this? Because it looks like you’re trying to use regexes to parse XML.

DO NOT USE REGEX TO PARSE XML.

It is the wrong tool for the job. There are a billion special-cases in
XML,
let alone HTML. It may be possible to write a correct regex, but you
won’t be
able to – nor would I want to try. There are already dozens of
libraries to
do this for you, it’s a thoroughly-solved problem, and there’s no good
reason
to try to do it again yourself… I’d suggest Nokogiri.

Once you’ve actually parsed it, the solution is much easier:

It’s not valid XML without a root element.

It doesn’t have to be called body, but it has to exist.

text = “#{text}”
doc = Nokogiri.parse(text)
(doc / ‘field’).each{|e| e.content = eval e.text}

Of course, the use of ‘eval’ should scare you. Your original
implementation,
and this one, is ridiculously insecure. Don’t use local variables for
this!
(That comes up about as often on this list as people trying to use
regexes to
parse XML, and it makes about as much sense.) Instead, use a hash:

h = {‘wibble’ => 2.0, ‘bobble’ => 3.0}
(doc / ‘field’).each{|e| e.content = h[e.text]}

And to turn it back into XML:

doc.to_s

Paul H. wrote:

Seeeeems like you’re looking for eval.

Of course, this mean you absolutely trust your input.

Alternatively, nothing stops you from just storing the values in a hash,
doing something likke

fields = {“wibble” => 2.0, “bobble” => 3.0}
newtext = text.gsub(%r{(.*?)}) {|value| fields[value]}

Still this assumes that you absolutely know your input will always be
in this exact format, otherwise (for example if you’re not the person
generating the input) use an actual XML parser (like Nokogiri).

Dudebot wrote:

Thanks, Luc, but that’s not what I need. That produces

this is an wibble
of stuff bobble

instead of

this is an 2.0
of stuff 3.0

I need it to interpolate the value of the expressions, not the
expressions themselves. Is this not possible in Ruby?

Wow let me try that post again.

$1, $2, etc are the capture globals; the value passed to the block
passed to gsub is the match itself.

So you want text.gsub(%r{(.*?)}) {fields[$1]}

On Feb 27, 12:58 pm, Dudebot [email protected] wrote:

newtext = text.gsub( /(.*?)</field>/, “#{\1}” )

?

And how can I do that interpolation? (adding \s to \1, e.g. \1
doesn’t help)

Many TIA,
Craig

It seems that this will work:

newtext = text.gsub(/(.*?)</field>/) {|match| eval $1}

but I can’t explain why what you attempted didn’t work. Hopefully
somebody else can explain what’s going on.

Jeremy

Regarding my question about interpolation (sorry, David, for top-
posting, blame Google for making it the default, and me for being
lazy) thanks super Paul and David for the detailed posts, and Jeremy
for the stab at it. I found a nice write-up on Freshmeat:
Best Open Source Mac Software Development Software 2023 Hope this helps those
who may be browsing in the future for this kind of solution.

Point taken, Paul, about security. Only trusted users are allowed
this functionality in my app.

Thanks again, all

Seeeeems like you’re looking for eval.

Of course, this mean you absolutely trust your input.

Alternatively, nothing stops you from just storing the values in a hash,
doing something likke

fields = {“wibble” => 2.0, “bobble” => 3.0}
newtext = text.gsub(%r{(.*?)}) {|value| fields[value]}

Still this assumes that you absolutely know your input will always be
in this exact format, otherwise (for example if you’re not the person
generating the input) use an actual XML parser (like Nokogiri).

Dudebot wrote:

Thanks, Luc, but that’s not what I need. That produces

this is an wibble
of stuff bobble

instead of

this is an 2.0
of stuff 3.0

I need it to interpolate the value of the expressions, not the
expressions themselves. Is this not possible in Ruby?