Interpolation question

D

Dudebot

Why does

text = <<ENDTEXT
this is an <field>wibble</field>
of stuff <field>bobble</field>
ENDTEXT

wibble = 2.0
bobble = 3.0

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

puts newtext

throw this error:
/test.rb:13: syntax error, unexpected $undefined
newtext = text.gsub( /<field>(.*?)<\/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
 
L

Luc Heinrich

newtext =3D text.gsub( /<field>(.*?)<\/field>/, "#{\1}" )

newtext =3D text.gsub( /<field>(.*?)<\/field>/, '\1' )

--=20
Luc Heinrich - (e-mail address removed)
 
D

Dudebot

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?
 
P

Paul Harrington

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{<field>(.*?)</field>}) {|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).
 
P

Paul Harrington

Paul said:
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{<field>(.*?)</field>}) {|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).
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{<field>(.*?)</field>}) {fields[$1]}
 
D

David Masover

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

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?

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>(.*?)<\/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 = "<body>#{text}</body>"
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
 
Y

yermej

Why does

text = <<ENDTEXT
this is an <field>wibble</field>
of stuff <field>bobble</field>
ENDTEXT

wibble = 2.0
bobble = 3.0

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

puts newtext

throw this error:
/test.rb:13: syntax error, unexpected $undefined
newtext = text.gsub( /<field>(.*?)<\/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

It seems that this will work:

newtext = text.gsub(/<field>(.*?)<\/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
 
D

Dudebot

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:
http://freshmeat.net/articles/templates-in-ruby 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
 

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,755
Messages
2,569,534
Members
45,007
Latest member
obedient dusk

Latest Threads

Top