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?
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.
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.
"Navindra Umanee" <navindra@cs.mcgill.ca> schrieb im Newsbeitrag
news:20050207002403.A12145@cs.mcgill.ca...
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
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.
> 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.
You could write your interpolater. Here is an example:
$ 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
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
···
Robert Klemme <bob.news@gmx.net> wrote:
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
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
···
On Feb 7, 2005, at 10:05 AM, Navindra Umanee wrote:
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.
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
···
On Feb 7, 2005, at 4:00 AM, Martin DeMello wrote:
Robert Klemme <bob.news@gmx.net> wrote:
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.
> 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.
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..
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.
···
Bill Kelly <billk@cts.com> wrote:
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
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.
\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.