How do I execute ruby template files (ERB) without a web server from command line? - ruby

I need ERB (Ruby's templating system) for templating of non-HTML files.
(Instead, I want to use it for source files such as .java, .cs, ...)
How do I "execute" Ruby templates from command line?

You should have everything you need in your ruby/bin directory. On my (WinXP, Ruby 1.8.6) system, I have ruby/bin/erb.bat
erb.bat [switches] [inputfile]
-x print ruby script
-n print ruby script with line number
-v enable verbose mode
-d set $DEBUG to true
-r [library] load a library
-K [kcode] specify KANJI code-set
-S [safe_level] set $SAFE (0..4)
-T [trim_mode] specify trim_mode (0..2, -)
-P ignore lines which start with "%"
so erb your_erb_file.erb should write the result to STDOUT.
(EDIT: windows has erb.bat and just plain "erb". The .bat file is just a wrapper for erb, which I guess should make the same command work pretty much the same on any OS)
See the prag prog book discussion (starts about half-way down the page).
Note also that Jack Herrington wrote a whole book about code generation that uses Ruby/ERB.

Write a ruby script that does it. The API documentation is here:
http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/
For example:
template = ERB.new File.read("path/to/template.erb"), nil, "%"
template.result(binding)
(Where binding is a binding with the #vars that the template needs.)

Another option would be to use ruby -e, since ERB itslef is so simple.
Something like:
ruby -rerb -e "puts ERB.new(File.read(<file name here>)).result"
However, I assume you have a context you want to render the template in. How are you expecting to get that context? As an example, check out:
ruby -rerb -e "hello = 'hello'; puts ERB.new('<%= hello %> world').result(binding)"
which will print out "hello world", using the top-level, where you defined the hello variable, as the binding.

If you can switch ERB to Erubis, your problem solving is as simple as:
require 'erubis'
template = File.read("sample_file.erb")
template = Erubis::Eruby.new(template)
template.result(:your_variable => "sample")

Found this question while trying to test my Puppet templates.
Ended with this solution:
Along your foo.erb create a file foo.vars.erb
Put all your template variables into that new file, e.g.:
<% #my_param="foo bar" %>
<% #another_param=123 %>
or (equivalent):
<%
#my_param="foo bar"
#another_param=123
%>
On command line run this:
cat foo.vars.erb foo.erb | erb
Your fully rendered template should now be printed to std-out. From there you check the output by hand, or you can take diff (or other tools) to compare it to a pre-rendered output.

I tried to comment on this, but comments link not available.
I'm using this:
template = ERB.new File.new("path/to/template.erb").read, nil, "%"
template.result(binding)
From the posting above: and I found what I think it might be a problem:
I'm creating DOS BATCH files like:
%JAVA_HOME%\bin\jar -xvf <%=inputfile%>...
And I found weird thing problem - I get this when I run with the code above:
Processing Template test.txt
erb):2:in `render': compile error (SyntaxError)
erb):2: syntax error, unexpected tSTRING_BEG, expecting $end
erbout.concat "\n"
^
from DBUser.rb:49:in `render'
from DBUser.rb:43:in `each'
from DBUser.rb:43:in `render'
from DBUser.rb:81
I tried the following, and got round my particular problem - not sure if this is the right answer for everybody ...
template = ERB.new File.new("path/to/template.erb").read
template.result(binding)

Related

How to write Rspec test for running file from command line?

I have a Ruby project with a UNIX executable file called parse located in a bin subfolder in my project root directory.
At the moment it's just this:
#!/usr/bin/env ruby
# frozen_string_literal: true
puts 'hello world'
The file can be executed on the command line when this command is run from the project root directory: bin/parse
It works fine, but I also want to write a passing Rspec test for it.
I have this spec file:
RSpec.describe "end-to-end application behaviour" do
subject { system('bin/parse') }
it 'prints the expected messsage to stdout' do
expect { subject }.to output(
'hello world'
).to_stdout
end
end
When I run it I get the test failure:
expected block to output "hello world" to stdout, but output nothing
This is the location of my spec file relative to my project root: spec/integration/parse_spec.rb
I tried placing require and require_relative statements in that spec file with the paths to the parse executable, in case that would help, but I just kept getting:
LoadError: cannot load such file
Does anyone know how I can write a test in that file that will pass and prove the parse executable behaviour works?
Don't Use the RSpec Output Matcher
RSpec has a built-in output matcher than can test both where output goes, as well as its contents. However, it's testing where your Ruby output goes, not whether some external application is using standard input or standard error. You're going to have to make some different assumptions about your code.
You can avoid driving yourself nuts by comparing strings rather than testing the underlying shell or your output streams. For example, consider:
RSpec.describe "parse utility output" do
it "prints the right string on standard output" do
expect(`echo hello world`).to start_with("hello world")
end
it "shows nothing on standard output when it prints to stderr" do
expect(`echo foo >&2 > /dev/null`).to be_empty
end
end
Just replace the echo statements with the correct invocation of parse for your system, perhaps by setting PATH directly in your shell, using a utility like direnv, or by modifying ENV["PATH"] in your spec or spec_helper.
As a rule of thumb, RSpec isn't really meant for testing command-line applications. If you want to do that, consider using the Aruba framework to exercise your command-line applications. It's best to use RSpec to test the results of methods or the output of commands, rather than trying to test basic functionality. Of course, your mileage may vary.
Use ‍to_stdout_from_any_process instead of to_stdout:
expect { subject }.to output('hello world').to_stdout_from_any_process

Not able to get result for def using ruby on mac osx

This is just a sample method I have created for testing purpose using Ruby on Mac OSX 10.12 but I don't get the desired output: Can anyone suggest please? I tried getting the result using both paranthesis and without (). It doesn't even throw any error.
def hi
puts "Hello World"
End
hi
hi()
hi("Hello Matz")`
Try this:
def hi
puts "Hello World"
end
hi
hi()
And this:
def greet(greeting)
puts greeting
end
greet("Hello Matz")
Note that in this line:
hi("Hello Matz")`
you have a tick mark at the end, so that is an error:
1.rb:5: syntax error, unexpected tXSTRING_BEG, expecting end-of-input
It doesn't even throw any error.
Then you aren't running that program.
I suggest you open a Terminal window (Applications/Utilities/Terminal.app), and type in:
$ vimtutor
vim is a free computer programming editor that comes with your Mac. Do the tutorial and learn how to use vim. To run a ruby program, you enter your code into a file, then save it as, say, my_prog.rb. Then you need to give that file to ruby to execute it. You execute a ruby program like this:
$ ruby my_prog.rb
You can create a directory for all your ruby programs like this:
$ mkdir ruby_programs
$ cd ruby_programs
To create a new file inside that directory, use vim:
~/ruby_programs$ vi my_prog.rb
Once you are done typing in your code, save the file, which will put you back at the prompt in Terminal, then you can run your program:
~/ruby_programs$ ruby my_prog.rb
Once you get comfortable with vim, and you feel confident running your ruby programs, consider installing macvim with the vivid chalk color scheme:
It's nicer to look at than plain vim.
Try editing your file so that it reads:
def hi
puts "Hello World"
end
hi
Some important differences to note: def and end are both case-sensitive. The inside of the function definition is indented by two spaces. Since the function takes no arguments, no parentheses are necessary on the call to hi on line 4.
Depending on your filename, enter the command ruby FILENAME and you should see the output Hello World
Ruby keywords are case sensitive. Your code uses End and you probably wanted to use end to mark the end of the hi method.
Because End is not the same as end (and End is not a keyword), irb keeps waiting for input and treats the other three lines as part of the hi method. As far as it can tell, its definition is not complete until it reaches the end keyword (all non-capital letters.)
The correct way to define the method is:
def hi
puts "Hello World"
end
Then you can call it using either hi or hi().
Calling it as hi("Hello Matz") (or hi "Hello Matz") throws an ArgumentError exception with the message wrong number of arguments (given 1, expected 0) because it is called with one argument but the definition of method hi doesn't specify anything about arguments (by its definition, the method hi doesn't accept any argument).

How to prevent capistrano replacing newlines?

I want to run some shell scripts remotely as part of my capistrano setup. To test that functionality, I use this code:
execute <<SHELL
cat <<TEST
something
TEST
SHELL
However, that is actually running /usr/bin/env cat <<TEST; something; TEST which is obviously not going to work. How do I tell capistrano to execute the heredoc as I have written it, without converting the newlines into semicolons?
I have Capistrano Version: 3.2.1 (Rake Version: 10.3.2) and do not know ruby particularly well, so there might be something obvious I missed.
I think it might work to just specify the arguments to cat as a second, er, argument to execute:
cat_args = <<SHELL
<<TEST
something
TEST
SHELL
execute "cat", cat_args
From the code #DavidGrayson posted, it looks like only the command (the first argument to execute) is sanitized.
I agree with David, though, that the simpler way might be to put the data in a file, which is what the SSHKit documentation suggests:
Upload a file from a stream
on hosts do |host|
file = File.open('/config/database.yml')
io = StringIO.new(....)
upload! file, '/opt/my_project/shared/database.yml'
upload! io, '/opt/my_project/shared/io.io.io'
end
The IO streaming is useful for uploading something rather than "cat"ing it, for example
on hosts do |host|
contents = StringIO.new('ALL ALL = (ALL) NOPASSWD: ALL')
upload! contents, '/etc/sudoers.d/yolo'
end
This spares one from having to figure out the correct escaping sequences for something like "echo(:cat, '...?...', '> /etc/sudoers.d/yolo')".
This seems like it would work perfectly for your use case.
The code responsible for this sanitization can be found in SSHKit::Command#sanitize_command!, which is called by that class's initialize method. You can see the source code here:
https://github.com/capistrano/sshkit/blob/9ac8298c6a62582455b1b55b5e742fd9e948cefe/lib/sshkit/command.rb#L216-226
You might consider monkeypatching it to do nothing by adding something like this to the top of your Rakefile:
SSHKit::Command # force the class to load so we can re-open it
class SSHKit::Command
def sanitize_command!
return if some_condition
super
end
end
This is risky and could introduce problems in other places; for example there might be parts of Capistrano that assume that the command has no newlines.
You are probably better off making a shell script that contains the heredoc or putting the heredoc in a file somewhere.
Ok, so this is the solution I figured out myself, in case it's useful for someone else:
str = %x(
base64 <<TEST
some
thing
TEST
).delete("\n")
execute "echo #{str} | base64 -d | cat -"
As you can see, I'm base64 encoding my command, sending it through, then decoding it on the server side where it can be evaluated intact. This works, but it's a real ugly hack - I hope someone can come up with a better solution.

Passing binding or arguments to ERB from the command line

I have been playing around with erb from the command line recently. I wanted to make a dirt simple erb template, for example the following:
<%- name = "Joe"; quality = "fantastic" -%>
Hello. My name is <%= name %>. I hope your day is <%= quality %>.
This works if I run
erb -T - thatfile.erb
what I want to do is to make name and quality be passable from command line arguments, so that I could do something like:
./thatfile.erb "Bill" "super"
from the bash prompt and do the same thing.
I am aware that I could write a ruby script that would just read that template in and then use ERB.new(File.read("thatfile.erb")).result(binding), or writing the template after an END and doing likewise, but I'm looking for a more lightweight approach if it exists, because I don't want to write two files for each erb script that I create for this purpose.
Alternatively, you can use a ruby script and load it in as a library.
# vars.rb
#hello = 'kirk'
# template.html.erb
<div><%= #hello %></div>
$ erb -r './vars' template.html.erb
Please note that Ruby 2.2 and newer provide a much nicer solution that was implemented according to this:
erb var1=val1 var2=val2 my-template.erb
I went with the BASH command-line shortcut for environmental variables.
Outside:
STUFF=foo,bar erb input.html.erb >output.html
Inside:
<%
stuff = ENV['STUFF'].split(',')
%>
After a few minutes e-searching I determined the other solutions are all variations on "write the erb wrapper command yourself." Could be wrong, but I ain't going back.
If you are using unix, try following:
$ cat 1.erb
Hello. My name is <%= name %>. I hope your day is <%= quality %>.
$ (echo '<% name="Joe"; quality="fantastic" %>' && cat 1.erb) | erb
Hello. My name is Joe. I hope your day is fantastic.

How can I pass instance variables to a HAML template on the command line?

Background
I'm trying to test the formatting of some HAML templates outside of Rails. The idea is to pass in some instance variables on the command line or via an included Ruby file, rendering the template to standard output. I tried this several different ways without success, as outlined below.
Requiring a Ruby File
For example, given the following two files:
HAML template: "test.haml"
!!!
%h1 Testing HAML CLI
%p= #bar
%p= #baz
Ruby file: "test.rb"
#foo = 'abc'
#bar = '123'
I would expect an invocation like haml -r ./test test.haml to return an interpolated HTML file on standard output, but it doesn't. Instead, I get just the HTML:
<!DOCTYPE html>
<h1>Testing HAML CLI</h1>
<p></p>
<p></p>
Programmatic Attempt
Since this didn't work, I also tried to do this programmatically. For example:
#!/usr/bin/env ruby
require 'haml'
#foo = 'abc'
#bar = '123'
engine = Haml::Engine.new(File.read 'test.haml')
puts engine.render
with exactly the same results, e.g. just the HTML with no variable interpolation.
Restating the Question
Clearly, something else is needed to get HAML to render the template with its associated variables. I would prefer to do this from the command line, either by passing arguments or including a file. How should I be invoking HAML from the command line to make it happen?
If that's not possible for whatever reason, how should I invoke HAML programmatically to perform the interpolation without depending on Rails?
You can supply a scope object and a local variables hash to the render method. In your example case, you would call:
engine = Haml::Engine.new(File.read 'test.haml')
engine.render(Object.new, { :#foo => 'abc', :#bar => '123' })
The reason that both of these examples are not working is that you are attempting to access instance variables from a different class. The simplest solution is to define and use methods instead of attempting to access another classes instance variables as if they were your own.
I.E. in test.rb
def foo
'abc'
end
test.haml
!!!
%h1 Testing HAML CLI
%p= foo

Resources