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

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

Related

How to use login credentials from yaml in ruby

I'm new in ruby and I can't move forward from using login cred. from a yml file for a ruby project .I have a basic yml file
login:
urls:
gmail: "https://accounts.google.com/signin"
users:
username: something
password: something_new
I've created a yml.rb with require yml ,and access the yml path & loading the file .
But I don't know how to go through users/username ... in my test.rb :( .I've added in the "it " a variable to store the yml class and at the end i'm trying with
expect data['valid_user']
expect data['login']['urls']['gmail']
expect data['login']['users']['username']
but in the terminal I receive th error "NoMethodError: undefined method `[]' for nil:NilClass "
Update
Here is my yml.rb
require 'rspec'
require 'yaml'
class YamlHelper
#env = {}
def initialize
file = "#{Dir.pwd}path of yml file"
#env = YAML.load_file(file)
end
def get_variables
#env
end
end
Here is my test.rb file
describe 'My behaviour' do
before(:each) do
#browser = Selenium::WebDriver.for :firefox
end
it 'verifies yml login' do
yaml_helper = YamlHelper.new
data = yaml_helper.get_variables
expect data['valid_user']
expect test_data['login']['urls']['gmail']
expect test_data['login']['users']['username']
expect test_data['login']['users']['password']
end
after(:each) do #browser.quit
end
Can anyone take a look ?
thanks in advance
Have a lovely day
It looks like the code is almost there. When I'm debugging this sort of thing I'll often try to distil it down to the most basic test first
Something like:
require 'yaml'
file = "#{Dir.pwd}/data.yml"
data = YAML.load_file(file)
data['valid_user']
#=> nil
data['login']['urls']['gmail']
#=> "https://accounts.google.com/signin"
data['login']['users']['username']
#=> "something"
From the above you can see there's probably a typo in your test.rb file: test_data should most likely be data. Also, your YAML file doesn't contain the valid_user key, so you probably want to remove it from the test, at least for now.
The other two keys load fine.
The error you're seeing NoMethodError: undefined method '[]' for nil:NilClass means that one of the hashes you're variables you're treating like a hash is actually nil. This sort of bug is fairly common when you're diving into nested hashes. It means one of two things:
You've correctly descended into the hash, but the data is not present in the YAML.
The data is present in the YAML, but you're not getting to it correctly.
One change you can make that will make this code a bit more resilient is to replace:
test_data['login']['users']['username']
with:
test_data.dig('login', 'users', 'username')
The latter uses dig, which delves into the data structure and tries to return the value you're after, but if it gets a nil back at any point it'll just return nil, rather than throwing an exception.
Finally, for the test you've pasted here, you don't need the before(:each) or after(:each) blocks – Selenium is only necessary for browser testing.

Using Ruby and Sinatra, Is it possible to use HAML in an "internal" or "inline" manner?

I've done gem install sinatra and gem install haml
And I have this .rb file
require 'sinatra'
get '/abc2' do
"<b>aaaaaaaaaaaaa</b>"
end
Now say I want that line of HTML but in HAML. and not externally
I know I can do
get '/abc' do
haml :index # /views/index.haml
end
And index.haml could have a line of haml %b aaaaaaa
But is there any way that I can include %b aaaaaaa in my ruby file itself and have it rendered. Without having to refer to a file e.g. without having to refer to /views/index.haml ?
Like CSS lets you have the CSS External, or it lets you have it internal within the html file.. Similarly, Javascript lets you import javascript externally, or it lets you have it internal to the html file.. Well i'm asking about using HAML internally to the .rb file. Is it possible?
I know HAML is intended as a template language for injecting data into.. but it is also a shorthand for writing HTML more concisely.
For small apps you can add the views at the end of your script so there are no external files.
get '/abc' do
haml :hello
end
__END__
## hello
%h1= "Hello there"
You should be able to use Haml::Engine#render to convert the Haml to HTML like so:
get '/abc2' do
Haml::Engine.new(<<-Haml).render(binding)
%b aaaaaa
Haml
end
This uses a heredoc (everything between the <<-Haml line and the closing Haml. binding is a special variable that basically refers to the current scope.

How can I "require" code from another .rb file like in PHP?

Coming to Ruby from a PHP background, I'm used to being able to use require, require_once, include, or include_once which all have a similar effect, but the key being they continue to process code in the same scope where the include / require command was invoked.
Example:
sub.php
<?php
echo $foo;
main.php
<?php
$foo = 1234;
include('sub.php'); // outputs '1234'
When I first started using Ruby I tried to include / require / require_relative / load other .rb files, and after becoming a little frustrated with not having it work how I would expect it to I decided that there were better ways to go about breaking up large files and that Ruby didn't need to behave in the same way PHP did.
However, occasionally I feel that for testing purposes it would be nice to to load code from another .rb file in the way PHP does - in the same scope with access to all the same variables - without having to use class / instance variables or constants. Is this possible? Maybe somehow using a proc / binding / or eval command?
Again, I'm not advocating that this should be used during development - but I am curious if it is possible - and if so, how?
Yes, this is possible, although certainly not something I'd recommend doing. This works:
includer.rb:
puts var
include.rb:
var = "Hello!"
eval(File.read("include.rb"), binding)
Running this (Ruby 2.2.1, Ruby 1.9.3) will print Hello!. It works simply: eval takes an optional binding with which to evaluate the code it is passed, and Kernel#binding returns the current binding.
To have code run in same binding, you could simply eval the file contents as follows:
example.rb
class Example
def self.called_by_include
"value for bar"
end
def foo
puts "Called foo"
end
eval( File.read( 'included.rb' ) )
end
Example.new.bar
included.rb
BAR_CONSTANT = called_by_include
def bar
puts BAR_CONSTANT
end
Running ruby example.rb produces output
value for bar
The important thing is the eval( File.read( 'included.rb' ) ) code, which if you really wanted you could define as a class method on Object, to allow arbitrary source to be included with a convenience function*. The use of constants, class variables etc just shows influences working in both directions between the two pieces of source code.
It would be bad practice to use this in any production code. Ruby gives you much better tools for meta-programming, such as ability to use mix-ins, re-open classes, define methods from blocks etc.
* Something like this
class Object
def self.include_source filename
eval( File.read( filename ) )
end
end
And the line in example.rb would become just
include_source 'included.rb'
Again I have to repeat this is not such a great idea . . .
To import external .rb file in your code, I'm not sure but I think it have to be a gem.
Use require followed by the name of the gem you want to import.
Example
require 'foobar'
# do some stuff
Or you can use load to import entire rb file
load 'foobar.rb'
# do some stuff
Good luck and sorry for my english

How do I turn a hash to a string in Puppet DSL?

I have a hash of hashes that I need to embed in an exec resource command. My thought was to serialize the hash to a string and interpolate it into the exec call. The exec call will be executing ruby code via ruby -e 'ruby code here'.
Using irb, I know that hash.to_s creates a single line parse-able version of the hash. Or I could use json. I doubt you can call to_s in puppet, but am not sure.
The stdlib for Puppet has parseyaml and parsejson to deserialize, but is there a way to serialize to a parse-able string? I can write a custom puppet function to do it, but prefer an already built in solution if there is one.
Update
I am considering defining a puppet function. I have never written one before, so am not sure of the syntax. Here is my first attempt:
Puppet::Parser::Functions.newfunction(
:serialize_hash,
:arity => 2,
:doc => "Serialize a hash to any depth and optionally escape the double quotes.",
:type => :rvalue) do |args|
hash = args[0]
escape_quotes = args[1]
serialized = hash.to_s
if (escape_quotes)
serialized.sub!(/"/, "\\\"")
end
serialized
end
You can always execute ruby code inline with your puppet module:
$my_string = inline_template('<%= #my_hash.to_s %>')
Obviously it is important to not overuse this, but it is particularly useful when a very simple ruby function can achieve what you need.

Ruby TDD with Rspec (Basic Questions)

I am trying to run a very basic test with Terminal and Sublime Text 3. My simple test runs, but fails (undefined local variable or method 'x')
My folder hierarchy looks like this:
spec_helper.rb looks like this:
require_relative '../test'
require 'yaml'
test_spec.rb is extremely basic
require 'spec_helper.rb'
describe "testing ruby play" do
it "finds if x is equal to 5" do
x.should eql 5
end
end
and my test.rb file has x = 5 That's it.
Will a variable only be recognizable if it's part of a class? And do I need to call a new class every time I run my test?
From the docs
require(name) → true or false
Loads the given name, returning true if successful and false if the feature is already
loaded.
[snip]
Any constants or globals within the loaded source file will be
available in the calling program’s global namespace. However, local
variables will not be propagated to the loading environment.
You could use a constant in your required file:
X = 5
...
X.should eql 5 # => passes
But you probably want to do something entirely different here. Perhaps you could expand on the question and explain what you are trying to accomplish.

Resources