I've just started learning ruby and I'm trying to edit the following script. The code as is downloads files while creating directories.
def download_files
puts "Downloading #{#base_url} to #{backup_path} from site..."
puts
file_list_by_time = get_file_list_by_time
if file_list_by_time.count == 0
puts "No files to download. Possible reasons:\n\t* Accept regex didn't let any files through (Accept Regex: \"#{#accept_regex.to_s}\")\n\t* Site is not in site."
return
end
count = 0
file_list_by_time.each do |file_remote_info|
count += 1
file_url = file_remote_info[:file_url]
file_id = file_remote_info[:file_id]
file_time = file_remote_info[:time]
file_path_elements = file_id.split('/')
if file_id == ""
dir_path = backup_path
file_path = backup_path + 'index.html'
elsif file_url[-1] == '/' or not file_path_elements[-1].include? '.'
dir_path = backup_path + file_path_elements[0..-1].join('/')
file_path = backup_path + file_path_elements[0..-1].join('/') + '/index.html'
else
dir_path = backup_path + file_path_elements[0..-2].join('/')
file_path = backup_path + file_path_elements[0..-1].join('/')
end
unless File.exists? file_path
open('myfile.txt', 'a') do |f|
f.puts ("http://example.com/#{file_time}id_/#{file_url}")
end
begin
structure_dir_path dir_path
open(file_path, "wb") do |file|
begin
rescue OpenURI::HTTPError => e
puts "#{file_url} # #{e}"
file.write(e.io.read)
rescue StandardError => e
puts "#{file_url} # #{e}"
end
end
rescue StandardError => e
puts "#{file_url} # #{e}"
end
puts "#{file_url} -> #{file_path} (#{count}/#{file_list_by_time.size})"
else
puts "#{file_url} # #{file_path} already exists. (#{count}/#{file_list_by_time.size})"
end
end
puts
puts "Download complete, saved in #{backup_path} (#{file_list_by_time.size} files)"
end
end
I want to change it to just save the full file URL to a text file i.e. save the URL without downloading the file.
I added the following to the code which works perfectly. I now just need to stop the script from downloading the files.
open('myfile.txt', 'a') do |f|
f.puts ("http://example.com/#{file_time}id_/#{file_url}")
end
i tried removing the part from structure_dir_path dir_path to the end but i keep getting an error message. Any ideas how can i implement that correctly?
Return Early from Method
To stop processing, just return from anywhere inside your method, including from within a block. For example:
open('myfile.txt', 'a') do |f|
url = "http://example.com/#{file_time}id_/#{file_url}"
f.puts url
return url
end
Related
I have been trying to display as "file name def statements..." But this code is not helping.
files = Dir.entries("C:\\Users\\itcuser\\Desktop\\Ruby Programs").select {|f| !File.directory? f}
files.each do |file_name|
if !File.directory? file_name
File.open(file_name) do |f|
**puts file_name**
f.any? do |line|
if line.include?("def")
print line
end
end
end
end
end
Well, you're not using the return value of your f.any? to print the filename. Should be more like this instead.
File.open(file_name) do |f|
puts file_name if f.any? { |line| line.include?("def") }
end
Try this:
files = Dir.entries("C:\\Users\\itcuser\\Desktop\\Ruby Programs")
files.each do |file_name|
# skip this, if file_name is a directory
next if File.directory? file_name
lines = File.readlines(filename)
puts file_name if lines.any? { |line| line.include?('def')
end
My code is:
def write_to_file(line, my_file)
File.open(my_file, 'a') do |file|
p '-----loop number:' + line.id.to_s
file.puts "#{line.id}"
end
end
If I loop three times with this method, I can see:
-----loop number:1
-----loop number:2
-----loop number:3
But it can only write the last id into my_file. Even I tried:
file << "#{line.id}"
# or
file.write "#{line.id}\n"
The result was the same.
try this
def write_to_file(line, my_file)
File.open(my_file, 'a') do |file|
p '-----loop number:' + line.to_s
file.puts "#{line}"
end
end
[1,2,3,4].each do |line|
write_to_file(line, my_file)
end
UPDATE: OK, so I implemented your code, but now the indentation is not showing up! Any ideas what might be wrong? I modified the code so that it would attempt to pass my original test (this is only an exercise so in real life I would not be overriding the XmlDocument class) and here is the modified code:
class XmlDocument
attr_reader :indent_depth, :bool
def initialize(bool = false, indent_depth = 0)
#indent_depth = indent_depth
#bool = bool
end
def method_missing(name, *args)
indentation = ' '*indent_depth
attrs = (args[0] || {}).map { |k, v| " #{k}='#{v}'" }.join(' ')
if block_given?
puts indent_depth
opening = "#{indentation}<#{name}#{attrs}>"
contents = yield(XmlDocument.new(true,indent_depth+1))
closing = "#{indentation}</#{name}>"
bool ? opening + "\n" + contents + "\n" + closing : opening + contents + closing
else
"#{indentation}<#{name}#{attrs}/>"
end
end
end
I'm trying to get the method to pass this test:
it "indents" do
#xml = XmlDocument.new(true)
#xml.hello do
#xml.goodbye do
#xml.come_back do
#xml.ok_fine(:be => "that_way")
end
end
end.should ==
"<hello>\n" +
" <goodbye>\n" +
" <come_back>\n" +
" <ok_fine be='that_way'/>\n" +
" </come_back>\n" +
" </goodbye>\n" +
"</hello>\n"
...but I'm unsure as to where to go with my code, below. I was thinking of using a counter to keep track of how far indented we have to go. I tried some code, but then deleted it because it was getting too messy and I have a feeling that the indentation should not be too complicated to implement.
class XmlDocument
def initialize(bool = false)
#bool = bool
end
def send(tag_name)
"<#{tag_name}/>"
end
def method_missing(meth, arg={}, &block)
arbitrary_method = meth.to_s
tag_string = ''
# 1) test for block
# 2) test for arguments
# 3) test for hash
if block_given? # check for #xml.hello do; #xml.goodbye; end
if yield.class == String # base case: #xml.hello do; "yellow"; end
"<#{arbitrary_method}>#{yield}</#{arbitrary_method}>"
else # in the block we do not have a string, we may have another method
method_missing(yield)
end
elsif arg.empty? # no arguments e.g. #xml.hello
send(arbitrary_method)
else # hash as argument e.g. #xml.hello(:name => 'dolly')
send("#{arbitrary_method} #{arg.keys[0]}='#{arg.values[0]}'")
end
end
end
Your code needs a lot of work - some pointers:
Do not override the send method!
Don't call yield over and over - you don't know what side effects you might cause, not to mention a performance hit - call it once, and remember the return value.
You might want to read up on how to write a DSL (here is a blogpost on the subject), to see how it was done correctly in other places.
Ignoring the above, I will try to answer your question regarding indentation.
In a DSL use case, you might want to use a context object which holds the indentation depth as state:
class Indented
attr_reader :indent_depth
def initialize(indent_depth = 0)
#indent_depth = indent_depth
end
def method_missing(name, *args)
indentation = ' ' * indent_depth
attrs = (args[0] || {}).map { |k, v| "#{k}='#{v}'" }.join(' ')
if block_given?
"#{indentation}<#{name} #{attrs}>\n" +
yield(Indented.new(indent_depth + 1)) +
"\n#{indentation}</#{name}>"
else
"#{indentation}<#{name} #{attrs}/>"
end
end
end
xml = Indented.new
puts xml.hello do |x|
x.goodbye do |x|
x.come_back do |x|
x.ok_fine(:be => "that_way")
end
end
end
# => <hello >
# => <goodbye >
# => <come_back >
# => <ok_fine be='that_way'/>
# => </come_back>
# => </goodbye>
# => </hello>
In the gets portion at the lower half of the program, the terminal is not asking me for input but automatically taking input. I am unable to understand how this is happenening.
The Code is:
puts "Welcome to my automatic file opener"
puts "Version - 2.0"
if ARGV[0] && ARGV[1] #to ensure Arguements are given as input
old_data = File.open(ARGV[0]).readlines
new_data = File.open(ARGV[1]).readlines
class Differentiator
def old_stuff
puts "the old files are:-"
puts old_data
end
def new_stuff
puts "The new files are:-"
puts new_data
end
def updated_list
puts "The newly added files are:-"
newly_added = new_data - old_data
puts newly_added
end
def deleted_list
puts "The deleted files are:-"
deleted_data = old_data - new_data
puts deleted_data
end
def stable_list
puts "The unchanged/stable files are:-"
unchanged_data = new_data - newly_added
puts unchanged_data
end
end#end of class
while true
puts "Choose your option:"
puts "1.Old Files of System"
puts "2.New Files of System"
puts "3.Added Files of System"
puts "4.Deleted Files of System"
puts "5.Stable Lists"
puts "6.Exit"
print " Please Choose your Output:-"
**option_method=gets.chomp.to_i**
filecase1 = Differentiator.new
if option_method == 1
filecase1.old_stuff
end
if option_method == 2
filecase1.new_stuff
end
if option_method == 3
filecase1.updated_list
end
if option_method == 4
filecase1.deleted_list
end
if option_method == 5
filecase1.stable_list
end
if option_method == 6
break
exit
end
if option_method != (1..6)
puts "Sorry,Wrong Input"
end
end
else
puts "The Right Method of Usage is : ruby <scriptname>.rb old_file new_file"; exit;
end
Because you have to use $stdin.gets otherwise it will read the files given in ARGV.
It is written in the man page http://www.ruby-doc.org/core-2.0/Kernel.html in the first line talking about gets
When I do $stdout.puts "foo", I want it to go to both stdout and to a file.
I came up with:
def log_init
$stdout.sync = true
old_out = $stdout.dup
old_out.sync = true
r, w = IO.pipe
$stdout.reopen(w)
fork do
f = File.open('/tmp/test', 'a') do |f|
f.sync = true
while (sel = IO.select([r], [], [], 1000))
readfds, *rest = sel
data = readfds.first.readpartial(1024)
old_out.write(data)
f.write(data)
end
end
end
end
Can you do this without requiring a second process?
Just do it from the shell:
your-program | tee file
I think this post/thread from the Ruby mailing list may help you along: http://www.ruby-forum.com/topic/102759#226506
In particular this piece of code:
["$stdout", "$stderr"].each do |std|
io = eval(std)
old_write = io.method(:write)
class << io
self
end.module_eval do
define_method(:write) do |text|
unless text =~ /^[\r\n]+$/ # Because puts calls twice.
File.open("logfile.log", "a") do |f|
f.puts [std[1..-1].upcase, caller[2], text].join(" ")
end
end
old_write.call(text)
end
end
end
$stdout.puts "text on stdout"
$stderr.puts "text on stderr"