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!

On Tue, Jun 19, 2012 at 5:26 AM, Todd W. [email protected] 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

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.

Robert K. 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!

Nathan S. 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!

On Wed, Jun 20, 2012 at 3:22 AM, Todd W. [email protected] wrote:

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.”

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 1:53 PM, Jess Gabriel y Galn
[email protected] wrote:

<%= first %>,
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

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.

On Wed, Jun 20, 2012 at 3:24 PM, Robert K.
[email protected] wrote:

first = array.shift
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.

created an array through which I recursively generate a template.
end
<%= ’ ’ * level %>open level=<%= level %>
}

p arr
puts e[arr, 0]

Very nice and elegant. Thanks !

Jesus.

end
<%= ’ ’ * level %>open 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)