delayed string interpolation

  • Thread starter Navindra Umanee
  • Start date
N

Navindra Umanee

Hi,

I'm generating an HTML header as a String that makes heavy use of
interpolation. Essentially I have something like:

def make_html_header()
str <<END
blah #{blah} blah #{blah} blah #{blah}
END
end

This works great. Most of the interpolations are essentially
configuration parameters that I've elegantly stuffed into the instance
variables of a Singleton class, some are method invocations. These
parameters don't change often, if at all, and the header is more or
less identical for each HTML page that is generated.

So I would like to generate the header once and stuff it in an
instance variable. However, there are one or two variable
interpolations that are unique to each page e.g. the title of the
page.

How can I specify that some interpolations should be interpolated
immediately whilst a few others should be delayed until the next time
the String is evaluated? I can think of some hacky ways i.e. doing
subsequent string processing and substitions, I'm just wondering if
there is a more elegant way.

Also, where is the "<<MARKER" syntax (nothing in ri?) and Ruby string
interpolation officially documented, if anywhere?

Thanks,
Navin.
 
N

Navindra Umanee

the String is evaluated? I can think of some hacky ways i.e. doing
subsequent string processing and substitions, I'm just wondering if
there is a more elegant way.

To add to that, I thought about leaving a few "%s" in the string and
replacing them later, but that too seems ugly because I have feed in
variables in order and sometimes even repeatedly.

Thanks,
Navin.
 
B

Bill Kelly

From: "Navindra Umanee said:
I'm generating an HTML header as a String that makes heavy use of
interpolation. Essentially I have something like:

def make_html_header()
str <<END
blah #{blah} blah #{blah} blah #{blah}
END
end

This works great. Most of the interpolations are essentially
configuration parameters that I've elegantly stuffed into the instance
variables of a Singleton class, some are method invocations. These
parameters don't change often, if at all, and the header is more or
less identical for each HTML page that is generated.

So I would like to generate the header once and stuff it in an
instance variable. However, there are one or two variable
interpolations that are unique to each page e.g. the title of the
page.

How can I specify that some interpolations should be interpolated
immediately whilst a few others should be delayed until the next time
the String is evaluated? I can think of some hacky ways i.e. doing
subsequent string processing and substitions, I'm just wondering if
there is a more elegant way.

I dunno if this counts as elegant or not, :)
but a couple years ago, I used:

class String
def reeval(b = TOPLEVEL_BINDING)
eval(%Q{<<"END_REEVAL"\n} + self + "\nEND_REEVAL\n", b)
end
end

in similar circumstances. I would just put a backslash
in front of any interpolations I wanted to defer. Then
I'd call reeval at some later point at run-time. I would
also pass in a binding, so that my (deferred) interpolations
would have access to instance methods of the class performing
the reeval.

I'd described it in more detail in:
http://ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/59979
(the external links in that post no longer work, sorry.)


HTH,

Regards,

Bill
 
C

Charles Mills

Navindra said:
To add to that, I thought about leaving a few "%s" in the string and
replacing them later, but that too seems ugly because I have feed in
variables in order and sometimes even repeatedly.

You could write your interpolater. Here is an example:

$ irb
irb(main):001:0> require 'tmp.rb'
=> true
irb(main):002:0> s = ":hey :day"
=> ":hey :day"
irb(main):003:0> s.interpolate:)hey => "hello", :day=>"yo")
=> "hello yo"

$ cat tmp.rb
class String
def interpolate(hash)
self.gsub(/:(\w+)/) { hash[$1.to_sym] }
end
def interpolate!(hash)
self.gsub!(/:(\w+)/) { hash[$1.to_sym] }
end
end

$

-Charlie
 
R

Robert Klemme

Navindra Umanee said:
Hi,

I'm generating an HTML header as a String that makes heavy use of
interpolation. Essentially I have something like:

def make_html_header()
str <<END
blah #{blah} blah #{blah} blah #{blah}
END
end

This works great. Most of the interpolations are essentially
configuration parameters that I've elegantly stuffed into the instance
variables of a Singleton class, some are method invocations. These
parameters don't change often, if at all, and the header is more or
less identical for each HTML page that is generated.

So I would like to generate the header once and stuff it in an
instance variable. However, there are one or two variable
interpolations that are unique to each page e.g. the title of the
page.

How can I specify that some interpolations should be interpolated
immediately whilst a few others should be delayed until the next time
the String is evaluated? I can think of some hacky ways i.e. doing
subsequent string processing and substitions, I'm just wondering if
there is a more elegant way.

One option is two levels of interpolation:

generator = "my gen"
application = "my test app"
header = "\"<html>.....#{generator} .... #{application} .... \#{title}\""
....
title = "foo"
real_header = eval header

Or you create a lambda for that:

def create_header(generator, application)
eval "lambda {|title| \"<html>.....#{generator} .... #{application} ....
\#{title}\" }"
end
=> said:
Also, where is the "<<MARKER" syntax (nothing in ri?) and Ruby string
interpolation officially documented, if anywhere?

http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_stdtypes.html#S2

Kind regards

robert
 
M

Martin DeMello

Robert Klemme said:
One option is two levels of interpolation:

generator = "my gen"
application = "my test app"
header = "\"<html>.....#{generator} .... #{application} .... \#{title}\""
...
title = "foo"
real_header = eval header

By this point I'd have switched to a proper template engine, just to
make sure the eval didn't bring in some hairy corner cases (am I being
overly paranoid?) I was going to suggest this earlier, in fact, but I
felt amrita was probably a bit too heavy for this case.

martin
 
J

James Edward Gray II

By this point I'd have switched to a proper template engine, just to
make sure the eval didn't bring in some hairy corner cases (am I being
overly paranoid?) I was going to suggest this earlier, in fact, but I
felt amrita was probably a bit too heavy for this case.

This was my first thought when reading the original message as well.
Might want to have a look at erb in the standard library.

James Edward Gray II
 
N

Navindra Umanee

Hi,

Thanks a lot for both eval solutions (exactly what I was looking for)
and the template suggestion.

I still have nightmares about Zope's DTML, so I wasn't sure I wanted
to go with templating this time. I think as I encounter more and more
issues however I might have to consider to a templating solution
afterall.

Of the two templating solutions suggested so far, Amrita seems too
indirect for my tastes. I'd rather directly code what my HTML looks
like than have it be the result of some fancy/unknown
computation... Not to mention that's just going to come back and bite
me when the implementation changes (or doesn't change).

As for ERB, I'm not yet sure how it's better or easier than the simple
2-level string eval I wanted to do. It seems pretty much the same
except with different enclosing tags and more overhead. ERB::Util has
some interesting subroutines though. I'll have to look more closely
to understand the advantages.

Thanks,
Navin.
 
N

Navindra Umanee

Bill Kelly said:
I dunno if this counts as elegant or not, :)
but a couple years ago, I used:

class String
def reeval(b = TOPLEVEL_BINDING)
eval(%Q{<<"END_REEVAL"\n} + self + "\nEND_REEVAL\n", b)
end
end

I have to say this is such a really neat hack... it says so much
about Ruby too.

I had a hard time understanding it until I realised that the first
END_REEVAL doesn't need to be in quotes. Writing it as:

eval("<<END_REEVAL\n" + self + "\nEND_REEVAL\n", b)

seems to work just as well. Any reason you did it that way?

Cheers,
Navin.
 
B

Bill Kelly

From: "Navindra Umanee said:
I have to say this is such a really neat hack... it says so much
about Ruby too.

Thanks! And, indeed: thanks Matz !! =D
I had a hard time understanding it until I realised that the first
END_REEVAL doesn't need to be in quotes. Writing it as:

eval("<<END_REEVAL\n" + self + "\nEND_REEVAL\n", b)

seems to work just as well. Any reason you did it that way?

I'm guessing I got into the habit early on to remind
myself that ruby lets you specify single-quote semantics
if you want them, like:

x = <<'ENDTEXT'
now things like \n are literal
as they would be in 'single quote' strings
ENDTEXT

So I probably used <<"END_REEVAL" trying to be specific
about my request for double-quote semantics.... However
it seems to have just become a habit. :) Maybe I'll
stop doing it... Hehe..


Regards,

Bill
 
N

Navindra Umanee

Bill Kelly said:
I'm guessing I got into the habit early on to remind
myself that ruby lets you specify single-quote semantics
if you want them, like:

x = <<'ENDTEXT'
now things like \n are literal
as they would be in 'single quote' strings
ENDTEXT

Oh, hmph. I didn't even know that. It's not really documented is it?

I didn't even realise \n would be converted to a newline in heredoc as
well. Going to have to be really really careful about things like
user-input, I guess.

Thanks for the useful information. :)

Cheers,
Navin.
 
Z

Zach Dennis

Navindra said:
Oh, hmph. I didn't even know that. It's not really documented is it?

I didn't even realise \n would be converted to a newline in heredoc as
well. Going to have to be really really careful about things like
user-input, I guess.

Thanks for the useful information. :)

\n will only be converted it you allow string expansion,

#a bunch of blank lines
x = <<"ENDTEXT"
\n\n\n\n\n
ENDTEXT

#a bunch of literal \n 's
x = <<'ENDTEXT'
\n\n\n\n\n
ENDTEXT

Note the double-quotes compared to the single quotes.

Zach
 
N

Navindra Umanee

Zach Dennis said:
#a bunch of blank lines
x = <<"ENDTEXT"
\n\n\n\n\n
ENDTEXT

#a bunch of literal \n 's
x = <<'ENDTEXT'
\n\n\n\n\n
ENDTEXT

Well, I'm using heredoc because I don't want to specify \n and the
such. The string is specified and looks almost exactly as the document.

But I *do* want #{} expansion. This is the way it pretty much works
on bash heredoc by default.

Cheers,
Navin.
 
J

James Edward Gray II

As for ERB, I'm not yet sure how it's better or easier than the simple
2-level string eval I wanted to do. It seems pretty much the same
except with different enclosing tags and more overhead. ERB::Util has
some interesting subroutines though. I'll have to look more closely
to understand the advantages.

Sorry, I missed this message when you posted it.

I was thinking you would Heredoc a erb template using normal #{ ... }
interpolation for the things you wanted to calculate up front and erb
<%= ... %> place holders for the bits to fill in later. Then you could
just generate the template repeatedly as needed.

Hope that clears up my idea, though I'm sure you have a solution by now.

James Edward Gray II
 

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

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,040
Latest member
papereejit

Latest Threads

Top