Trouble Creating Directories with mkdir - ruby

New to Ruby, probably something silly
Trying to make a directory in order to store files in it. Here's my code to do so
def generateParsedEmailFile
apath = File.expand_path($textFile)
filepath = Pathname.new(apath + '/' + #subject + ' ' + #date)
if filepath.exist?
filepath = Pathname.new(filepath+ '.1')
end
directory = Dir.mkdir (filepath)
Dir.chdir directory
emailText = File.new("emailtext.txt", "w+")
emailText.write(self.generateText)
emailText.close
for attachment in #attachments
self.generateAttachment(attachment,directory)
end
end
Here's the error that I get
My-Name-MacBook-2:emails myname$ ruby etext.rb email4.txt
etext.rb:196:in `mkdir': Not a directory - /Users/anthonydreessen/Developer/Ruby/emails/email4.txt/Re: Make it Brief Report Wed 8 May 2013 (Errno::ENOTDIR)
from etext.rb:196:in `generateParsedEmailFile'
from etext.rb:235:in `<main>'

I was able to recreate the error - it looks like email4.txt is a regular file, not a directory, so you can't use it as part of your directory path.

If you switched to mkdir_p and get the same error, perhaps one of the parents named in '/Users/anthonydreessen/Developer/Ruby/emails/email4.txt/Re: Make it Brief Report Wed 8 May 2013' already exists as a regular file and can't be treated like a directory. Probably that last one named email.txt

You've got the right idea, but should be more specific about the files you're opening. Changing the current working directory is really messy as it changes it across the entire process and could screw up other parts of your application.
require 'fileutils'
def generate_parsed_email_file(text_file)
path = File.expand_path("#{#subject} #{date}", text_file)
while (File.exist?(path))
path.sub!(/(\.\d+)?$/) do |m|
".#{m[1].to_i + 1}"
end
end
directory = File.dirname(path)
unless (File.exist?(directory))
FileUtils.mkdir_p(directory)
end
File.open(path, "w+") do |email|
emailText.write(self.generateText)
end
#attachments.each do |attachment|
self.generateAttachment(attachment, directory)
end
end
I've taken the liberty of making this example significantly more Ruby-like:
Using mixed-case names in methods is highly irregular, and global variables are frowned on.
It's extremely rare to see for used, each is much more flexible.
The File.open method yields to a block if the file could be opened, and closes automatically when the block is done.
The ".1" part has been extended to keep looping until it finds an un-used name.
FileUtils is employed to makes sure the complete path is created.
The global variable has been converted to an argument.

Related

Opening a Text File in Ruby

I am trying to create a program that will count the word frequency within a text file that I have created. I have a text file titled moms_letter.txt and this is my code:
word_count = {}
File.open("moms_letter.txt", "r") do |f|
f.each_line do |line|
words = line.split(' ').each do |word|
word_count[word] += 1 if word_count.has_key? word
word_count[word] = 1 if not word_count.has_key? word
end
end
end
puts word_count
The problem I am getting is when I go to run the file, I get the error:
there is no such file or directory - moms_letter.txt (Errno: : ENOENT)
Not quite sure why this is occurring when I have the text file created.
Any help is appreciated.
I am also newbie in Ruby, so thanks for the patience.
You must be executing your program from outside the directory where your moms_letter.txt file resides. You need to use an absolute path to open your file. Or, execute your program always from the directory where the .txt is. So, instead of using "moms_letter.txt" go with "complete/path/to/file/moms_letter.txt".
I'm fairly new to Ruby too, but have worked with text files a bit recently. It may seem like an obvious question, but is the text file you're trying to open in the same directory as your .rb file? Otherwise you'll need to include the relative path to it.
For troubleshooting sake, try File.new("temp.txt", "w") and then File.open("temp.txt", "r") to see if that works. Then you'll know if it's an issue with your code or with the txt file you're trying to access.
Also using File.exists?("moms_letter.txt") will help you determine whether you can access that file from within your .rb script.
Hope that helps!

What is the best practice to read a file (under lib) while developing a gem?

I experienced some pain when deal with the "path" in developing a gem.
Here is the folder structure
production codes:
lib/gem_name/foo/templates/some_template.erb
lib/gem_name/foo/bar.rb
test codes:
test/gem_name/foo/bar_test.rb
In bar.rb, I read the template by:
File.read("templates/some_template.erb") => Errno::ENOENT: No such file or directory
when I run the unit test in bar_test.rb in RubyMine, it gives me the error:
Errno::ENOENT: No such file or directory - D:/.../test/gem_name/foo/templates/some_template.erb
Obviously the test in the path is wrong.
My question are,
How to deal with this issues?
What is the best practice to handle
such path problem while developing a gem?
Edit:
Since __FILE__ only returns the path of the file it is written, currently I define fname (see #ckruse's answer) like functions in every file I need it. It works but it is not elegant. Perhaps someone will have a better solution than mine on this. If so, please let me know.:)
You can always refer to the directory of the current file by File.dirname(__FILE__) and then use relative pathes, e.g.:
fname = File.dirname(__FILE__) + "/templates/some_template.rb"
File.read(fname)
Edit: To shortcut this just write a method:
def fname(file)
File.dirname(__FILE__) + "/../til/../project/../root/../" + file
end
Edit 3: You also could use caller to always refer to the directory of the calling file:
def fname(file)
path, _ = caller.first.split(':', 2)
File.dirname(path) + "/" + file
end

Test all subclasses on file update

I am learning unit testing with PHP and am following the TDD session on tutsplus: http://net.tutsplus.com/sessions/test-driven-php/
I have set up a ruby watchr script to run the PHPUnit unit tests every time a file is modified using Susan Buck's script: https://gist.github.com/susanBuck/4335092
I would like to change the ruby script so that in addition to testing a file when it is updated it will test all files that inherit from it. I name my files to indicate inheritance (and to group files) as Parent.php, Parent.Child.php, and Parent.Child.GrandChild.php, etc so the watchr script could just search by name. I just have no idea how to do that.
I would like to change:
watch("Classes/(.*).php") do |match|
run_test %{Tests/#{match[1]}_test.php}
end
to something like:
watch("Classes/(.*).php") do |match|
files = get all classes that inherit from {match[1]} /\b{match[1]}\.(.*)\.php/i
files.each do |file|
run_test %{Tests/{file}_test.php}
end
end
How do I do the search for file names in the directory? Or, is there an easier/better way to accomplish this?
Thanks
EDIT
This is what I ended up with:
watch("#{Library}/(.*/)?(.*).php") do |match|
file_moded(match[1], match[2])
end
def file_moded(path, file)
subclasses = Dir["#{Library}/#{path}#{file}*.php"]
p subclasses
subclasses.each do |file|
test_file = Tests + file.tap{|s| s.slice!(".php")}.tap{|s| s.slice!("#{Library}")} + TestFileEnd
run_test test_file
end
end
Where Library, Tests, and TestFileEnd are values defined at the top of the file. It was also changed so that it will detect changes in subfolders to the application library and load the appropriate test file.
I'm not entirely certain, but i think this will work:
watch("Classes/(.*).php") do |match|
subclasses = Dir["Classes/#{match[1]}*.php"]
filenames = subclasses.map do |file|
file.match(/Classes\/(.*)\.php/)[1]
end
filenames.each do |file|
run_test "Tests/#{file}_test.php"
end
end
It's probably not the cleaneast way, but it should work.
The first line saves all the relative paths to files in the Classes directory beginning with the changed filename in subclasses.
in the map block I use a regex to only get the filename, without any folder names or the .php extensions.
Hope this helps you

Error reading local file in Sinatra

I'm trying to write a Sinatra app that reads in a list from a file, and then spits back a random item from that list.
I'm having trouble figuring out the path to the file to read it, though. Sinatra says 'no such file or directory' when I try to load an item in my browser:
Errno::ENOENT at /wod
No such file or directory - http://localhost:4567/listing.txt
Here is the code:
require 'sinatra'
#list
get /item
puts read_list[rand(#list.size)]
end
def read_list
File.open('listing.txt', 'r').readlines
end
I have the file in /public, which the Sinatra README says is the default location for hosting static files. Furthermore, if I put it in /public I can navigate to localhost:4567/listing.txt and read the file in the browser.
A couple things I noticed:
get /item
isn't correct, it should be:
get '/item' do
If you start your code inside the same directory the Ruby code is in, the current working-directory will be ".", which is where Ruby will look when trying to:
File.open('listing.txt', 'r').readlines
Ruby will actually use './listing.txt' as the path. That's OK if you manually launch the code from the root directory of the application, but that doesn't work well if you try to launch it from anywhere else.
It's better to be explicit about the location of the file when you're actually trying to load something for use with a web server. Instead of relying on chance, there are a couple things you can do to help make it more bullet-proof. Consider this:
def read_list
running_dir = File.dirname(__FILE__)
running_dir = Dir.pwd if (running_dir == '.')
File.open(running_dir + '/public/listing.txt', 'r').readlines
end
File.dirname gets the path information from __FILE__, which is the absolute path and name of the current file running. If the application was started from the same directory as the file, that will be ., which isn't what we want. In that case, we want the absolute path of the current working-directory, which Dir.pwd returns. Then we can append that to the path of the file you want, from the root of the application.
You'll need to do File.read('public/listing.txt', 'r') to get what you want here.
File.open isn't part of Sinatra and doesn't know to look in a specific place for static files, so it just looks in the current working directory.

Good Way to Handle Many Different Files?

I'm building a specialized pipeline, and basically, every step in the pipeline involves taking one file as input and creating a different file as output. Not all files are in the same directory, all output files are of a different format, and because I'm using several different programs, different actions have to be taken to appease the different programs.
This has led to some complicated file management in my code, and the more I try to organize the file directories, the more ugly it's getting. Just about every class involves some sort of code like the following:
#fileName = File.basename(file)
#dataPath = "#{$path}/../data/"
MzmlToOther.new("mgf", "#{#dataPath}/spectra/#{#fileName}.mzML", 1, false).convert
system("wine readw.exe --mzXML #{#file}.raw #{$path}../data/spectra/#{File.basename(#file + ".raw", ".raw")}.mzXML 2>/dev/null")
fileName = "#{$path}../data/" + parts[0] + parts[1][6..parts[1].length-1].chomp(".pep.xml")
Is there some sort of design pattern, or ruby gem, or something to clean this up? I like writing clean code, so this is really starting to bother me.
You could use a Makefile.
Make is essential a DSL designed for handling converting one type of file to another type via running an external program. As an added bonus, it will handle only performing the steps necessary to incrementally update your output if some set of source files change.
If you really want to use Ruby, try a rakefile. Rake will do this, and it's still Ruby.
You can make this as sophisticated as you want but this basic script will match a file suffix to a method which you can then call with the file path.
# a conversion method can be used for each file type if you want to
# make the code more readable or if you need to rearrange filenames.
def htm_convert file
"HTML #{file}"
end
# file suffix as key, lambda as value, the last uses an external method
routines = {
:log => lambda {|file| puts "LOG #{file}"},
:rb => lambda {|file| puts "RUBY #{file}"},
:haml => lambda {|file| puts "HAML #{file}"},
:htm => lambda {|file| puts htm_convert(file) }
}
# this loops recursively through the directory and sub folders
Dir['**/*.*'].each do |f|
suffix = f.split(".")[-1]
if routine = routines[suffix.to_sym]
routine.call(f)
else
puts "UNPROCESSED -- #{f}"
end
end

Resources