delaying string evaluation

N

Navindra Umanee

Hi,

I would like to partially expand a string and then further expand it
later on when more information is available. I am currently using
string interpolation with a reeval method suggested by Bill Kelly.

class String
def reeval(b = TOPLEVEL_BINDING)
eval("<<END_REEVAL\n" + self + "\nEND_REEVAL\n", b).chomp!
end
end

This lets me do stuff like this:

expanded = "one"
delayed = "two"

s = "#{expanded} \#{delayed}" # => "one #{delayed}"
s = s.reeval(binding) # => "one two"

As a more complex example, this is a string I wish to partially expand:

options = [ 10, 20, 30, 40, 50 ]

combobox =<<"END"
...
#{options.map {|n|
%{<option value="#{n}"\#{thread_threshold == n ? ' SELECTED' : ''}>#{n} comments</option>}
}.join("\n")
}
...
END

The idea is to create the combobox and then later insert the SELECTED
part, when I know the value of thread_threshold. Unfortunately, the
above partial expansion is:

<option value="10"#{thread_threshold == n ? ' SELECTED' : ''}>10 comments</option>
<option value="20"#{thread_threshold == n ? ' SELECTED' : ''}>20 comments</option>
<option value="30"#{thread_threshold == n ? ' SELECTED' : ''}>30 comments</option>
<option value="40"#{thread_threshold == n ? ' SELECTED' : ''}>40 comments</option>
<option value="50"#{thread_threshold == n ? ' SELECTED' : ''}>50 comments</option>

This would have been great, except that I have all these values for
'n' that have been left out of the expressions that I want to evaluate
afterwards. That is, I want the partial expansion to be this:

<option value="10"#{thread_threshold == 10 ? ' SELECTED' : ''}>10 comments</option>
<option value="20"#{thread_threshold == 20 ? ' SELECTED' : ''}>20 comments</option>
<option value="30"#{thread_threshold == 30 ? ' SELECTED' : ''}>30 comments</option>
<option value="40"#{thread_threshold == 40 ? ' SELECTED' : ''}>40 comments</option>
<option value="50"#{thread_threshold == 50 ? ' SELECTED' : ''}>50 comments</option>

and later when I reeval the string:

thread_threshold = 50
combobox.reeval(binding)

the result should be:

<option value="10">10 comments</option>
<option value="20">20 comments</option>
<option value="30">30 comments</option>
<option value="40">40 comments</option>
<option value="50" SELECTED>50 comments</option>

Anyone have any ideas for how I can get a correct partial expansion?

I have a lambda/closure feeling about how this should work, but I
can't quite see the solution in the context of strings.

Thanks,
Navin.
 
E

Eric Hodel

Hi,

I would like to partially expand a string and then further expand it
later on when more information is available. I am currently using
string interpolation with a reeval method suggested by Bill Kelly.

is sprintf (aka %) unable to fulfill your requirements?
class String
def reeval(b = TOPLEVEL_BINDING)
eval("<<END_REEVAL\n" + self + "\nEND_REEVAL\n", b).chomp!
end
end

This lets me do stuff like this:

expanded = "one"
delayed = "two"

s = "#{expanded} \#{delayed}" # => "one #{delayed}"
s = s.reeval(binding) # => "one two"

s = "#{expanded} %s"
s = s % delayed
As a more complex example, this is a string I wish to partially
expand:

options = [ 10, 20, 30, 40, 50 ]

combobox =<<"END"
...
#{options.map {|n|
%{<option value="#{n}"\#{thread_threshold == n ? ' SELECTED' : ''}
#{n} comments</option>}
}.join("\n")
}
...
END

combobox = <<END
...
%s
...
END

opts = options.map { |n| "<option value=\"#{n}\"%s>#{n} comments</
option>" }.join "\n"

combobox = combobox % opts
and later when I reeval the string:

thread_threshold = 50
combobox.reeval(binding)

the result should be:

<option value="10">10 comments</option>
<option value="20">20 comments</option>
<option value="30">30 comments</option>
<option value="40">40 comments</option>
<option value="50" SELECTED>50 comments</option>

Hrm, maybe not...

How about:

opts = options.map { |n| "<option value=\"#{n}\"SEL_#{n}>#{n}
comments</option>" }.join "\n"

combobox = combobox % opts

combobox.gsub!(/SEL_(\d+)/) do
thread_threshold == $1.to_i ? ' SELECTED' : ''
end
Anyone have any ideas for how I can get a correct partial expansion?

I have a lambda/closure feeling about how this should work, but I
can't quite see the solution in the context of strings.

I think the reeval thing is the wrong way to go about it. I find it
much more useful to use a library that generates HTML programatically
or from a template than to try fancy substitution tricks (unless its
really small). Eventually you end up with either a template language
or programatic HTML generation in the end.
 
P

Pit Capitain

Navindra said:
I would like to partially expand a string and then further expand it
later on when more information is available. (...)

combobox =<<"END"
...
#{options.map {|n|
%{<option value="#{n}"\#{thread_threshold == n ? ' SELECTED' : ''}>#{n} comments</option>} |
#{n}
}.join("\n")
}
...
END

(...)

Change the second "n" to "#{n}", just like the others.

Regards,
Pit
 
A

Ara.T.Howard

Hi,

I would like to partially expand a string and then further expand it
later on when more information is available. I am currently using
string interpolation with a reeval method suggested by Bill Kelly.

class String
def reeval(b = TOPLEVEL_BINDING)
eval("<<END_REEVAL\n" + self + "\nEND_REEVAL\n", b).chomp!
end
end

This lets me do stuff like this:

expanded = "one"
delayed = "two"

s = "#{expanded} \#{delayed}" # => "one #{delayed}"
s = s.reeval(binding) # => "one two"

why not make up a mini interpolator?

~ > cat a.rb
class String
def expand! h = {}
gsub!(%r/:\w+/o){|k| h[k.delete(':').intern] || k}
end
def expand(*a, &b)
dup.expand!(*a, &b); self
end
end

s = '<:expanded> - <:delayed>'

s.expand! :expanded => 'one'
puts s

s.expand! :delayed => 'two'
puts s

~ > ruby a.rb
<one> - <:delayed>
<one> - <two>

cheers.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| My religion is very simple. My religion is kindness.
| --Tenzin Gyatso
===============================================================================
 
N

Navindra Umanee

Eric Hodel said:
is sprintf (aka %) unable to fulfill your requirements?

I am aware that there are solutions such as these, but I keep finding
the string interpolation solution to be the most attractive/elegant
one as a general solution.
s = "#{expanded} %s"
s = s % delayed

If there are multiple occurrences of delayed, you have to repeatedly
apply it in the second line in proper order, and if you have any
independent occurrence of % in the string, I'm not entirely sure how
to escape it. This solution isn't very symmetric either.
I think the reeval thing is the wrong way to go about it.

What do you have against reeval? I have been trying to convince
myself there are better solutions, but after looking I keep coming
back to it. It just seems more natural or at least it works as a
general solution.
I find it much more useful to use a library that generates HTML
programatically

You mean like the CGI.* stuff or Amrita? CGI.* stuff looks very
painful and verbose to use, and Amrita, while it looks interesting in
simple cases, gives me the feeling that I will have to use too many
contortions to get it to work the way I want.

I mean constructs like <p id="var"></p> in Amrita look brilliant.
But then you get to stuff like <a id='a2' amrita:type='link'>Amrita2</a>
which starts to look more ugly and as you get to complex conditional
code generation it gets worse and worse.

It just seems easier to "just do it the way I want" in Ruby instead of
artificially constraining myself in Amrita, since I have the feeling I
would have to resort to Ruby programming in addition to Amrita just to
get the sort of effects I want anyway.

It could well be I'm not giving Amrita its due. I have been
considering re-implementing my stuff in it, but at the depressing
prospect, I thought I'd try making the string interpolation template
stuff more elegant (which it obviously isn't yet).
or from a template than to try fancy substitution tricks (unless its
really small). Eventually you end up with either a template language
or programatic HTML generation in the end.

I agree that this is what I will end up with -- I'm just trying to get
it to work in the most elegant way I can.

What do you use?

Cheers,
Navin.
 
N

Navindra Umanee

Ara.T.Howard said:
why not make up a mini interpolator?

~ > cat a.rb
class String
def expand! h = {}
gsub!(%r/:\w+/o){|k| h[k.delete(':').intern] || k}
end
def expand(*a, &b)
dup.expand!(*a, &b); self
end
end

Interesting.

As a general solution I'm not sure it's what I want.
I am also expanding complex objects (e.g #{posting.attribute}) and
with your custom interpolator I would end up either having to copy
information around (e.g. :posting_attribute => posting.attribute) or
using it with the regular delayed string interpolation strategy anyway.

Thanks,
Navin.
 
J

James Edward Gray II

I agree that this is what I will end up with -- I'm just trying to get
it to work in the most elegant way I can.

Are you aware that Ruby ships with a templating language in its
standard library?

irb(main):001:0> require "erb"
=> true
irb(main):002:0> expand, delay = "Expanded!", "Delayed!"
=> ["Expanded!", "Delayed!"]
irb(main):003:0> template = "#{expand} <%= delay %>"
=> "Expanded! <%= delay %>"
irb(main):004:0> ERB.new(template).result(binding)
=> "Expanded! Delayed!"

James Edward Gray II
 
N

Navindra Umanee

James Edward Gray II said:
Are you aware that Ruby ships with a templating language in its
standard library?

Yes I know of ERB. I am not sure what the advantage of using a
different syntax is, when I can simply use the same Ruby syntax but
escape it when necessary.

<%= delay %> is at least as verbose as \#{delay}.

What advantage do you see to using a new syntax instead of just Ruby?

Cheers,
Navin.
 
J

James Edward Gray II

Yes I know of ERB. I am not sure what the advantage of using a
different syntax is, when I can simply use the same Ruby syntax but
escape it when necessary.

<%= delay %> is at least as verbose as \#{delay}.

What advantage do you see to using a new syntax instead of just Ruby?

Off the top of my head:

* ERb's <% ... %> flow control construct.
* The ability to control spacing as you can with the special string
ERb's constructor takes.
* ERb's handy "% ... entire line of code ..." shortcut.
* ERb's utility methods for HTML and URL escaping.

But if you've found what works for you, far be it from me to
complain. I was just making sure you were aware of another option.

James Edward Gray II
 
N

Navindra Umanee

Hi James,

James Edward Gray II said:
But if you've found what works for you, far be it from me to
complain. I was just making sure you were aware of another option.

Thanks a lot for the info, I really appreciate it actually.
Off the top of my head:

* ERb's <% ... %> flow control construct.
* ERb's handy "% ... entire line of code ..." shortcut.

#{} allows arbitrary flow control of course. I think I can see where
ERB might have an edge in that it is easier to mix fixed strings with
Ruby code output.
* The ability to control spacing as you can with the special string
ERb's constructor takes.

I suppose I do sometimes end up with an extra \n here and there. I
haven't been bothered too much by it yet, because it generally doesn't
affect HTML rendering at all.

Another spacing problem right now is that I build my templates from a
lot of sub-templates and I can't see an easy way to get the HTML
indentation right because I never know what level I'm importing a
sub-template at. I doubt ERb would be of help here though and this is
basically only a concern regarding the readability of the HTML output.
* ERb's utility methods for HTML and URL escaping.

Unless I'm misunderstanding something, those utility methods are
replicated everywhere in Ruby. For example, CGI has its own copy of
them and I think WEBrick does too. Typically the methods are only 1
or 2 lines... I'm already using my own version of them.

Thanks a lot for the info! Let me know if I'm missing something.
I'll start thinking about whether it's worth switching to ERB given
the above.

Cheers,
Navin.
 
D

Devin Mullins

Navindra said:
#{} allows arbitrary flow control of course. I think I can see where
ERB might have an edge in that it is easier to mix fixed strings with
Ruby code output.
To be specific:

<html>
<head><title>Woo!</title>
<body>
<table>
<% @table.each do |row| %>
<tr>
<% row.each do |cell| %>
<td><%= cell %></td>
<% end %>
</tr>
<% end %>
</table>
</body>
</html>

Devin
 
E

Eric Hodel

If there are multiple occurrences of delayed, you have to repeatedly
apply it in the second line in proper order, and if you have any
independent occurrence of % in the string, I'm not entirely sure how
to escape it. This solution isn't very symmetric either.

Yes :(
What do you have against reeval? I have been trying to convince
myself there are better solutions, but after looking I keep coming
back to it. It just seems more natural or at least it works as a
general solution.

I find \#{} hard to read because I have to think about it. (It also
feels very funny.)
I agree that this is what I will end up with -- I'm just trying to get
it to work in the most elegant way I can.

What do you use?

I've never used Amrita, and I've found CGI lacking. Rails' use of
erb is very nice, I've used (and extended) RDoc's template system and
ported one from Seaside which was CGI-like, but felt better.

The biggest problem with CGI is that it does everything you want out-
of-the-box in an ugly way, and nobody seems to wrap up fragments into
methods that act as templates. CGI used this way would be much more
palatable.

See also http://raa.ruby-lang.org/search.rhtml?search=template

Its so easy to write a proper one that everybody does it! It seems
to be the second Ruby project everyone uses.
 
N

Navindra Umanee

Devin Mullins said:
<html>
<head><title>Woo!</title>
<body>
<table>
<% @table.each do |row| %>
<tr>
<% row.each do |cell| %>
<td><%= cell %></td>
<% end %>
</tr>
<% end %>
</table>
</body>
</html>

Indeed. This is what the same code looks like in Ruby syntax:

<html>
<head><title>Woo!</title>
<body>
<table>
#{@table.map do |row|
%{<tr>
#{row.map do |cell|
%{<td>#{cell}</td>}
end.join}
</tr>}
end.join}
</table>
</body>
</html>

What do you think? It's close isn't it? But thanks for your example,
I'm slowing starting to see the point.

Cheers,
Navin.
 
N

Navindra Umanee

(Side note: Using %% is enough to escape it)
I find \#{} hard to read because I have to think about it. (It also
feels very funny.)

Yeah, that's true. It's doing something special since it's
essentially delaying evaluation. It's almost sort of a simplistic
lambda, but for strings. If I were to do the same thing in erb,

"#{expanded} \#{delayed}"

would become:

"<%= expanded %> <%%= delayed %>"

The double % should give you same pause that \ does.

I am somehow loath to mix both syntaxes, although I realise that's an
option. I don't really know why I would want to use one syntax to
partially build a template and another syntax to build the rest.
See also http://raa.ruby-lang.org/search.rhtml?search=template

Its so easy to write a proper one that everybody does it! It seems
to be the second Ruby project everyone uses.

Hehe! Thanks a lot for the info. I'll have to think about this and
play around with a few of them.

Cheers,
Navin.
 
A

Ara.T.Howard

Indeed. This is what the same code looks like in Ruby syntax:

<html>
<head><title>Woo!</title>
<body>
<table>
#{@table.map do |row|
%{<tr>
#{row.map do |cell|
%{<td>#{cell}</td>}
end.join}
</tr>}
end.join}
</table>
</body>
</html>

What do you think? It's close isn't it? But thanks for your example, I'm
slowing starting to see the point.

and amrita is even nicer:

~ > cat a.rb
require 'amrita/template'

template = Amrita::TemplateText::new <<-html
<html>
<head> <title> Woo! </title> </head>
<body>
<table>
<tr id=rows>
<td id=foo></td>
<td id=bar></td>
</tr>
</table>
</body>
</html>
html

rows = [
{ :foo => 42, :bar => 42.0 },
{ :foo => 'forty-two', :bar => 'FORTY-TWO' },
]

template.expand STDOUT, :rows => rows


~ > ruby a.rb
<html>
<head> <title> Woo! </title> </head>
<body>
<table>
<tr>
<td>42</td>
<td>42.0</td>
</tr><tr>
<td>forty-two</td>
<td>FORTY-TWO</td>
</tr>
</table>
</body>
</html>

ahhhh. ;-)

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| My religion is very simple. My religion is kindness.
| --Tenzin Gyatso
===============================================================================
 
N

Navindra Umanee

Ara.T.Howard said:
and amrita is even nicer:

Hmmm, but your code doesn't quite do the same thing in that it doesn't
render the same structure:

@table = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]

Any particular reason you did it that way? I expect it's a symptom of
using Amrita, you have to beat the data into shape first.

Cheers,
Navin.
 
A

Ara.T.Howard

Ara.T.Howard said:
and amrita is even nicer:

Hmmm, but your code doesn't quite do the same thing in that it doesn't
render the same structure:

@table = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]

Any particular reason you did it that way? I expect it's a symptom of
using Amrita, you have to beat the data into shape first.

just showing a simple example. for the same thing you'd

~ > cat a.rb
require 'amrita/template'

template = Amrita::TemplateText::new <<-html
<html>
<head> <title> Woo! </title> </head>
<body>
<table>
<tr id=rows>
<td id=a></td> <td id=b></td> <td id=c></td>
</tr>
</table>
</body>
</html>
html

rows = [
{ :a => 1, :b => 2, :c => 3 },
{ :a => 4, :b => 5, :c => 6 },
{ :a => 7, :b => 8, :c => 9 },
]

template.expand STDOUT, :rows => rows


~ > ruby a.rb
<html>
<head> <title> Woo! </title> </head>
<body>
<table>
<tr>
<td>1</td> <td>2</td> <td>3</td>
</tr><tr>
<td>4</td> <td>5</td> <td>6</td>
</tr><tr>
<td>7</td> <td>8</td> <td>9</td>
</tr>
</table>
</body>
</html>

i generally wrap the 'beating' of data with something like

class SomeTable < ::Array
FIELDS = :a, :b, :c
def to_amrita
map do |row|
FIELDS.inject(Hash::new){|h,f| h[f] = row.shift; h}
end
end
end

so you can do something like

st = SomeTable[
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]

template.expand STDOUT, :rows => st.to_amrita

and have made component type object that have an 'to_html' method using an
internal template... the possiblities are endless but amrita, though it has a
small learning curve, can enable some really elegant ones. personally i
detest mixing html with ruby code in any form so amrita takes the cake in my
book.

cheers.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| My religion is very simple. My religion is kindness.
| --Tenzin Gyatso
===============================================================================
 
N

Navindra Umanee

Ara.T.Howard said:
i generally wrap the 'beating' of data with something like

Aha, but this is just the beginning. I was impressed by Amrita when I
first looked at the table example, but the more complex stuff you have
to do, the more contortions you have to suffer. To do an arbitrary
text substitution, you have to begin using these ugly <span>
statements, for example, and to substitute attributes you have to use
some other indirect stuff to describe them... but all this is easy
compared to what you have to do for complex control flow and
computation.

Development has in fact stopped on Amrita, as far as I can tell. Now
it's Amrita2 which allows you to use ERB... and you're back to square
one with an uncomfortable mix of different syntaxes. Essentially I
think this is a tacit admission that the Amrita approach is not powerful
enough on its own, or really it is just too clunky. Then there is the
horror that is Template Spec.

I could have it all wrong of course, having never used Amrita.
However when I start thinking about how I'm going to convert all my
"just do it in Ruby" code to Amrita, I get a nice little headache.

My Ruby HTML code generation does recursive tree traversals, has
special conditions, it has counters keeping track of nesting levels
that it uses for later substitions in the code it is building. It's
complex stuff doing complex things, but I "just do it" because it
really is straightforward Ruby coding. There's nothing magic about
recursion or counters.

On the other hand, I haven't the faintest idea how I would approach
that in Amrita. I imagine I would have to do all the work on some
external data structures and then somehow apply it to a template.
It's just an extra indirection and extra work... for what really? A
performance hit at runtime? Amrita just feels like a mental
straitjacket to me.

I too, like you, am attracted to the idea of separating HTML template
code from Ruby code. I can't quite think of a way of doing this that
is actually worthwhile when getting down to the more complex stuff.

:)

Cheers,
Navin.
 

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
474,262
Messages
2,571,054
Members
48,769
Latest member
Clifft

Latest Threads

Top