Problem using OpenStruct with ERB - ruby

EDIT: forgot to include my environment info... Win7x64, RubyInstaller Ruby v1.9.1-p378
EDIT 2: just updated to v1.9.1, patch 429, and still getting this same error.
Edit 3: running this same code in Ruby v1.8.7, patch 249, works fine. so it's v1.9.1 that broke it, apparently.
I'm new to using ERB and the samples i could find are... ummm... less than helpful... having played around with ERB for about an hour, I got some basic examples working (finally), but I have no idea why this doesn't work...
require 'ostruct'
require 'erb'
data = {:bar => "bar"}
vars = OpenStruct.new(data)
template = "foo "
erb = ERB.new(template)
vars_binding = vars.send(:binding)
puts erb.result(vars_binding)
this code produces the following error:
irb(main):007:0> puts erb.result(vars_binding)
NameError: undefined local variable or method `bar' for main:Object
from (erb):1
from C:/Ruby/v1.9.1/lib/ruby/1.9.1/erb.rb:753:in `eval'
from C:/Ruby/v1.9.1/lib/ruby/1.9.1/erb.rb:753:in `result'
from (irb):7
from C:/Ruby/v1.9.1/bin/irb:12:in `'
why is it looking at the main:Object binding? I told it to use the binding from the OpenStruct by passing in vars_binding
can someone fill me in on why it doesn't work, and help me get it to work?

The problem is where the binding is being executed. The 1.8.7-way obj.send(:binding) does not work anymore (see issue2161), the environment must be the object itself. So use instance_eval:
require 'ostruct'
require 'erb'
namespace = OpenStruct.new(:first => 'Salvador', :last => 'Espriu')
template = 'Name: <%= first %> <%= last %>'
ERB.new(template).result(namespace.instance_eval { binding })
#=> Name: Salvador Espriu
More about this issue in this answer.

Fix to Problem:
I stumbled upon this question when encountering the same type of error with similar code in Ruby 1.9.2.
I'm new to Ruby so I can't explain what is happening. I continued to search online and found this blog post that has an approach that seems to work. After modifying your example to incorporate this approach I end up with the following, working, code:
require 'ostruct'
require 'erb'
class ErbBinding < OpenStruct
def get_binding
return binding()
end
end
data = {:bar => "baz"}
vars = ErbBinding.new(data)
template = "foo <%= bar %>"
erb = ERB.new(template)
vars_binding = vars.send(:get_binding)
puts erb.result(vars_binding)
Additional Information:
When the code is run thru the IRB, I get:
require 'ostruct'
=> true
require 'erb'
=> true
class ErbBinding < OpenStruct
def get_binding
return binding()
end
end
=> nil
data = {:bar => "baz"}
=> {:bar=>"baz"}
vars = ErbBinding.new(data)
=> #<ErbBinding bar="baz">
template = "foo <%= bar %>"
=> "foo <%= bar %>"
erb = ERB.new(template)
=> #<ERB:0x2b73370 #safe_level=nil, #src="#coding:IBM437\n_erbout = ''; _erbout.concat \"foo \"; _erbout.concat(( bar ).to_s); _erbout.force_encoding(__ENCODING__)", #enc=#<Encoding:IBM437>, #filename=nil>
vars_binding = vars.send(:get_binding)
=> #<Binding:0x2b6d418>
puts erb.result(vars_binding)
foo baz
=> nil

What's your environment look like? This code worked for me (I just changed the string "bar" to "baz" to disambiguate in my brain, and added it to the template):
require 'ostruct'
require 'erb'
data = {:bar => "baz"}
vars = OpenStruct.new(data)
template = "foo <%= bar %>"
erb = ERB.new(template)
vars_binding = vars.send(:binding)
puts erb.result(vars_binding)
When I run it, I get:
defeateds-MacBook-Pro:Desktop defeated$ ruby erb.rb
foo baz
Under 1.8.7 on OSX:
defeateds-MacBook-Pro:Desktop defeated$ ruby -v
ruby 1.8.7 (2009-06-08 patchlevel 173) [universal-darwin10.0]

Looks like this does not work with higher ruby versions
with ruby 2.1.1
[19] pry(main)> name = "samtoddler"
=> "Suresh"
[20] pry(main)> template_string = "My name is <%= name %>"
=> "My name is <%= name %>"
[21] pry(main)> template = ERB.new template_string
=> #<ERB:0x007fadf3491c38
#enc=#<Encoding:UTF-8>,
#filename=nil,
#safe_level=nil,
#src="#coding:UTF-8\n_erbout = ''; _erbout.concat \"My name is \"; _erbout.concat(( name ).to_s); _erbout.force_encoding(__ENCODING__)">
[22] pry(main)> puts template.result
NameError: undefined local variable or method `name' for main:Object
from (erb):1:in `<main>'
with ruby 1.9.3
[2] pry(main)> name = "samtoddler"
=> "Suresh"
[3] pry(main)> template_string = "My name is <%= name %>"
=> "My name is <%= name %>"
[4] pry(main)> template = ERB.new template_string
=> #<ERB:0x007f9be2a1fdf8
#enc=#<Encoding:UTF-8>,
#filename=nil,
#safe_level=nil,
#src=
"#coding:UTF-8\n_erbout = ''; _erbout.concat \"My name is \"; _erbout.concat(( name ).to_s); _erbout.force_encoding(__ENCODING__)">
[5] pry(main)> puts template.result
My name is samtoddler
So it gives error but still works in 1.9.3 and all the versions below 1.9.3.

Related

Puppet ERB templates: How to inject variable into statement

I'd like to make some modifications on variable in erb templates:
e.g. decode base64 and split string
It tried
<%= Base64.decode64(#my_variable).rpartition(':').last %>
and
<%= Base64.decode64(<%= #my_variable %>).rpartition(':').last %>
and via scope
<%= Base64.decode64(scope['my_class::my_variable']).rpartition(':').last %>
but it is nil.
Filepath: /usr/lib/ruby/1.9.1/base64.rb
Line: 58
Detail: undefined method `unpack' for nil:NilClass
puppet version 3.8.2
The first example that you tried is the correct ERB notation.
That error would be occurring because you passed the nil object to Base64.decode64():
[1] pry(main)> require 'base64'
=> true
[2] pry(main)> Base64.decode64(nil).rpartition(':').last
NoMethodError: undefined method `unpack' for nil:NilClass
from /Users/alexharvey/.rvm/rubies/ruby-2.1.4/lib/ruby/2.1.0/base64.rb:58:in `decode64'
(Admittedly, I don't have your Ruby 1.9.1 and I wasn't able to easily install it, but it's unlikely this changed.)
That means that #my_variable is set to nil, and that would happen if you failed to actually set the Puppet variable $my_variable in your manifest.
So you would need to call the template with a block something like this:
$my_variable = 'foo'
file { '/tmp/foo':
ensure => file,
content => template('test/mytemplate.erb')
}

How do I use instance variables initialized in my initialize method in other methods?

This is what my class looks like:
require 'oga'
require 'net/http'
require 'pry'
module YPCrawler
class PageCrawler
def initialize(url)
#url = 'http://www.someurl.com'
end
def get_page_listings
body = Net::HTTP.get(URI.parse(#url))
document = Oga.parse_html(body)
bizlistings = document.css('div.result')
binding.pry
end
end
end
Yet when I get thrown into pry, I see this:
[1] pry(YPCrawler::PageCrawler)> #url
=> nil
[2] pry(YPCrawler::PageCrawler)> body
NameError: undefined local variable or method `body' for YPCrawler::PageCrawler:Class
from (pry):2:in `<class:PageCrawler>'
[3] pry(YPCrawler::PageCrawler)> document
NameError: undefined local variable or method `document' for YPCrawler::PageCrawler:Class
from (pry):3:in `<class:PageCrawler>'
[4] pry(YPCrawler::PageCrawler)> bizlistings
NameError: undefined local variable or method `bizlistings' for YPCrawler::PageCrawler:Class
from (pry):4:in `<class:PageCrawler>'
[5] pry(YPCrawler::PageCrawler)> url
NameError: undefined local variable or method `url' for YPCrawler::PageCrawler:Class
Did you mean? URI
from (pry):5:in `<class:PageCrawler>'
[6] pry(YPCrawler::PageCrawler)> #url
=> nil
Why can I not access #url that was initialized in my def initialize method?
Edit 1
Added Screenshots of what my code and the terminal PRY session really look like, since there was some disbelief about the position of my binding.pry.
Edit 2
My main lib/yp-crawler.rb file looks like this:
require_relative "yp-crawler/version"
require_relative "yp-crawler/page-crawler"
require_relative "yp-crawler/listing-crawler"
module YPCrawler
end
So the code that is run above is my yp-crawler/page-crawler.rb file, which I included in my lib/yp-crawler.rb file.
Edit 3
Here is a recording of my entire workflow. Please tell me what I am missing:
https://www.dropbox.com/s/jp1abthfkiplb4p/Pry-not-cooperating.webm?dl=0
I bet your code looks as follows:
module YPCrawler
class PageCrawler
attr_reader :url
def initialize(url)
#url = 'http://www.someurl.com'
end
def get_page_listings
body = Net::HTTP.get(URI.parse(#url))
document = Oga.parse_html(body)
bizlistings = document.css('div.result')
end
binding.pry
end
end
Even though you might have moved the binding.pry into method, most likely you did not reload the console, so it executes the "wrong" version.
From your screenshots it is clear that either file is not reloaded or you just made changes to wrong file.

Using a Ruby Module for Defaults

I'd like to use a Ruby Module to store a set of configuration defaults.
I'm having some problems using the values and hope someone could help.
This may not be the best way to do this but this is what I've come up with so far.
Here is a module to hold value for a persons Resume => resume.rb
module Resume
require 'ostruct'
attr_reader :personal, :education
#personal = OpenStruct.new
#education = Array.new
def self.included(base)
set_personal
set_education
end
def self.set_personal
#personal.name = "Joe Blogs"
#personal.email_address = 'joe.blogs#gmail.com'
#personal.phone_number = '5555 55 55 555'
end
def self.set_education
#education << %w{ School\ 1 Description\ 1 }
#education << %w{ School\ 2 Description\ 2 }
end
end
From irb it works fine:
% irb -I .
1.9.3-p194 :001 > require 'resume'
=> true
1.9.3-p194 :002 > include Resume
=> Object
1.9.3-p194 :003 > puts Resume.personal.name
Joe Blogs
=> nil
However when I include this into a class it throws and error => build.rb
require 'resume'
class Build
include Resume
def build
puts Resume.personal.name
end
end
From irb:
% irb -I .
1.9.3-p194 :001 > require 'build'
=> true
1.9.3-p194 :002 > b = Build.new
=> #<Build:0x00000001d0ebb0>
1.9.3-p194 :003 > b.build
NoMethodError: undefined method `personal' for Resume:Module
from /home/oolyme/projects/lh_resume_builder_ruby/build.rb:7:in `build'
from (irb):3
from /home/oolyme/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `<main>'
I've tried a few variations to output the include module variables in the Build class instance but all error out.
attr_accessor creates a couple of instance methods, meaning that they will be available on an instance of Build. But you clearly want class instance methods. Change definition of your module to this:
module Resume
require 'ostruct'
def self.personal
#personal
end
def self.education
#education
end
def self.included(base)
#personal = OpenStruct.new
#education = Array.new
set_personal
set_education
end
def self.set_personal
#personal.name = "Joe Blogs"
#personal.email_address = 'joe.blogs#gmail.com'
#personal.phone_number = '5555 55 55 555'
end
def self.set_education
#education << %w{ School\ 1 Description\ 1 }
#education << %w{ School\ 2 Description\ 2 }
end
end
And it'll work
b = Build.new
b.build # >> Joe Blogs

Template functions with HAML in Sinatra

I'd like to be able to create template functions in Sinatra HAML templates that themselves contain haml. Is there any way to do this or something similar? It'd be cool if it could work with markdown too.
foo.haml
def foo(x)
%h2 something
%p something about #{x}
%h1 Herp de derp
= foo("mary")
= foo("us")
Cheers!
Actually, you can do something like this:
# app.rb
require 'sinatra'
require 'haml'
helpers do
def foo(name)
haml = <<-HAML
#hello_block
Hello, #{name}
HAML
engine = Haml::Engine.new(haml)
engine.render
end
end
get '/' do
haml :index
end
# index.haml
= foo 'World'
Function is close, what you really need is what's known as a partial. These are predefined templates that you can place inside other views. For instance, you may have a comment partial to display a comment's author, timestamp, content, etc. You can then render this partial for each of the comments on a particular post.
Essentially, you'll end up with the following
# _foo.haml.erb
%h2 somthing
%p= x
# index.haml.erb
%h1 Herp de derp
= render :partial => "foo", :locals => { :x => "mary" }
= render :partial => "foo", :locals => { :x => "us" }

Ruby Net:LDAP- NoMethodError for attributes that don't exist

I'm doing a simple Net:LDAP search and when I'm outputting an entry's attribute that may not exist for every entry, I get an error "NoMethodError: undefined method 'some_attribute'"
Here is the code:
require 'rubygems'
require 'net/ldap'
ldap = Net::LDAP.new
ldap.host = 'ldap.example.com'
ldap.port = 389
if ldap.bind
filter = Net::LDAP::Filter.eq( "sn", "Smith" )
treebase = "ou=people,o=company"
ldap.search( :base => treebase, :filter => filter, :return_result => false) do |entry|
puts #{entry.some_attribute}
end
end
else
puts "bind unsuccessful"
end
I tried also doing:
if entry.respond_to?(some_attribute)
puts "#{entry.some_attribute}"
end
That didn't work, it returns as false for every entry (when some entries have the attribute).
Ruby is expecting a symbol in the respond_to? method call.
ruby-1.8.7-p299 > class Foo
ruby-1.8.7-p299 ?> attr_accessor :some_attr
ruby-1.8.7-p299 ?> end
=> nil
ruby-1.8.7-p299 > Foo.new.respond_to?(some_attr)
NameError: undefined local variable or method `some_attr' for #<Object:0xb77ce950>
from (irb):4
ruby-1.8.7-p299 > Foo.new.respond_to?(:some_attr)
=> true

Resources