How to avoid undefined method error for Nilclass - ruby

I use the dbf gem to read data out of an df file. I wrote some code:
# encoding: UTF-8
require 'dbf'
widgets = DBF::Table.new("patient.dbf")
widgets.each do |record|
puts record.vorname
end
Basically the code works but after ruby writes about 400 record.vorname to the console i get this error:
...
Gisela
G?nter
mycode.rb:5:in `block in <main>': undefined method `vorname' for nil:NilClass (NoM
ethodError)
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/dbf-2.0.6/lib/
dbf/table.rb:101:in `block in each'
......
My question is how can i avoid this error? Therefore it would be intresting why ( how you can see in the error) the record.vorname's with ä,ö,ü are displayed like ?,?,? for eg:
Günter is transformed to G?nter
Thanks

For some reason, your DBF driver returns nil records. You can pretend that this problem doesn't exist by skipping those.
widgets.each do |record|
puts record.vorname if record
end

About your question about the wrong chars, according to the dfb documentation:
Encodings (Code Pages)
dBase supports encoding non-english characters in different formats.
Unfortunately, the format used is not always set, so you may have to
specify it manually. For example, you have a DBF file from Russia and
you are getting bad data. Try using the 'Russion OEM' encoding:
table = DBF::Table.new('dbf/books.dbf', nil, 'cp866')
See doc/supported_encodings.csv for a full list of supported
encodings.
So make sure you use the right encoding to read from the DB.

To avoid the NoMethodError for nil:Nil Class you can probably try this:
require 'dbf'
widgets = DBF::Table.new("patient.dbf")
widgets.each do |record|
puts record.vorname unless record.blank?
end

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.

Can't open a file in docx gem when I call it through a string

I am using the docx gem to read a docx file. The code works when I write it like this:
require 'docx'
doc = Docx::Document.open('example.docx')
puts doc
It prints the doc perfectly. However, I need to get the path from the user through a gets. I need to do this:
require docx
puts "Provide the path of the document:"
document_path = gets.chomp.tr(" ", "") #it makes sure that any accidental whitespace is removed.
doc = Docx::Document.open(document_path)
puts doc
With this code I expect to get the same result that with the former one. The only difference is that I call the docx document to open through a string, not explicitly. Instead, I get this error:
/var/lib/gems/2.3.0/gems/rubyzip-1.1.7/lib/zip/file.rb:82:in `initialize': File '/root/Documents/Projects/Wordsworth/example.docx' (Zip::Error)
not found
from /var/lib/gems/2.3.0/gems/rubyzip-1.1.7/lib/zip/file.rb:96:in `new'
from /var/lib/gems/2.3.0/gems/rubyzip-1.1.7/lib/zip/file.rb:96:in `open'
from /var/lib/gems/2.3.0/gems/docx-0.2.07/lib/docx/document.rb:25:in `initialize'
from /var/lib/gems/2.3.0/gems/docx-0.2.07/lib/docx/document.rb:50:in `new'
from /var/lib/gems/2.3.0/gems/docx-0.2.07/lib/docx/document.rb:50:in `open'
from test.rb:17:in `<main>'
I visited the docx gem github page but in the examples it gives the docx called is always explicitly written by the coder, never a string. I hope I can get some help. Thanks a lot!

Ruby chomp!.strip! in one line

I'm trying to scrape a table from a website.
status = recPage.css("#MainContent_GridView1").css("tr")[line].css("td")[2].text.chomp.strip
And I got this error for some of the rows.
undefined method `strip' for nil:NilClass (NoMethodError)
or
undefined method `chomp' for nil:NilClass (NoMethodError)
So I thought I could use chomp!.strip! to skip the nil values. But apparently it won't allow me to put those 2 into one line.
Is there a way to modify this?
How about:
status = recPage.css("#MainContent_GridView1").css("tr")[line].css("td")[2].text.chomp.strip rescue ""
It will set status to "";
You're getting these errors because there're nodes with .text=nil, also chomp! and strip! are meant to modify string in-place, not to ignore errors, and they return nil.
Best way is to check the presence of text (not oneliner, thought):
txt = recPage.css("#MainContent_GridView1").css("tr")[line].css("td")[2].text
txt = txt.chomp.strip if txt
In ruby 2.3+ you can use:
recPage.css("#MainContent_GridView1").css("tr")[line].css("td")[2].text&.chomp.&strip
rescue ""-method could lead to errors in the future (if there are some other errors in this line)
If you really want to do it destructively, you can do:
...text&.tap(&:chomp!)&.tap(&:strip!)

ruby open pathname with specified encoding

I am trying to open files telling Ruby 1.9.3 to treat them as UTF-8 encoding.
require 'pathname'
Pathname.glob("/Users/Wes/Desktop/uf2/*.ics").each { |f|
puts f.read(["encoding:UTF-8"])
}
The class documentation goes through several levels of indirection, so I am not sure I am specifying the encoding properly. When I try it, however, I get this error message
ICS_scanner_strucdoc.rb:4:in read': can't convert Array into Integer (TypeError)
from ICS_scanner_strucdoc.rb:4:inread'
from ICS_scanner_strucdoc.rb:4:in block in <main>'
from ICS_scanner_strucdoc.rb:3:ineach'
from ICS_scanner_strucdoc.rb:3:in `'
This error message leads me to believe that read is trying to interpret the open_args as the optional leading argument, which would be the length of the read.
If I put the optional parameters in, as in puts f.read(100000, 0, ["encoding:UTF-8"]) I get an error message that says there are too many arguments.
What is the appropriate way to specify only the encoding? Would it be correct to say that this is an inconsistency between the documentation and the behavior of the class?
Mac OS 10.8
rvm current reports "ruby-1.9.3-p484"
I'm not sure you want to specify encoding for path name or for file itself.
If it is latter, this maybe what you want.
Pathname.glob("/Users/Wes/Desktop/uf2/*.ics").each { |f|
puts File.open(f,"r:UTF-8")
}
With Pathname.read you can write like this.
Pathname.glob("/Users/Wes/Desktop/uf2/*.ics").each do |f|
path = Pathname(f)
puts path.read
end

How to execute local functions using code from external file in ruby?

Can a require execute a locally defined function? I guess the easiest way to describe what I need is to show an example.
I'm using ruby 1.9.3, but solutions for 1.8 and 2.0 are also welcome.
I have a file main.rb as the following:
class Stuff
def self.do_stuff(x)
puts x
end
require_relative('./custom.rb')
do_stuff("y")
end
And also have a file custom.rb in the same folder, with the following content:
do_stuff("x")
Running main.rb, I have following output:
/home/fotanus/custom.rb:1:in `<top (required)>': undefined method `do_stuff' for main:Object (NoMethodError)
from main.rb:5:in `require_relative'
from main.rb:5:in `<class:Stuff>'
from main.rb:1:in `<main>'
Note that without the require, the output is y.
I'm not sure if it is the best solution but using eval should do the trick.
class Stuff
def self.do_stuff(x)
puts x
end
eval(File.read('./custom.rb'))
do_stuff("y")
end
The output will be:
pigueiras#pigueiras$ ruby stuff.rb
x
y
In C, #include literally drops the code as-is into the file. require in Ruby is different: it actually runs the code in the required file in its own scope. This is good, since otherwise we could break required code by redefining things before the require.
If you want to read in the contents of a script and evaluate it in the current context, there are methods for doing just that: File.read and eval.

Resources