16

I need some help with erb templates, I can't seem to get my head around passing an array and then iterating over it. My problem is this. I want to pass a few arrays: `

device      => ["eth0", "br0"],
ipaddr      => ["192.168.12.166", "192.168.12.199"],
netmask     => ["255.255.255.0", "255.255.255.0"], 
hwaddr      => '',
network     => '',
gateway     => ["192.168.12.254", "192.168.12.204"],                                                                                                                

To a template that iterates over each item in the array and prints it out:

auto <%= device %> inet static                                                                                                                                        
address <%= ipaddr %>
netmask <%= netmask %>
broadcast <%= broadcast %>
gateway <%= gateway %>

As far as I can get so far is figuring out that I need to do something with device.each |device| puts device, but I don't know what the syntax is supposed to look like. I believe you can tell what I'm trying to do from these snippets, and then you might understand that the entries need to be seperate, and not interpolated. Any help you can offer would be appreciated. I know I should be trying things out in irb and figuring them out from there, which is what I'm reading up on now.

Thanks!

matt
  • 805
  • 3
  • 9
  • 14

3 Answers3

31

the basic syntax for using each in ruby is something like this:

array.each do |item_from_array| BLOCK

so if you only had one array then you could just do something like this: (I would use a different name inside the vertical bars for clarity)

<% device.each do |dev| %>
  auto <%= dev %> inet static
<% end %>

However that would iterate over all of your devices first, before moving on to your ipaddr array. I'm guessing you want them each in turn auto, address, netmask, etc. In that case you'd be better off using a more 'traditional' index and looping through N times, like this:

<% for idx in (0..1) %>
  auto <%= device[idx] %> inet static
  address <%= address[idx] %>
  netmask <%= netmask[idx] %>
  broadcast <%= broadcast[idx] %>
<% end %>

Of course you need to think about what your maximum size of array is, and what to do if an array contains less entries than the others. You can find the maximum size of all the arrays by doing something like this: [device,address,netmask,broadcast].map{|a| a.length}.max

and you can skip over a particular array like this: <% if idx < address.length %> address <%= address[idx] %><% end %>

nocache
  • 1,805
  • 16
  • 18
  • This works, and is exactly what I'm looking for. Thanks! There is a syntax error on the line `<% for idx in (0..1) $>` but I can't edit it unless I change 6 characters. – matt Aug 16 '11 at 15:03
  • 1
    First example should be: `<% device.each do |dev| %> auto <%= dev %> inet static <% end %>` – jamtur01 Jun 29 '12 at 07:49
  • @nocache this is pretty old, but thanks for including the ".each" example even though it didn't necessarily answer the OP question, it was what I needed! – Mark_Eng Apr 18 '17 at 13:25
5

ERB Templates for Dummies

Basic code:

require 'erb'
ERB.new(template).result binding_for_template

What are template and binding_for_template?

Template

Just the template content. You can read it from a file just with a File.read(path).

Binding for template

A binding

encapsulate the execution context at some particular place in the code and retain this context for future use.

How do you use it? Easy:

def binding_for_my_template
  devices      = ["eth0", "br0"]
  ipaddrs      = ["192.168.12.166", "192.168.12.199"]
  netmasks     = ["255.255.255.0", "255.255.255.0"]
  hwaddrs      = ['']
  networks     = ['']
  gateways     = ["192.168.12.254", "192.168.12.204"]
  binding
end

That will return a binding with all six arrays (I changed hwaddr and network to arrays for consistency.

Iterating over arrays

The usual way is using the each method, just like this:

<%- devices.each do |d| %>
  auto <%= d %> inet static
<%- end %>

but there are other methods if you wanna put all in one line, for example devices.join ' ' will join all the strings with a space in between.

ThiefMaster
  • 310,957
  • 84
  • 592
  • 636
Serabe
  • 3,834
  • 19
  • 24
1

You should read the docs. Quoting:

# Managed by Class['ntp']
<% servers_real.each do |server| -%>
server <%= server %>
<% end -%>

# ...

This snippet will iterate over each entry in the array and print it after a server statement, so, for example, the string generated from the Debian template will end up with a block like this:

# Managed by Class['ntp']
server 0.debian.pool.ntp.org iburst
server 1.debian.pool.ntp.org iburst
server 2.debian.pool.ntp.org iburst
server 3.debian.pool.ntp.org iburst
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 3
    This works if you're only using one variable, and the order doesn't matter. I'm trying to do something a little more complicated. – matt Aug 16 '11 at 15:05