Emulation for ERB extention -%> for the purpose of unittests - ruby

I need to evaluate an ERB template, and then ensure it's a valid NGINX configuration file, but vanilla ERB doesn't allow the -%> directive. How am I able to add that extension into my rakefile?
I've been able to replicate the problem in irb as so:
~ $ irb
irb(main):001:0> require 'erb'
=> true
irb(main):002:0> var = "yeah"
=> "yeah"
irb(main):003:0> ERB.new(" <% if var == 'yeh' -%>
irb(main):004:1" something
irb(main):005:1" <% else -%>
irb(main):006:1" something else
irb(main):007:1" <% end -%>
irb(main):008:1" ").result binding #"
SyntaxError: (erb):1: syntax error, unexpected ';'
...concat " "; if var == 'yeh' -; _erbout.concat "\nsomething\...
... ^
(erb):3: syntax error, unexpected keyword_else, expecting $end
; else -; _erbout.concat "\nsomething else\n"
^
from /usr/lib/ruby/1.9.1/erb.rb:838:in `eval'
from /usr/lib/ruby/1.9.1/erb.rb:838:in `result'
from (irb):3
from /usr/bin/irb:12:in `<main>'

In order to use the -%> syntax in ERB you need to set the trim mode option to '-'. This is the third option to the constructor, you will need to pass nil as the second (unless you want to change the safe_level from the default):
ERB.new(" <% if var == 'yeh' %>
something
<% else %>
something else
<% end %>
", nil, '-').result binding
Without this option the - is included in the generated Ruby script and gives you the syntax error when you try to run it.
Note that there is another eRuby processor, Erubis which might have slightly different options (it can still use this syntax). This one is used by Rails. Check out the docs for more info.

Related

Chef Template If Else

I'm trying to dynamic change create a nginx xonfig file with chef using an erb. What is the correct syntax for if else it an .erb file.
The below code gives me an error
(erubis):17: syntax error, unexpected tINTEGER, expecting keyword_end
<% node['dd']['pipeline']['env'] -%>
resolver <%if node['dd']['pipeline']['env'] == "production" then 10.100.0.5 else 10.0.0.5 end -%> valid=30s;
Because 10.100.0.5 isn't a valid Ruby literal. You want this:
<%= if node['dd']['pipeline']['env'] == "production" then "10.100.0.5" else "10.0.0.5" end %>

`-%>` tag does not seem to work by default

I have an erb script:
<% foo="second" %>
first <%= foo %> third
When I run this with the erb command, it puts a leading blank line in the output:
% erb junk
first second third
But when I change the closing %> to -%> in the first line, the script fails unless I use erb -T -:
~$ erb junk1
/usr/share/ruby/erb.rb:850:in `eval': junk1:1: syntax error, unexpected ';' (SyntaxError)
_erbout = ''; foo="second" -; _erbout.concat "\n"
^
from /usr/share/ruby/erb.rb:850:in `result'
from /usr/share/ruby/erb.rb:832:in `run'
from /bin/erb:133:in `run'
from /bin/erb:154:in `<main>'
$ erb -T - junk1
first second third
I thought -%> was supposed to be always recognized as a tag to skip the trailing newline. I am testing these templates to use with Puppet, so I assume Puppet will recognize the -%> tag, and this funkiness is just part of the erb command.
In order to enable a trim_mode (-%>), you have to instantiate the ERB object with trim mode parameter, such as:
e = ERB.new(str, nil, '-')
Note that Rails does not use the stdlib's ERB by default - instead, it uses erubis. Documentation on this can feature can be found in Section 6.3 here:
Since 2.6.0, '<%= -%>' remove tail spaces and newline. This is for compatibiliy with ERB when trim mode is '-'. '<%= =%>' also removes tail spaces and newlines, and this is Erubis-original enhancement (cooler than '<%= -%>', isn't it?).
See the ERB documentation for more information. Note also that if you have any training spaces after the -%>, then this can cause the newline to not be trimmed.

Ruby ERB how to create list of mount points

I am trying to use ERB templating to dynamically create a list of mount points for filesystems, to be monitored for disk usage by Nagios. I am having trouble figuring out the exact syntax for ERB, I have it working in straight Ruby.
Here is what I have tried for ERB
<% #str = `df -h`; #str.scan(/\/[\w\/]+$/){|m| -%><%= -p #m %><% unless m.match(/\/dev|\/proc/)};puts %>
Here is my code, and desired output working in the Ruby CLI:
ruby -e '
str = `df -h`
str.scan(/\/[\w\/]+$/){|m| print "-p #{m} " unless m.match(/\/dev|\/proc/)};puts'
-p /net -p /home -p /Network/Servers <-- Output
First of all, you don't need to run those in the template. You probably have some ruby code lauching the template (like a Rails controller or a Sinatra class). You can put your code there, and store the output to show in the template (example assuming rails).
Second, you don't want to use print or puts (as those would output toward the terminal, not the template), but to store the output in a variable.
The controller:
class MountPointsController < ApplicationController
def index
#output = ""
str = `df -h`
str.scan(/\/[\w\/]+$/){|m| output << "-p #{m} " unless m.match(/\/dev|\/proc/)};output << "\n"
end
end
The template is then as simple as (note the '<%=' that means "output the result in the template):
<%= #output %>
Even if I would recommend against, here is a sample with all the code in the template:
<% #output = "" %>
<% str = `df -h` %>
<% str.scan(/\/[\w\/]+$/){|m| output << "-p #{m} " unless m.match(/\/dev|\/proc/)};output << "\n" %>
<%= #output %>

Chef Template DSL - how to load a hash from json and iterate keys and values

I have the below code in a chef template but get error when uploading to chef server. How do I resolve?
<%
contents_hash = File.read('/tmp/cluster_hash')
neoservers_hash = JSON.parse(contents_hash)
-%>
<% "#{neoservers_hash}".each_pair do |id, ipaddress| %>
<%= "server.#{id}=#{ipaddress}:2888:3888" %>
<% end %>
When I try to upload the cookbook, I get the following error:
$ knife cookbook upload neo4j -E development
Uploading neo4j [0.1.0]
FATAL: Erb template templates/default/coord.cfg.erb has a syntax error:
FATAL: -:7: syntax error, unexpected tIDENTIFIER, expecting '}'
FATAL: _buf << ( "server.#{id}=#{ipaddress}:2888:3888" ).to_s; _buf << '
FATAL: ^
FATAL: -:8: unterminated string meets end of file
You have a weird syntax in this line:
<% "#{neoservers_hash".each_pair do |id, ipaddress| %>
You seem to try to use string evaluation with the neoserver_hash variable, which will not really work, as neoserver_hash is a Hash and not a String. Also you are missing the closing brace. Instead, you probably want to get rid of the string evaluation completely and use something like this:
<% neoservers_hash.each_pair do |id, ipaddress| %>

About using method "print" etc. in ERB for metaprogramming

I am using ERB via console for metaprogramming (for math software). For example, I have file test.erb containing
text line before ruby
<%= 'via <%=' %>
<% print 'print' %>
<% puts 'puts' %>
text line after ruby
When I parse it by $ erb test.erb, I get the following output
printputs
text line before ruby
via <%=
text line after ruby
I am not surprised by it, but wonder if there is a good way to catch output of print method and put it at the place where it is called in the ERB template?
text line before ruby
via <%=
print
puts
text line after ruby
Imagine that I have a complex construction, where I would prefer to print instead of collecting output in a string inside <%= %>.
Update
Just to illustrate the answer of Brian:
text line before ruby
<%= '<%=' %>
% print 'print'
% puts 'puts'
% E = _erbout
% E << '_erbout'+"\n"
text line after ruby
Parsing the file $ erb test.erb:
printputs
text line before ruby
<%=
_erbout
text line after ruby
Not certain if this helps in your particular case, but consider looking at some examples using the _erbout method
text line before ruby
<%= 'via <%=' %>
<% _erbout << 'print' %>
<% _erbout << 'puts' %>
text line after ruby
Hope this gets you somewhere.
As an option, one can redefine Kernel#p method:
file: p_for_erb.rb
module Kernel
alias :p_super :p
def p *args
if args.empty?
##_erbout
elsif args.first.class == Hash
##_erbout = args.first[:init]
else
args.each { |a| ##_erbout << a }
end
end
end
...and then do similar to this:
file: mytest.erb
text before ruby
% require 'p_for_erb'
% p init: _erbout # to initialize class variable ##_erbout
% p "my p output"
Command $ erb mytest.erb produces
text before ruby
my p output

Resources