Net::ftp getbinaryfile() saving to file vs saving to variable - ruby

Using the following ftp_download method works, but if I change
ftp.getbinaryfile(file,localdir,1024) #=> Saves the file to localdir
to
ftp.getbinaryfile(file) #=> returns nil
I get nil returned. According to
http://www.ruby-doc.org/stdlib-2.0/libdoc/net/ftp/rdoc/Net/FTP.html#method-i-getbinaryfile
inilf I set localfile to nil as above, the data should be retrieved and returned by the method. What am I doing wrong?
def ftp_download(domain,remotedir,filename_regex,user=nil,pwd=nil)
ftp = Net::FTP::new(domain)
if user && pwd
ftp.login(user, pwd)
end
ftp.chdir(remotedir)
fileList = ftp.nlst(filename_regex)
fileList.each do |file|
localdir=File.join(remotedir,file)
localdir=localdir[1..-1] if localdir[0]="/"
FileUtils.mkdir_p(File.dirname(localdir))
ftp.getbinaryfile(file,localdir,1024)
end
ftp.close
end

If you look at the getbinaryfile method signature you will notice that the default value for the second parameter (localfile) is not nil but File.basename(remotefile)
getbinaryfile(remotefile,
localfile=File.basename(remotefile),
blocksize=DEFAULT_BLOCKSIZE)
If you want localfile to be nil you have to pass it explicitly:
ftp.getbinaryfile(file, nil)

Related

Using variable declared in one method to open webpage in another method

I am working on a CLI Project and trying to open up a web page by using url variable declared in another method.
def self.open_deal_page(input)
index = input.to_i - 1
#deals = PopularDeals::NewDeals.new_deals
#deals.each do |info|
d = info[index]
#product_url = "#{d.url}"
end
#product_url.to_s
puts "They got me!"
end
def self.deal_page(product_url)
#self.open_deal_page(input)
deal = {}
html = Nokogiri::HTML(open(#product_url))
doc = Nokogiri::HTML(html)
deal[:name] = doc.css(".dealTitle h1").text.strip
deal[:discription] = doc.css(".textDescription").text.strip
deal[:purchase] = doc.css("div a.button").attribute("href")
deal
#binding.pry
end
but I am receiving this error.
`open': no implicit conversion of nil into String (TypeError)
any possible solution? Thank you so much in advance.
Try returning your #product_url within your open_deal_page method, because now you're returning puts "They got me!", and also note that your product_url is being created inside your each block, so, it won't be accessible then, try creating it before as an empty string and then you can return it.
def open_deal_page(input)
...
# Create the variable
product_url = ''
# Assign it the value
deals.each do |info|
product_url = "#{info[index].url}"
end
# And return it
product_url
end
In your deal_page method tell to Nokogiri to open the product_url that you're passing as argument.
def deal_page(product_url)
...
html = Nokogiri::HTML(open(product_url))
...
end

No implicit converson from nil to String

I am getting an error
ruby/2.1.0/open-uri.rb:36:in `open': no implicit conversion of nil into String (TypeError)
here #filename, #directory and #xmlFile all have String as class type if I print them.
But somehow still in eval_script the above error is thrown. I don't undertstand why?
def execute
...
#result = eval_script(#filename,#xmlFile,#directory)
end
def eval_script filename,xml,directory
proc = Proc.new{}
eval(File.read(filename),proc.binding, filename)
end
Edit:
1) execute method is my rails action controller method.
Script:
# encoding: UTF-8
require 'nokogiri'
require 'open-uri'
doc = Nokogiri::XML(open(ARGV.first))
path = ARGV[1]
print path
File.delete(path + "/testOut.txt") if File.exist?(path + "/testOut.txt")
file = File.open(path + "/testOut.txt", 'w')
doc.css('testcases').each { |node| file.write "#{node['name']}\n" if node.css('results[test="testOut"]').any? }
Well, there's your problem. Line 4 of your script is
doc = Nokogiri::XML(open(ARGV.first))
But there are no ARGV elements being passed, so you're trying to open nil
Since you have the binding available, just refer to the variables defined in the eval_script method.
doc = Nokogiri::XML(open(xml))

Minitest: How to stub/mock the file result of Kernel.open on a URL

I have been trying to use Minitest to test my code (full repo) but am having trouble with one method which downloads a SHA1 hash from a .txt file on a website and returns the value.
Method:
def download_remote_sha1
#log.info('Downloading Elasticsearch SHA1.')
#remote_sha1 = ''
Kernel.open(#verify_url) do |file|
#remote_sha1 = file.read
end
#remote_sha1 = #remote_sha1.split(/\s\s/)[0]
#remote_sha1
end
You can see that I log what is occurring to the command line, create an object to hold my SHA1 value, open the url (e.g. https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.4.2.deb.sha1.txt)
I then split the string so that I only have the SHA1 value.
The problem is that during a test, I want to stub the Kernel.open which uses OpenURI to open the URL. I would like to ensure that I'm not actually reaching out to download any file, but rather I'm just passing the block my own mock IO object testing just that it correctly splits stuff.
I attempted it like the block below but when #remote_sha1 = file.read occurs the file item is nil.
#mock_file = Minitest::Mock.new
#mock_file.expect(:read, 'd377e39343e5cc277104beee349e1578dc50f7f8 elasticsearch-1.4.2.deb')
Kernel.stub :open, #mock_file do
#downloader = ElasticsearchUpdate::Downloader.new(hash, true)
#downloader.download_remote_sha1.must_equal 'd377e39343e5cc277104beee349e1578dc50f7f8'
end
I was working on this question too, but matt figured it out first. To add to what matt posted:
When you write:
Kernel.stub(:open, #mock_file) do
#block code
end
...that means when Kernel.open() is called--in any code, anywhere before the stub() block ends--the return value of Kernel.open() will be #mock_file. However, you never use the return value of Kernel.open() in your code:
Kernel.open(#verify_url) do |f|
#remote_sha1 = f.read
end
If you wanted to use the return value of Kernel.open(), you would have to write:
return_val = Kernel.open(#verify_url) do |f|
#remote_sha1 = f.read
end
#do something with return_val
Therefore, the return value of Kernel.open() is irrelevant in your code--which means the second argument of stub() is irrelevant.
A careful examination of the source code for stub() reveals that stub() takes a third argument--an argument which will be passed to a block specified after the stubbed method call. You, in fact, have specified a block after your stubbed Kernel.open() method call:
stubbed method call -+ +- start of block
| | |
V V V
Kernel.open(#verify_url) do |f|
#remote_sha1 = f.read
end
^
|
end of block
So, in order to pass #mockfile to the block you need to specify it as the third argument to Kernel.stub():
Kernel.stub(:open, 'irrelevant', #mock_file) do
end
Here is a full example for future searchers:
require 'minitest/autorun'
class Dog
def initialize
#verify_url = 'http://www.google.com'
end
def download_remote_sha1
#remote_sha1 = ''
Kernel.open(#verify_url) do |f|
#remote_sha1 = f.read
end
#puts #remote_sha1[0..300]
#remote_sha1 = #remote_sha1.split(" ")[0] #Using a single space for the split() pattern will split on contiguous whitespace.
end
end
#Dog.new.download_remote_sha1
describe 'downloaded file' do
it 'should be an sha1 code' do
#mock_file = Minitest::Mock.new
#mock_file.expect(:read, 'd377e39343e5cc277104beee349e1578dc50f7f8 elasticsearch-1.4.2.deb')
Kernel.stub(:open, 'irrelevant', #mock_file) do
#downloader = Dog.new
#downloader.download_remote_sha1.must_equal 'd377e39343e5cc277104beee349e1578dc50f7f8'
end
end
end
xxx
The second argument to stub is what you want the return value to be for the duration of your test, but the way Kernel.open is used here requires the value it yields to the block to be changed instead.
You can achieve this by providing a third argument. Try changing the call to Kernel.stub to
Kernel.stub :open, true, #mock_file do
#...
Note the extra argument true, so that #mock_file is now the third argument and will be yielded to the block. The actual value of the second argument doesn’t really matter in this case, you might want to use #mock_file there too to more closely correspond to how open behaves.

Retrieve a file in Ruby

So what I am trying to do is pass a file name into a method and and check if the file is closed. What I am struggling to do is getting a file object from the file name without actually opening the file.
def file_is_closed(file_name)
file = # The method I am looking for
file.closed?
end
I have to fill in the commented part. I tried using the load_file method from the YAML module but I think that gives the content of the file instead of the actual file.
I couldn't find a method in the File module to call. Is there a method maybe that I don't know?
File#closed? returns whether that particular File object is closed, so there is no method that is going to make your current attempted solution work:
f1 = File.new("test.file")
f2 = File.new("test.file")
f1.close
f1.closed? # => true # Even though f2 still has the same file open
It would be best to retain the File object that you're using in order to ask it if it is closed, if possible.
If you really want to know if your current Ruby process has any File objects open for a particular path, something like this feels hack-ish but should mostly work:
def file_is_closed?(file_name)
ObjectSpace.each_object(File) do |f|
if File.absolute_path(f) == File.absolute_path(file_name) && !f.closed?
return false
end
end
true
end
I don't stand by that handling corner cases well, but it seems to work for me in general:
f1 = File.new("test.file")
f2 = File.new("test.file")
file_is_closed?("test.file") # => false
f1.close
file_is_closed?("test.file") # => false
f2.close
file_is_closed?("test.file") # => true
If you want to know if any process has the file open, I think you'll need to resort to something external like lsof.
For those cases where you no longer have access to the original file objects in Ruby (after fork + exec, for instance), a list of open file descriptors is available in /proc/pid/fd. Each file there is named for the file descriptor number, and is a symlink to the opened file, pipe, or socket:
# Returns hash in form fd => filename
def open_file_descriptors
Hash[
Dir.glob( File.join( '/proc', Process.pid.to_s, 'fd', '*' ) ).
map { |fn| [File.basename(fn).to_i, File.readlink(fn)] rescue [nil, nil] }.
delete_if { |fd, fn| fd.nil? or fd < 3 }
]
end
# Return IO object for the named file, or nil if it's not open
def io_for_path(path)
fd, fn = open_file_descriptors.find {|k,v| path === v}
fd.nil? ? nil : IO.for_fd(fd)
end
# close an open file
file = io_for_path('/my/open/file')
file.close unless file.nil?
The open_file_descriptors method parses the fd directory and returns a hash like {3 => '/my/open/file'}. It is then a simple matter to get the file descriptor number for the desired file, and have Ruby produce an IO object for it with for_fd.
This assumes you are on Linux, of course.

How to get around "Can't convert nil into String (TypeError)" when reading a csv file in Ruby?

I am working on a program that will eventually compare two .csv files and print out any variances between the two. However, at the moment I can't get past a "can't convert nil into String (TypeError)" when reading one of the files.
Here is a sample line from the problematic csv file:
11/13/15,11:31:00,ABCD,4000150097,1321126281700ABCDEF,WR00002440,,,4001,1392,AI,INTERNAL RETURN,INBOUND,,ABCDEF
And here is my code so far:
require 'csv'
class CSVReportCompare
def initialize(filename_data, filename_compare)
puts "setting filename_data=", filename_data
puts "setting compare=", filename_compare
#filename_data = filename_data
#filenam_compare = filename_compare
end
def printData
#data = CSV.read(#filename_data)
puts #data.inspect
end
def printCompareData
#compareData = CSV.read(#filename_compare)
puts #compareData.inspect
end
def compareData
end
end
c1 = CSVReportCompare.new("data.csv", "compare_data.csv")
c1.printData
c1.printCompareData
Anyways, is there a way to get around the error?
You have a typo in your initialize method:
#filenam_compare = filename_compare
#-------^ missing "e"
So you're setting the wrong instance variable. Instance variables are created when they're first used and initialized to nil so later, when you try to access #filename_compare, the instance variable with the correct name is created and has a value of nil.

Resources