Puppet Templates Iterations and yaml files - ruby

I have the following yaml file in my data dir:
---
type:
- config_setting1:
foo: bar
- config_setting2:
foo: bar
My .erb template looks like this:
conf {
<% settings = YAML.load_file('/etc/puppetlabs/code/environments/example/data/conf.yaml') -%>
<% settings['type'].each do |val| -%>
<%= val %>
<% end -%>
}
When I run puppet on my agent machine I end up with this:
conf {
{"config_setting1"=>nil, "foo"=>"bar"}
{"config_setting2"=>nil, "foo"=>"bar"}
}
My end goal is to get the output to look like this:
conf {
config_setting1 {
foo: bar
}
config_setting2 {
foo: bar
}
}
I know I have some clean up to do on my template to actually get things to output that way, but I'm more focused on the how than the end result at the moment. As you can see I'm familiar with using the ['type'] on the end of the settings to navigate through the nested hash, and I realize I could create this structure pretty easily if I hard coded it but I want to understand how to use it iteratively. I've been attempting to follow the Puppet Documentation on iterations but their examples don't work even when you copy them verbatim... which makes things a little difficult. How can I call pull out a single piece of data in a nested yaml file like I have? Either just the key or just a specific value? I tried something like:
<% settings['type'].each do |val| -%>
<%= settings['val'] %>
<% end -%>
and multiple variations of this but I couldn't find the right syntax to get what I wanted. I've also tried having something along the lines of <% settings['type'].each do |index, value| -%> but I was unable to get any results I could use out of that either. If anyone could point me in the right direction I'd appreciate it. I'm open to being told that I'm going about this entirely the wrong way as well; if there is a better way to get at this data I'm all ears.
Another question that's less important, but still irks me - in my load_file I have the absolute path... is there a way to use relative?

Amazing how typing something out will answer your own question. I realized there was a pretty easy solution. If we take my template:
conf {
<% settings = YAML.load_file('/etc/puppetlabs/code/environments/example/data/conf.yaml') -%>
<% settings['type'].each do |val| -%>
<%= val %>
<% end -%>
}
and on line three replace <% settings['type'].each do |val| -%> with <% settings.keys.each do |val| -%> I'm able to get what I'm looking for. I'd still be interested if there is a better way to do this though, either how I'm loading via yaml or otherwise.

Related

erb file with chef syntax

Trying to output the contents of
node['a'] = {:b "1" :c "2"}
by doing this:
a:
<% a = node['a'] %>
b: <% a[:b] %>
c: <% a[:c] %>
<% end %>
to generate this:
a:
b: 1
c: 2
However not entirely sure the correct syntax to do this being new to ruby, chef and erb.
Okay, so let's rewind a bit. The first thing is that you generally don't want to reference node attributes directly in templates. In some cases like attributes coming from Ohai it can be okay as a shorthand, but for important data I would also pass it in via the variables property like this:
template '/etc/whatever.conf' do
source 'whatever.conf.erb'
variables a: node['a']
end
With that in place we've now expose the data as a template variable. The second piece of improving this is to let Ruby do the heavy lifting of generating YAML. We can do this using the .to_yaml method in the template:
<%= #a.to_yaml %>
That should be all you need!

How to concatenate #something inside a render function in a template

I have this function in my template:
<%= for {element, id} <- Enum.with_index(MyProject.PageView.Recursion.buildElements(#header_linkNumber),1) do %>
<%= render FabricaASA.ComponentView, #header_linkType,
button_id: "#{id}",
button_mainStyle: #header_mainStyle
%>
<% end %>
Now I would like to concatenate, on my right side, #header_mainStyle + id so that from other template, for each created element, I could pass: header_mainStyle1, header_mainStyle2,...header_mainStyleN
Also, on the left side, where I have button_mainStyle: I would like to concatenate #header_linkType + _mainStyle: so that I could dynamically change it to, link_mainStyle: or button_mainStyle:
Up to now I wasn't able to do it properly...
I'm afraid you are doing something wrong if you need such thing. Maybe there's a simpler solution...
Anyway: since some version of Phoenix (I'm sorry I don't know which one precisely, maybe 1.0?), #-variables are stored in #conn.assigns map and you can access them by name there. In older versions, these variables were macros and this kind of magic did not work.
So you can try to put this into the controller:
def index(conn, _params) do
render conn, "index.html", [var1: "var1"]
end
and this into the page template:
<p>var1: <%= #var1 %></p>
<p>assigns:</p>
<%= for i <- 1..10 do %>
<p>var<%= i %>:<p>
<pre><%=
varname = "var#{i}" |> String.to_atom
inspect(#conn.assigns[varname]) %>
</pre>
<% end %>
...you will see var1 to var10 bindings (screenshot: http://postimg.org/image/4b4790cjz/). But it's little bit black magic and probably wrong approach.

ERB/Templating clue for ruby

Optimally I'd want template to look something like this:
interfaces {
<%= name %> {
description "<%= description %>";
mtu <%= mtu %>;
}
}
However, I'd want lines not to be printed if the code evaluates to non-true value. It could be hack to ERB, where after \n it sets print_line = true and after evaluating code with b%> to false it sets print_line = false, and only print if print_line.
But it seems changing ERB for this isn't exactly trivial, it reads whole template, non-code parts are inserted as print "data", and result is single big string of ruby code which is evaluated as whole during #result. I would either need to eval b%> code during string-scanning and just insert print "data" if it returns true, or nothing at all or I'd need to rescan the code in #result to run b%> first.
It seems bit fruitless to even use template, if you end up writing all of it inside code blocks, like:
interfaces {
<%= name %> {
<% if description %>
description "<%= description %>";
<% end%>
<% if mtu %>
mtu <%= mtu%>;
<% end%>
}
}
or:
interfaces {
<%= name %> {
<%= "description \"#{description}\";" if description %>
<%= "mtu #{mtu};" if mtu %>
}
}
I need to support various different configuration formats, in some other configuration format it might be 'maximum-transfer-unit <%= mtu> bytes'. I wish to keep all platform-specific intelligence in the template and actual code in the template minimum. I have no problem adding platform agnostic complexity outside the templates.
Seems like relatively common use-case. Before I go NIHing my own template language or hacking ERB, are there perhaps some other template language better fit for my use-case. Or missingsomething else?
I've implemented the hack for <%b stuff %> where whole line is omitted if that evaluates to false. ERB was not designed for anything like this at all so it's very dirty, and it may be better for me to just write my own template language, unless someone can suggest maybe existing solution where I can cleanly implement something like.
For interested parties, hack is here http://p.ip.fi/-NeJ
Ended up re-inventing the wheel: https://github.com/ytti/malline
Helper methods
def description_helper(description)
"description \"#{description}\";" if description
end
def mtu_helper(mtu)
"mtu #{mtu};" if mtu
end
interfaces {
<%= name %> {
<%= description_helper %>
<%= mtu_helper %>
}
}
Decorator pattern
class MyObject
def description
'some description'
end
def mtu
nil
end
module ViewDecorator
def description
"description \"#{super}\";" if super
end
def mtu
"mtu #{super};" if super
end
end
end
o = MyObject.new
p o.description #=> "some description"
p o.mtu #=> nil
o.extend MyObject::ViewDecorator
p o.description #=> "description \"some description\";"
p o.mtu #=> nil

Puppet 3 loop execution only if variable is defined within a template

I am trying to create a loop only if index is defined. But it looks like
erb can't handle a loop within a if clause.
<% if(#index) %>
index <% index_files.each do |i| %> <%= i %> <% end %>;
<% end %>
Expected Result was:
index index.html index.php
or
""
Syntax error i got:
My flat approach failed as expected:
<% if(#index_files) %> try_files <% end %> <% index_files.each do |i| %> <%= i %> <% end %>
I defined index_files as undef => broke the each loop
I defined an empty array => since an empty array is defined it didn't work.
Maybe I can check the length of index_files?
Or do I need a complete different way to solve the problem?
I'm doing the same and it works for me, also for nginx ;).
For example:<% if #proxy_ignore_headers %> proxy_ignore_headers<% proxy_ignore_headers.each do |i| -%> <%= i %><% end -%>;
That works like a charm, the only difference with you is using () for the if condition, but I bet puppet supports (). It's weird, maybe you had pressed a bad combination generating a character that can't be seen but it's messing with your code, try writing all from scratch just in case.
You can see the full template here
Good luck
At first glance you just need to change
index_files.each
to
#index_files.each

Rails 3 refactoring issue

The following view code generates a series of links with totals (as expected):
<% #jobs.group_by(&:employer_name).sort.each do |employer, jobs| %>
<%= link_to employer, jobs_path() %> <%= "(#{jobs.length})" %>
<% end %>
However, when I refactor the view's code and move the logic to a helper, the code doesn't work as expect.
view:
<%= employer_filter(#jobs_clone) %>
helper:
def employer_filter(jobs)
jobs.group_by(&:employer_name).sort.each do |employer,jobs|
link_to employer, jobs_path()
end
end
The following output is generated:
<Job:0x10342e628>#<Job:0x10342e588>#<Job:0x10342e2e0>Employer A#<Job:0x10342e1c8>Employer B#<Job:0x10342e0d8>Employer C#<Job:0x10342ded0>Employer D#
What am I not understanding? At first blush, the code seems to be equivalent.
In the first example, it is directly outputting to erb, in the second example it is returning the result of that method.
Try this:
def employer_filter(jobs)
employer_filter = ""
jobs.group_by(&:employer_name).sort.each do |employer,jobs|
employer_filter += link_to(employer, jobs_path())
end
employer_filter
end
Then call it like this in the view:
raw(employer_filter(jobs))
Also note the use of "raw". Once you move generation of a string out of the template you need to tell rails that you don't want it html escaped.
For extra credit, you could use the "inject" command instead of explicitly building the string, but I am lazy and wanted to give you what I know would work w/o testing.
This syntax worked as I hoped it would:
def employer_filter(jobs_clone)
jobs_clone.group_by(&:employer_name).sort.collect { |group,items|
link_to( group, jobs_path() ) + " (#{items.length})"
}.join(' | ').html_safe
end

Resources