I can't get headers when I parse csv file with Ruby - ruby

I can't get headers. No error happens. How can I get it properly?
Data:
"A","B","C"
"1","2","3"
Ruby:
require "csv"
table = CSV.read("filename", :headers => true)
puts table[0] # "1","2","3"
puts table[headers] # Nothing happens.

Try table.headers:
irb(main):006:0> table.headers
=> ["A", "B", "C"]
How are you running this code? table[headers] should return an error:
irb(main):008:0> table[headers]
NameError: undefined local variable or method `headers' for main:Object
from (irb):8
from /usr/bin/irb:12:in `<main>'

Related

undefined method `+' for nil:NilClass (NoMethodError) ruby

trying to build a pptx to scorm converter. I get an error NoMethodError Undefined method `+' for NilClass. I guess it may be due to a defined method. any idea on how i can remove this error ?
dir = ARGV.shift
dest = ARGV.shift
pptx = dir + "/presentation.pptx"
lis = []`enter code here`
STDERR.puts "Copy template => #{dest}"
FileUtils.cp_r "template", dest
Dir["#{dir}/*.PNG"].each do |file|
STDERR.puts "Copy #{file} => #{dest}/img"
FileUtils.cp file, "#{dest}/img/"
STDERR.puts "Creating thumb #{file} => #{dest}/img/thumb"
name = file.split(/\//).last
system "/usr/bin/convert", "-scale", "200x", file, "#{dest}/img/thumb/#{name}"
lis.push name
end
ordered = lis.sort_by { |x| x[/\d+/].to_i }
DIR is nil
If you debug your code as follows:
puts dir.nil? # true
So, in order to run this code you must provide the ruby shell with 2 arguments, as follows:
ruby test.rb DIRECTORY_NAME DESTINATION_NAME

Parsing through nested hash using .present? - undefined method `[]' for nil:NilClass (NoMethodError)

***EDITED 2nd time to show that I need to handle looking in multiple locations.
EDITED to show exception being raise even when handler built in.
I am currently parsing through responses from an API that includes arrays that I've converted into a hash using
hash_table = xml_response.to_h
The challenge is that sometimes the data I'm looking for is located in different locations, and when I use a key method:
data_i_need = hash_table['key1']['key2'][0]
if there's nothing there, it throws this error:
undefined method `[]' for nil:NilClass (NoMethodError)
I've tried using:
if hash_table['key1']['key2'][0].present?
data_i_need = hash_table['key1']['key2'][0]
puts "data was here"
elsif hash_table['key3']['key4'][0].present?
data_i_need = hash_table['key3']['key4'][0]
puts "data here"
elsif hash_table['key5']['key6'][0].present?
data_i_need = hash_table['key5']['key6'][0]
puts "data here"
else
"data not found"
end
But it throws the same error:
undefined method `[]' for nil:NilClass (NoMethodError)
you should check for the presence all the previous hash key because if one of them is nil, the exception will be raised
hash_table['key1'].present? && hash_table['key1']['key2'].present? && hash_table['key1']['key2']['key3'].present? && hash_table['key1']['key2']['key3'][0].present?
Update:
To return "not found", you can catch the exception like this:
data_i_need = begin
hash_table['key1']['key2']['key3'][0]
rescue NoMethodError
"data not found"
end
Update 2:
You can use this function to check if the keys exist in the hash in the if else condition statements:
h = {:a => {:b => {0 => 1}}, :c => 2}
def has_nested_keys?(hash, *keys)
keys.inject(hash) do |result, key|
result = result[key]
end
true
rescue
false
end
has_nested_keys?(h, :a, :b, 0) #=> true
has_nested_keys?(h, :c, :d, 0) #=> false

How to scrape data from list of URLs and save data to CSV with nokogiri

I have a file called bontyurls.csv that looks like this:
http://bontrager.com/model/11383
http://bontrager.com/model/01740
http://bontrager.com/model/09595
I want my script to read that file and then spit out a file like this: bonty_test_urls_results.csv
url,model_names
http://bontrager.com/model/11383,"Road TLR Conversion Kit"
http://bontrager.com/model/01740,"404 File Not Found"
http://bontrager.com/model/09595,"RXL Road"
Here's what I've got so far:
# based on code from here: http://www.andrewsturges.com/2011/09/how-to-harvest-web-data-using-ruby-and.html
require 'nokogiri'
require 'open-uri'
require 'csv'
#urls = Array.new
#model_names = Array.new
urls = CSV.read("bontyurls.csv")
(0..urls.length - 1).each do |index|
puts urls[index][0]
doc = Nokogiri::HTML(open(urls[index][0]))
doc.xpath('//h1').each do |model_name|
#model_name << model_name.content
end
end
# write results to file
CSV.open("bonty_test_urls_results.csv", "wb") do |row|
row << ["url", "model_names"]
(0..#urls.length - 1).each do |index|
row << [
#urls[index],
#model_names[index]]
end
end
That code isn't working. I'm getting this error:
$ ruby bonty_test_urls.rb
http://bontrager.com/model/00310
bonty_test_urls.rb:15:in `block (2 levels) in <main>': undefined method `<<' for nil:NilClass (NoMethodError)
from /home/simon/.rvm/gems/ruby-1.9.3-p194/gems/nokogiri-1.5.5/lib/nokogiri/xml/node_set.rb:239:in `block in each'
from /home/simon/.rvm/gems/ruby-1.9.3-p194/gems/nokogiri-1.5.5/lib/nokogiri/xml/node_set.rb:238:in `upto'
from /home/simon/.rvm/gems/ruby-1.9.3-p194/gems/nokogiri-1.5.5/lib/nokogiri/xml/node_set.rb:238:in `each'
from bonty_test_urls.rb:14:in `block in <main>'
from bonty_test_urls.rb:11:in `each'
from bonty_test_urls.rb:11:in `<main>'
Here is some code that returns the model_name at least. I'm just having trouble getting it to work in the larger script:
require 'open-uri'
require 'nokogiri'
doc = Nokogiri::HTML(open("http://bontrager.com/model/09124"))
doc.xpath('//h1').each do |node|
puts node.text
end
Also, I haven't figured out how to handle the URLs that return a 404.
This is how I'd do it:
require 'csv'
require 'nokogiri'
require 'open-uri'
CSV_OPTIONS = {
:write_headers => true,
:headers => %w[url model_names]
}
CSV.open('bonty_test_urls_results.csv', 'wb', CSV_OPTIONS) do |csv|
csv_doc = File.foreach('bontyurls.csv') do |url|
url.chomp!
begin
doc = Nokogiri.HTML(open(url))
h1 = doc.at('h1').text.strip
h1 = doc.at('title').text.strip.sub(/^Bontrager: /i, '') if (h1.empty?)
csv << [url, h1]
rescue OpenURI::HTTPError => e
csv << [url, e.message]
end
end
end
Which generates a CSV file like:
url,model_names
http://bontrager.com/model/11383,Road TLR Conversion Kit (Model #11383)
http://bontrager.com/model/01740,404 File Not Found
http://bontrager.com/model/09595,RXL Road (Model #09595)
You declare #model_names, but try to push in to #model_name, which is why it's nil.

How do I debug an undefined method for nil class error?

I'm trying to create a classifier using the cardmagic classifier gem. This is my code:
require 'classifier'
classifications = '1007.09', '1006.03'
traindata = Hash["1007.09" => "ADAPTER- SCREENING FOR VALVES VBS", "1006.03" => "ACTUATOR- LINEAR"]
b = Classifier::Bayes.new classifications
traindata.each do |key, value|
b.train(key, value)
end
But when I run this I get the following error:
Notice: for 10x faster LSI support, please install http://rb-gsl.rubyforge.org/
c:/Ruby192/lib/ruby/gems/1.9.1/gems/classifier-1.3.3/lib/classifier/bayes.rb:27:in `block in train': undefined method `[]' for nil:NilClass (NoMethodError)
from c:/Ruby192/lib/ruby/gems/1.9.1/gems/classifier-1.3.3/lib/classifier/bayes.rb:26:in `each'
from c:/Ruby192/lib/ruby/gems/1.9.1/gems/classifier-1.3.3/lib/classifier/bayes.rb:26:in `train'
from C:/_Chris/Code/classifier/smdclasser.rb:13:in `block in <main>'
from C:/_Chris/Code/classifier/smdclasser.rb:11:in `each'
from C:/_Chris/Code/classifier/smdclasser.rb:11:in `<main>'
This is the source from the gem code:
# Provides a general training method for all categories specified in Bayes#new
# For example:
# b = Classifier::Bayes.new 'This', 'That', 'the_other'
# b.train :this, "This text"
# b.train "that", "That text"
# b.train "The other", "The other text"
def train(category, text)
category = category.prepare_category_name
text.word_hash.each do |word, count|
#categories[category][word] ||= 0
#categories[category][word] += count
#total_words += count
end
end
I am lost where to go to troubleshoot this error, what is the next step I should take?
Classifier::Bayes.new expects an exploded array of values, rather than a single parameter. For example, notice that the sample code uses:
b = Classifier::Bayes.new 'This', 'That', 'the_other'
rather than:
b = Classifier::Bayes.new ['This', 'That', 'the_other']
Pass in the splat version of your classifications array and it should work:
b = Classifier::Bayes.new *classifications

Problem using OpenStruct with ERB

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.

Resources