Does ERB have recursive template support?

I want to use ERB template library to do some code generation. It
requires generating nested structures, so I need recursive
template support. As I know, Rails does have recursive template support,
but I can't find it in ERB document.

Could someone confirm if there's recursive template support in the
standard ERB template library? Thanks a lot!

···

--
Posted via http://www.ruby-forum.com/.

What exactly do you mean by "recursive templates"? Do you mean a
template which can present recursive structures such as trees? Or do
you want an ERB template to include itself? Both should be doable
since you can invoke arbitrary code.

Kind regards

robert

···

On Tue, Jun 19, 2012 at 5:26 AM, Todd Wei <lists@ruby-forum.com> wrote:

I want to use ERB template library to do some code generation. It
requires generating nested structures, so I need recursive
template support. As I know, Rails does have recursive template support,
but I can't find it in ERB document.

Could someone confirm if there's recursive template support in the
standard ERB template library? Thanks a lot!

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

I am currently using ERB for code generation. I implemented this using a xml
document schema of my own creation and chose not to do the recursive
template approach due to elegance. It strongly depends on your use case, but
I planned on dynamically loading many different ruby source files containing
an ERB template and it's template variables. Nesting templates would have
been quite ugly for me, since there could potentially be hundreds of
exceptions.

···

-----Original Message-----
From: Robert Klemme [mailto:shortcutter@googlemail.com]
Sent: Tuesday, June 19, 2012 1:26 AM
To: ruby-talk ML
Subject: Re: Does ERB have recursive template support?

On Tue, Jun 19, 2012 at 5:26 AM, Todd Wei <lists@ruby-forum.com> wrote:

I want to use ERB template library to do some code generation. It
requires generating nested structures, so I need recursive template
support. As I know, Rails does have recursive template support, but I
can't find it in ERB document.

Could someone confirm if there's recursive template support in the
standard ERB template library? Thanks a lot!

What exactly do you mean by "recursive templates"? Do you mean a template
which can present recursive structures such as trees? Or do you want an ERB
template to include itself? Both should be doable since you can invoke
arbitrary code.

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Robert Klemme wrote in post #1065085:

What exactly do you mean by "recursive templates"? Do you mean a
template which can present recursive structures such as trees? Or do
you want an ERB template to include itself? Both should be doable
since you can invoke arbitrary code.

Kind regards

robert

My code generator will generate Java nested classes. The problem is the
level of nesting can't be determined statically. So, the ideal case is I
define a Java class template in ERB, which accepts binding variables as
input
parameter, and in the template, it can "call" itself with new bindings.
Just like what does recursive function do.

I'm not sure if i can achieve this with "invoke arbitrary code". For
example, can I invoke the template itself with new bindings in the
template? Could you give me an example? Thanks!

···

--
Posted via http://www.ruby-forum.com/\.

Nathan Standiford wrote in post #1065169:

I planned on dynamically loading many different ruby source files
containing an ERB template and it's template variables.

Nathan, what does this mean? Could you give me more details? Thanks!

···

--
Posted via http://www.ruby-forum.com/\.

If I understood correctly, something like this could work for you:

# template.rb

this template contains:
<%= array.inspect %>
<%
first = array.shift
if first
%>
<%= first %>,
<%= ERB.new(File.read("template.erb"), 0, "",
"result_#{array.length}").result(binding) %>
<%
end
%>
and something else.

1.9.2p290 :041 > array = [1,2,3,4]; ERB.new(File.read("template.erb"),
0, "", "result_#{array.length}").result(binding)
=> "this template contains:\n[1, 2, 3, 4]\n\n1,\nthis template
contains:\n[2, 3, 4]\n\n2,\nthis template contains:\n[3,
4]\n\n3,\nthis template contains:\n[4]\n\n4,\nthis template
contains:\n\n\nand something else.\n\nand something else.\n\nand
something else.\n\nand something else.\n\nand something else."

The trick is to create a unique temporary variable for the ouput of
each iteration. If not, erb will overwrite the result of each
iteration.
I don't know how you represent your nested objects. In this example I
created an array through which I recursively generate a template.

I hope this gives you some ideas.

Jesus.

···

On Wed, Jun 20, 2012 at 3:22 AM, Todd Wei <lists@ruby-forum.com> wrote:

Robert Klemme wrote in post #1065085:

What exactly do you mean by "recursive templates"? Do you mean a
template which can present recursive structures such as trees? Or do
you want an ERB template to include itself? Both should be doable
since you can invoke arbitrary code.

Kind regards

robert

My code generator will generate Java nested classes. The problem is the
level of nesting can't be determined statically. So, the ideal case is I
define a Java class template in ERB, which accepts binding variables as
input
parameter, and in the template, it can "call" itself with new bindings.
Just like what does recursive function do.

I'm not sure if i can achieve this with "invoke arbitrary code". For
example, can I invoke the template itself with new bindings in the
template? Could you give me an example? Thanks!

If I understood correctly, something like this could work for you:

# template.rb

this template contains:
<%= array.inspect %>
<%
first = array.shift
if first
%>
<%= first %>,
<%= ERB.new(File.read("template.erb"), 0, "",
"result_#{array.length}").result(binding) %>
<%
end
%>
and something else.

1.9.2p290 :041 > array = [1,2,3,4]; ERB.new(File.read("template.erb"),
0, "", "result_#{array.length}").result(binding)
=> "this template contains:\n[1, 2, 3, 4]\n\n1,\nthis template
contains:\n[2, 3, 4]\n\n2,\nthis template contains:\n[3,
4]\n\n3,\nthis template contains:\n[4]\n\n4,\nthis template
contains:\n\n\nand something else.\n\nand something else.\n\nand
something else.\n\nand something else.\n\nand something else."

You are doing too much file reading here because you reread the
template file for each value. That's not necessary since we can use
parameter binding.

The trick is to create a unique temporary variable for the ouput of
each iteration. If not, erb will overwrite the result of each
iteration.

I think the trick lies in getting bindings properly by nesting them
properly. My first naive approaches did not work because variables
were overwritten.

I don't know how you represent your nested objects. In this example I
created an array through which I recursively generate a template.

Same here:

#!/opt/bin/ruby19

require 'erb'

def rt(s)
  e = ERB.new(s)
  recursive = lambda {|a, level| e.result(binding)}
end

arr = [
  1,
  2,
  [3],
  [4, [5]],
  6
]

e = rt %Q{
  <%= ' ' * level %>open level=<%= level %>
  <%
    if Enumerable === a
      a.each do |x|
        %><%= recursive[x, level + 1] %><%
      end
    else
      %><%= ' ' * level %><%= a %><%
    end
  %>
  <%= ' ' * level %>close level=<%= level %>
}

p arr
puts e[arr, 0]

Kind regards

robert

···

On Wed, Jun 20, 2012 at 1:53 PM, Jesús Gabriel y Galán <jgabrielygalan@gmail.com> wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

I have an XML file which has an integer weight for the template and template file information.

Here is how I loaded the template:
I used m = Module.new.eval(File.read(path).to_s.strip) for the load.
I used m.const_get("#{module}").const_get(#{class_name}") for dereferencing
I then called two methods, which I implemented in all the classes, which retrieves a hash and a template string.
I threw all of this into another class, which had other info.

···

-----Original Message-----
From: Todd Wei [mailto:lists@ruby-forum.com]
Sent: Tuesday, June 19, 2012 6:26 PM
To: ruby-talk ML
Subject: Re: Does ERB have recursive template support?

Nathan Standiford wrote in post #1065169:

I planned on dynamically loading many different ruby source files
containing an ERB template and it's template variables.

Nathan, what does this mean? Could you give me more details? Thanks!

--
Posted via http://www.ruby-forum.com/\.

If I understood correctly, something like this could work for you:

# template.rb

this template contains:
<%= array.inspect %>
<%
first = array.shift
if first
%>
<%= first %>,
<%= ERB.new(File.read("template.erb"), 0, "",
"result_#{array.length}").result(binding) %>
<%
end
%>
and something else.

1.9.2p290 :041 > array = [1,2,3,4]; ERB.new(File.read("template.erb"),
0, "", "result_#{array.length}").result(binding)
=> "this template contains:\n[1, 2, 3, 4]\n\n1,\nthis template
contains:\n[2, 3, 4]\n\n2,\nthis template contains:\n[3,
4]\n\n3,\nthis template contains:\n[4]\n\n4,\nthis template
contains:\n\n\nand something else.\n\nand something else.\n\nand
something else.\n\nand something else.\n\nand something else."

You are doing too much file reading here because you reread the
template file for each value. That's not necessary since we can use
parameter binding.

You are right. I was focusing on the temporary erbout variable approach.

The trick is to create a unique temporary variable for the ouput of
each iteration. If not, erb will overwrite the result of each
iteration.

I think the trick lies in getting bindings properly by nesting them
properly. My first naive approaches did not work because variables
were overwritten.

I don't know how you represent your nested objects. In this example I
created an array through which I recursively generate a template.

Same here:

#!/opt/bin/ruby19

require 'erb'

def rt(s)
e = ERB.new(s)
recursive = lambda {|a, level| e.result(binding)}
end

arr = [
1,
2,
[3],
[4, [5]],
6
]

e = rt %Q{
<%= ' ' * level %>open level=<%= level %>
<%
if Enumerable === a
a.each do |x|
%><%= recursive[x, level + 1] %><%
end
else
%><%= ' ' * level %><%= a %><%
end
%>
<%= ' ' * level %>close level=<%= level %>
}

p arr
puts e[arr, 0]

Very nice and elegant. Thanks !

Jesus.

···

On Wed, Jun 20, 2012 at 3:24 PM, Robert Klemme <shortcutter@googlemail.com> wrote:

On Wed, Jun 20, 2012 at 1:53 PM, Jesús Gabriel y Galán > <jgabrielygalan@gmail.com> wrote:

Same here:

#!/opt/bin/ruby19

require 'erb'

def rt(s)
e = ERB.new(s)
recursive = lambda {|a, level| e.result(binding)}
end

arr = [
1,
2,
[3],
[4, [5]],
6
]

e = rt %Q{
<%= ' ' * level %>open level=<%= level %>
<%
   if Enumerable === a
     a.each do |x|
       %><%= recursive[x, level + 1] %><%
     end
   else
     %><%= ' ' * level %><%= a %><%
   end
%>
<%= ' ' * level %>close level=<%= level %>
}

p arr
puts e[arr, 0]

Kind regards

robert

I like ERB#def_method.

#!/opt/bin/ruby19
require 'erb'

class View
  ERB.new(<<EOS).def_method(self, 'draw(a, level=0)')

<%= ' ' * level %>open level=<%= level %>
<%
   if Enumerable === a
     a.each do |x|
       %><%= draw(x, level + 1) %><%
     end
   else
     %><%= ' ' * level %><%= a %><%
   end
%>
<%= ' ' * level %>close level=<%= level %>
EOS
end

arr = [
1,
2,
[3],
[4, [5]],
6
]

view = View.new

p arr
puts view.draw(arr)