I'm writing a simple program that takes an input string, splits it in words and saves it in it's memory. There's three methods -- to save a string into memory, to load from file and to load from zip archive. Here's the code:
require 'zip'
class Storage
def initialize
#storage = ''
end
def add(string)
words = string.split ','
words.each do |word|
#storage << "#{word},"
end
end
def load_from_file(filename)
File.open filename, 'r' do |f|
f.each { |line| add line }
end
end
def load_from_zip(filename)
Zip::File.open "#{filename}.zip" do |zipfile|
zipfile.each { |entry| load_from_file entry.to_s }
end
end
end
While add and load_from_file methods work perfectly as I expect, load_from_zip returns me the following error each time I try to run it:
storage.rb:39:in `initialize': No such file or directory # rb_sysopen - test.txt (Errno::ENOENT)
Although the file exists in my archive. I would appreciate any suggestion on what I'm doing wrong
Zip::File.open "#{filename}.zip"
does not extract the zip file, it just opens it and shows what's inside.
You cannot call
File.open filename, 'r'
since filename isn't in your filesystem, just inside the .zip file.
You'll need to add a new method :
require 'zip'
class Storage
def initialize
#storage = ''
end
def add(string)
words = string.split ','
words.each do |word|
#storage << "#{word},"
end
end
def load_from_file(filename)
File.open filename, 'r' do |f|
f.each { |line| add line }
end
end
def load_from_zip(filename)
Zip::File.open "#{filename}.zip" do |zipfile|
zipfile.each { |entry| load_from_zipped_file(zipfile,entry)}
end
end
private
def load_from_zipped_file(zipfile, entry)
zipfile.read(entry).lines.each do |line|
add line
end
end
end
s = Storage.new
s.load_from_zip('test')
Related
class ReadFile
attr_accessor :filepath, :condigos
def initialize
#filepath = filepath
#condigos = false
end
def reader(filepath)
while condigos == false
File.foreach(filepath) do |line|
puts line
sleep(5)
end
end
end
end
require_relative 'reader_class'
reader = ReadFile.new
reader.reader("text.txt")
def file_to_array(filename)
my_text = []
File.open(filename, "r").each do |line|
my_text << line
end
my_text
end
Hello! My method is supposed to receive a filename when called, open the file and move each line to an array. However, it raises undefined local variable when called and I'm assuming it has something to do with the filename[DOT]ext ?!
My call happens in a method below
def clean_array
cleaned = file_to_array(text.txt).map do |element|
element.gsub(/\b['s\!\.\\n]*/m, "")
end
cleaned.map! do |el|
el.split(" ")
end
return cleaned.flatten!
#p cleaned
end
The file name should be a string, and as such, passed in quotes:
file_to_array("text.txt")
# OR
file_to_array('text.txt')
I want to download a binary file from http or https URL like:
URI('https://www.google.com/favicon.ico').download('google.ico')
I wrote the method for it like this:
module URI
def download(file)
File.open(file, 'wb') {|f| f.write(open(self).read)}
end
end
This method ends up with an error ArgumentError: extra arguments, while following code is working.
file = 'google.ico'
url = 'https://www.google.com/favicon.ico')
File.open(file, 'wb') {|f| f.write(open(url).read)}
What am I doing wrong? How should I fix it?
URI module doesn't download file. File class doesn't either. open comes from open-uri stdlib which is why it works in your 2nd example. If you have curl in your system this should work:
module URI
def self.download(file_url)
filename = file_url.split('/').last
`curl -O #{file_url}`
end
end
If you DON'T have curl use open-uri
require 'open-uri'
module URI
def self.download(file_url)
filename = file_url.split('/').last
File.open(filename, "wb") do |saved_file|
open(file_url, "rb") do |read_file|
saved_file.write(read_file.read)
end
end
end
end
And call it like this
URI.download('https://www.google.com/favicon.ico')
Note, URI behaves like more like a class so you need to define the method on the base object self otherwise you'll need to create an instance, but since it's just a module, use def self.some_method(some_arg) to be able to call URL.some_method(some_arg)
While this works, it is not recommended for production. Why do you wanna monkey patch URI when you can simply write your own module which does this?
You're better off doing something like this:
module SimpleFileDownload
require 'open-uri'
def self.download(file_url)
filename = file_url.split('/').last
File.open(filename, "wb") do |saved_file|
open(file_url, "rb") do |read_file|
saved_file.write(read_file.read)
end
end
end
end
and call it like:
SimpleFileDownload.download('https://www.google.com/favicon.ico')
I thought I'm calling Kernel.open from open-uri, but inside the module URI OpenURI::OpenRead is called.
At first I added binding.pry
module URI
def download(file)
binding.pry
File.open(file, 'wb') {|f| f.write(open(self).read)}
end
end
And checked which method is called like this:
pry(#<URI::HTTPS>)> show-method open
From: /Users/ironsand/.rbenv/versions/2.4.3/lib/ruby/2.4.0/open-uri.rb # line 720:
Owner: OpenURI::OpenRead
Visibility: public
Number of lines: 3
def open(*rest, &block)
OpenURI.open_uri(self, *rest, &block)
end
pry(#<URI::HTTPS>)> exit
pry(main)> show-method open
From: /Users/ironsand/.rbenv/versions/2.4.3/lib/ruby/2.4.0/open-uri.rb # line 29:
Owner: Kernel
Visibility: private
Number of lines: 11
def open(name, *rest, &block) # :doc:
if name.respond_to?(:open)
name.open(*rest, &block)
elsif name.respond_to?(:to_str) &&
%r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ name &&
(uri = URI.parse(name)).respond_to?(:open)
uri.open(*rest, &block)
else
open_uri_original_open(name, *rest, &block)
end
end
To use proper method I should have called the method explicitly.
module URI
def download(file)
File.open(file, 'wb') {|f| f.write(OpenURI.open_uri(self).read)}
end
end
The code above works as I expected.
I'm creating a program to create folder structure from the text file.
In that I need to get the spaces that comes before any word in the given line.
How can I implement this functionality in ruby without opening String class?
I come from C# and it's very easy to extract methods in it. But I don't know how rubyists approach the same problem!
Here is the half of the program that I have written.
require "FileUtils"
#lines_array = []
def file_to_array
file = File.open("fs.txt", "r") do |file|
file.each_line do |line|
#lines_array << line.rstrip
#lines_array.reject! {|l| l.empty?}
end
end
end
def creation
#lines_array.each do |i|
if ( /(.+)\.(\w+)/ =~ i )
FileUtils.touch i
else
FileUtils.mkdir_p i
end
end
end
def count_space
beginning = s.length - s.lstrip.length
beginning
end
How can I extract logic from file_to_array method? And how can I implement and use count_space on elements of #lines_array?
here is the solution:
s = ' aaa ' #3 spaces in start and after `aa` there are two spaces
s[/\A */].size # for blank spaces in start of string
=> 3
s[/ *\z/].size # for blank spaces a the end of string
=> 2
So if I understand you correctly, you want to be able to call the #count_spaces method on your lines. Well there are three ways to approach this.
Open up the String class (what you don't want to do)
Create a class that inherits from Sting
Create a class that contains the string as an instance variable (this is probably your prefered method)
Open the class
class String
def count_spaces
#code here
end
end
Inherited class
class FileSystemLine < String
def count_spaces
#code here
end
end
Class with data in instance variable
class FileSystemLine
def initialize(line)
#line = line
end
def count_spaces
#code here
# make sure that code acts on #line
# like #line.length - #line.strip.length
end
# If you want to keep string functionalities, you could look up ruby delegation or use this simple method_missing method
def method_missing(method, *args, &block)
if #line.respond_to?(method)
#line.__send__(method, *args, &block)
else
super
end
end
end
Just remember to initialize the class.
Option 1. remains the same, option 2. and option 3. both look like this: line = FilSystemLine.new(line)
So your code would look like:
#lines_array = []
def file_to_array
file = File.open("fs.txt", "r") do |file|
file.each_line do |line|
#lines_array << FileSystemLine.new(line.rstrip)
#lines_array.reject! {|l| l.empty?}
end
end
end
This is a class method. So far this is what I have:
def self.save(array, my_file)
File.open(my_file) do |f|
f.lines.each do |line|
text_line = line
text_line.write
end
end
end
I'd do as below:
def self.save(array, my_file)
File.open(my_file,'w') do |f|
f.puts array
end
end
IO::puts, if called with an array argument, writes each element on a new line. You don't need to close the file manually, as you called IO::open. As per the doc of IO::open, if the optional code block is given, it will be passed io as an argument, and the IO object will automatically be closed when the block terminates.
Use File#puts which is inherited from IO#puts
def self.save(array, my_file)
File.open(my_file, 'w+') do |f|
array.each do |line|
f.puts line
end
end
end