Passing in file extension Variable - ruby

I am trying to pass in the file extension as a variable but it doesn't seem to be working. when I run getlist.rb(txt)
def getlist(extension)
file = File.new("the_list.txt", "w")
Dir['../path/*.'+ extension].each { |f| file.puts File.basename(f, '.'+ extension).upcase }
end
Basically I want to drop in any file extension (txt, pdf, rb, etc) and it will give me the list without the extension names. Script works fine when it is hard coded just doesn't work when I try to drop in a variable.
Best Regards,
AZCards

My example:
def getlist(extension)
File.open("the_list.txt", "w"){|file|
Dir["*.#{extension}"].each { |f|
file << File.basename(f, File.extname(f)).upcase
file << "\n"
}
}
end
getlist('rb')
getlist(:rb)
My main modifications:
File.open isnted new (you forgot file.close). With File.open and a block the close is done automatic.
Replaces String+ with #-replacements. Advantage: you can use symbols when you call it (you wrote getlist.rb(txt) - this only works, when txt is a variable, but I think you want 'txt')
Use File#extension., see Daves answer.
I added newlines to the resulting file.
Update
Just in case you need a solution with multiple extensions and to call it from command line:
def getlist(*extensions)
p "*.{#{extensions.join(',')}}"
File.open("the_list.txt", "w"){|file|
Dir["*.{#{extensions.join(',')}}"].each { |f|
file << File.basename(f, File.extname(f)).upcase
file << "\n"
}
}
end
#getlist('rb', 'txt')
#getlist(:rb, :txt)
getlist(*ARGV)

Dir['../path/*.'+ extension].each { |f| file.puts File.basename(f, File.extname(f)).upcase }
That said, it works fine for me when using your original string concatenation, too:
ruby-1.9.2-p0 :035 > getlist("html")
=> ["./about.html", "./epl-v10.html", "./notice.html"]
ruby-1.9.2-p0 :037 > getlist("*")
=> ["./libcairo-swt.so", "./eclipse.ini", "./icon.xpm", "./eclipse.ini~", "./about.html", "./epl-v10.html", "./artifacts.xml", "./notice.html"]
ruby-1.9.2-p0 :038 > getlist("ini")
=> ["./eclipse.ini"]
(Prints w/o extension removed for space purposes, but they print w/o the extension, in uppercase.)

Related

How to append a text to file succinctly

Instead of writing
File.open("foo.txt", "w"){|f| f.write("foo")}
We can write it
File.write("foo.txt", "foo")
Is there simpler way to write this one?
File.open("foo.txt", "a"){|f| f.write("foo")}
This has been answered in great depth already:
can you create / write / append a string to a file in a single line in Ruby
File.write('some-file.txt', 'here is some text', File.size('some-file.txt'), mode: 'a')
f = File.open('foo.txt', 'a')
f.write('foo')
f.close
Yes. It's poorly documented, but you can use:
File.write('foo.txt', 'some text', mode: 'a+')
You can use << instead of .write:
File.open("foo.txt", "a") { |f| f << "foo" }
Although this was answered with multiple options, I have always felt that if Ruby has File.write path, content, it should also have File.append path, content, especially since the existing append syntax is not very pleasant.
So, whenever I need to append, I usually add this extension:
class File
class << self
def append(path, content)
File.open(path, "a") { |f| f << content }
end
end
end
# Now it feels intuitive:
File.write "note.txt", "hello\n"
File.append "note.txt", "world\n"

Debugging in rails Console

I have this script that i would like to test within the rails console
Gem.find_files("models/*.rb").each do |f|
filename = File.basename(f, '.*')
class_name_symbol = filename.classify.to_sym
autoload class_name_symbol, "models/#{filename}"
end
what i would like to do is print out the results in the console but can only get as far as outputting the array using
Gem.find_files("models/*.rb")
which returns this
["/home/richardlewis/.rvm/gems/ruby-1.9.3-p448#myblogView/bundler/gems/blogModels-8360dfc861ad/lib/models/portfolio_sector.rb", "/home/richardlewis/.rvm/gems/ruby-1.9.3-p448#myblogView/bundler/gems/blogModels-8360dfc861ad/lib/models/post.rb", "/home/richardlewis/.rvm/gems/ruby-1.9.3-p448#myblogView/bundler/gems/blogModels-8360dfc861ad/lib/models/image.rb", "/home/richardlewis/.rvm/gems/ruby-1.9.3-p448#myblogView/bundler/gems/blogModels-8360dfc861ad/lib/models/message.rb", "/home/richardlewis/.rvm/gems/ruby-1.9.3-p448#myblogView/bundler/gems/blogModels-8360dfc861ad/lib/models/sector.rb", "/home/richardlewis/.rvm/gems/ruby-1.9.3-p448#myblogView/bundler/gems/blogModels-8360dfc861ad/lib/models/portfolio.rb", "/home/richardlewis/.rvm/gems/ruby-1.9.3-p448#myblogView/bundler/gems/blogModels-8360dfc861ad/lib/models/category.rb"]
tips appreciated
After using answer the output is
models/portfolio_sector
models/post
models/message
models/sector
models/portfolio
models/category
=> ["/home/richardlewis/.rvm/gems/ruby-1.9.3-p448#myblogView/bundler/gems/blogModels-8360dfc861ad/lib/models/portfolio_sector.rb", "/home/richardlewis/.rvm/gems/ruby-1.9.3-p448#myblogView/bundler/gems/blogModels-8360dfc861ad/lib/models/post.rb", "/home/richardlewis/.rvm/gems/ruby-1.9.3-p448#myblogView/bundler/gems/blogModels-8360dfc861ad/lib/models/image.rb", "/home/richardlewis/.rvm/gems/ruby-1.9.3-p448#myblogView/bundler/gems/blogModels-8360dfc861ad/lib/models/message.rb", "/home/richardlewis/.rvm/gems/ruby-1.9.3-p448#myblogView/bundler/gems/blogModels-8360dfc861ad/lib/models/sector.rb", "/home/richardlewis/.rvm/gems/ruby-1.9.3-p448#myblogView/bundler/gems/blogModels-8360dfc861ad/lib/models/portfolio.rb", "/home/richardlewis/.rvm/gems/ruby-1.9.3-p448#myblogView/bundler/gems/blogModels-8360dfc861ad/lib/models/category.rb"]
not sure why the array is at the end?
Edit :
The script is supposed to take each item in the array and autoload the file contained in models/#{filename}. I would like to print out all the model/#{filename} paths in the console to ensure they are correct –
Gem.find_files("models/*.rb").each do |f|
filename = File.basename(f, '.*')
# So, instead of passing this filename to autoload, you print it. Or do both.
puts "models/#{filename}"
# class_name_symbol = filename.classify.to_sym
# autoload class_name_symbol, "models/#{filename}"
end && nil # suppress return value from `each`

How can I copy the contents of one file to another using Ruby's file methods?

I want to copy the contents of one file to another using Ruby's file methods.
How can I do it using a simple Ruby program using file methods?
There is a very handy method for this - the IO#copy_stream method - see the output of ri copy_stream
Example usage:
File.open('src.txt') do |f|
f.puts 'Some text'
end
IO.copy_stream('src.txt', 'dest.txt')
For those that are interested, here's a variation of the IO#copy_stream, File#open + block answer(s) (written against ruby 2.2.x, 3 years too late).
copy = Tempfile.new
File.open(file, 'rb') do |input_stream|
File.open(copy, 'wb') do |output_stream|
IO.copy_stream(input_stream, output_stream)
end
end
As a precaution I would recommend using buffer unless you can guarantee whole file always fits into memory:
File.open("source", "rb") do |input|
File.open("target", "wb") do |output|
while buff = input.read(4096)
output.write(buff)
end
end
end
Here my implementation
class File
def self.copy(source, target)
File.open(source, 'rb') do |infile|
File.open(target, 'wb') do |outfile2|
while buffer = infile.read(4096)
outfile2 << buffer
end
end
end
end
end
Usage:
File.copy sourcepath, targetpath
Here is a simple way of doing that using ruby file operation methods :
source_file, destination_file = ARGV
script = $0
input = File.open(source_file)
data_to_copy = input.read() # gather the data using read() method
puts "The source file is #{data_to_copy.length} bytes long"
output = File.open(destination_file, 'w')
output.write(data_to_copy) # write up the data using write() method
puts "File has been copied"
output.close()
input.close()
You can also use File.exists? to check if the file exists or not. This would return a boolean true if it does!!
Here's a fast and concise way to do it.
# Open first file, read it, store it, then close it
input = File.open(ARGV[0]) {|f| f.read() }
# Open second file, write to it, then close it
output = File.open(ARGV[1], 'w') {|f| f.write(input) }
An example for running this would be.
$ ruby this_script.rb from_file.txt to_file.txt
This runs this_script.rb and takes in two arguments through the command-line. The first one in our case is from_file.txt (text being copied from) and the second argument second_file.txt (text being copied to).
You can also use File.binread and File.binwrite if you wish to hold onto the file contents for a bit. (Other answers use an instant copy_stream instead.)
If the contents are other than plain text files, such as images, using basic File.read and File.write won't work.
temp_image = Tempfile.new('image.jpg')
actual_img = IO.binread('image.jpg')
IO.binwrite(temp_image, actual_img)
Source: binread,
binwrite.

trying to find the 1st instance of a string in a CSV using fastercsv

I'm trying to open a CSV file, look up a string, and then return the 2nd column of the csv file, but only the the first instance of it. I've gotten as far as the following, but unfortunately, it returns every instance. I'm a bit flummoxed.
Can the gods of Ruby help? Thanks much in advance.
M
for the purpose of this example, let's say names.csv is a file with the following:
foo, happy
foo, sad
bar, tired
foo, hungry
foo, bad
#!/usr/local/bin/ruby -w
require 'rubygems'
require 'fastercsv'
require 'pp'
FasterCSV.open('newfile.csv', 'w') do |output|
FasterCSV.foreach('names.csv') do |lookup|
index_PL = lookup.index('foo')
if index_PL
output << lookup[2]
end
end
end
ok, so, if I want to return all instances of foo, but in a csv, then how does that work?
so what I'd like as an outcome is happy, sad, hungry, bad. I thought it would be:
FasterCSV.open('newfile.csv', 'w') do |output|
FasterCSV.foreach('names.csv') do |lookup|
index_PL = lookup.index('foo')
if index_PL
build_str << "," << lookup[2]
end
output << build_str
end
end
but it does not seem to work
Replace foreach with open (to get an Enumerable) and find:
FasterCSV.open('newfile.csv', 'w') do |output|
output << FasterCSV.open('names.csv').find { |r| r.index('foo') }[2]
end
The index call will return nil if it doesn't find anything; that means that the find will give you the first row that has 'foo' and you can pull out the column at index 2 from the result.
If you're not certain that names.csv will have what you're looking for then a bit of error checking would be advisable:
FasterCSV.open('newfile.csv', 'w') do |output|
foos_row = FasterCSV.open('names.csv').find { |r| r.index('foo') }
if(foos_row)
output << foos_row[2]
else
# complain or something
end
end
Or, if you want to silently ignore the lack of 'foo' and use an empty string instead, you could do something like this:
FasterCSV.open('newfile.csv', 'w') do |output|
output << (FasterCSV.open('names.csv').find { |r| r.index('foo') } || ['','',''])[2]
end
I'd probably go with the "complain if it isn't found" version though.

How do I get all the files names in one folder using Ruby?

These are in a folder:
This_is_a_very_good_movie-y08iPnx_ktA.mp4
myMovie2-lKESbDzUwUg.mp4
his_is_another_movie-lKESbDzUwUg.mp4
How do I fetch the first part of the string mymovie1 from the file by giving the last part, y08iPnx_ktA? Something like:
get_first_part("y08iPnx_kTA") #=> "This_is_a_very_good_movie"
Break the problem into into parts. The method get_first_part should go something like:
Use Dir to get a listing of files.
Iterate over each file and;
Extract the "name" ('This_is_a_very_good_movie') and the "tag" ('y08iPnx_ktA'). The same regex should be used for each file.
If the "tag" matches what is being looked for, return "name".
Happy coding.
Play around in the REPL and have fun :-)
def get_first_part(path, suffix)
Dir.entries(path).find do |fname|
File.basename(fname, File.extname(fname)).end_with?(suffix)
end.split(suffix).first
end
Kind of expands on the answer from #Steve Wilhelm -- except doesn't use glob (there's no need for it when we're only working with filenames), avoids Regexp and uses File.exname(fname) to the File.basename call so you don't have to include the file extension. Also returns the string "This_is_a_very_good_movie" instead of an array of files.
This will of course raise if no file could be found.. in which case if you just want to return nil if a match couldn't be found:
def get_first_part(path, suffix)
file = Dir.entries(path).find do |fname|
File.basename(fname, File.extname(fname)).end_with?(suffix)
end
file.split(suffix).first if file
end
Can it be done cleaner than this? REVISED based on #Tin Man's suggestion
def get_first_part(path, suffix)
Dir.glob(path + "*" + suffix + "*").map { |x| File.basename(x).gsub(Regexp.new("#{suffix}\.*$"),'') }
end
puts get_first_part("/path/to/files/", "-y08iPnx_kTA")
If the filenames only have a single hyphen:
path = '/Users/greg/Desktop/test'
target = 'rb'
def get_files(path, target)
Dir.chdir(path) do
return Dir["*#{ target }*"].map{ |f| f.split('-').first }
end
end
puts get_files(path, 'y08iPnx_ktA')
# >> This_is_a_very_good_movie
If there are multiple hyphens:
def get_files(path, target)
Dir.chdir(path) do
return Dir["*#{ target }*"].map{ |f| f.split(target).first.chop }
end
end
puts get_files(path, 'y08iPnx_ktA')
# >> This_is_a_very_good_movie
If the code is assumed to be running from inside the directory containing the files, then Dir.chdir can be removed, simplifying things to either:
puts Dir["*#{ target }*"].map{ |f| f.split('-').first }
# >> This_is_a_very_good_movie
or
puts Dir["*#{ target }*"].map{ |f| f.split(target).first.chop }
# >> This_is_a_very_good_movie

Resources