IO.readlines method in ruby behaving wierdly? - ruby

It sounds stupid but it has given me 3 hr of head banging...!!
I have created a class method in which I am extracting a file base name (placed in the Root folder). The issue is IO.readlines method is not accepting the file with a base name returned from the fetching. It returns error:
./lib/fileCheck.rb:36:in `readlines': No such file or directory - (Errno::ENOENT)
But it works as soon as I manually enter the file base name in readlines. Here is the class method:
class FileCheck
def self.read_file
file = File.basename(Dir[File.join(File.expand_path('../.'), "*.txt")].to_s)
file = IO.readlines(file)
return file
end
end
No result, but as soon as I place the file name manually, it works perfectly.
def self.read_file
#file = File.basename(Dir[File.join(File.expand_path('../.'), "*.txt")].to_s)
file = IO.readlines('sample.txt')
return file
end
I check with irb and the statement
File.basename(Dir[File.join(File.expand_path('../.'), "*.txt")].to_s)
is returning a file base name of class String.
Any suggestions?????

it does not matter if you use expand_path or join. you still have a mayor issue in your code:
File.basename(Dir[File.join(File.expand_path('../.'), "*.txt")].to_s)
the Dir[] also known as Dir.glob returns an array! make sure to pick one of the elements instead of calling to_s. you will run into problems when there is more than one file.

Related

Dir.chdir(File.dirname(__FILE__)) throws Errno::ENOENT

I got a method where I use Dir.chdir(File.dirname(__FILE__)) inside. I am using it so that I can run the ruby file from anywhere and not get this error: No such file or directory # rb_sysopen - 'filename' (Errno::ENOENT).
Using the method for the first time works fine but using it for the second time throws an error. See method and exact error below.
def meth(string)
Dir.chdir(File.dirname(__FILE__))
hash = JSON.parse(File.read("file.json"))
# do something with hash and string
# return some value
end
meth("first string") # this returns what is expected
meth("second string") # this second usage of the method throws the error
Error sample pinpointing the line where I used Dir.chdir(File.dirname(__FILE__)):
dir/decoder.rb:44:in `chdir': No such file or directory # dir_s_chdir - lib (Errno::ENOENT)
Not sure if OS plays a role here, I am using an m1 BigSur on version 11.2.3.
Why is this happening?
What needs to be done so that the method` can be used as much as needed without running into the error?
Your problem here seems to be that __FILE__ is a relative path like dir/decoder.rb and that path becomes invalid after the first time Dir.chdir is used, because that command changes the working directory of your entire Ruby process. I think the solution would be to do something like this in your decoder.rb file:
DecoderDir = File.realpath(File.dirname(__FILE__))
def meth
Dir.chdir(DecoderDir)
# ...
end
I'm guessing that the first time the Ruby interpreter processes the file, that is early enough that the relative path in __FILE__ still refers to the right place. So, at that time, we generate an absolute path for future use.
By the way, a well-behaved library should not run Dir.chdir because it will affect all use of relative paths throughout your entire Ruby process. I pretty much only run Dir.chdir once and I run it near the beginning of my top-level script. If you're making a reusable library, you might want to do something like this to calculate the absolute path of the file you want to open:
DecoderJson = File.join(DecoderDir, 'file.json')

`__FILE__` not working within `DATA`/`__END__`

I'm working on Practicing Ruby's Self-Guided Course on Stream, File Formats, and Sockets, and came across the following problem in the pre-built test for the first exercise. The following test script is supposed to change the directory to the data subdirectory of the project folder:
eval(DATA.read) # load the test helper script
... # various calls to test method defined below
__END__
dir = File.dirname(__FILE__)
Dir.chdir("#{dir}/data")
...
But this breaks because __FILE__ returns (eval) (instead of the path to the file) and File.dirname(__FILE__) returns . Why is this happening, and how should it be written to yield the intended output instead?
__END__ and DATA aren't really relevant here. You're simply passing a string to Kernel#eval. For example, a simple eval('__FILE__') also returns "(eval)" because that's the default filename. It can be changed by passing another string but as third argument:
eval('__FILE__', nil, 'hello.rb') # => "hello.rb"
Or in your case:
eval(DATA.read, nil, __FILE__)

Ruby mkdir returns 0

I have a Ruby script which sets up a directory which I need some other methods to use to store files. So, I need to be able to pass the directory as a string to some other methods:
To create the directory
results_dir = Dir.mkdir("/results/#{Time.now.strftime('%m-%d-%Y_%H:%M:%S')}")
The problem is that results_dir returns 0, not the string I would expect for the directory that was made: "/results/01-18-2016_14:58:38"
So, when I try to pass this to another method (i.e. my_method(var1, var2, results_dir), it's reading it as:
0/the_file_i_create
How can I pass the actual directory path to my methods?
It's not clear why you expect Dir.mkdir to return the directory name, since the docs explicitly say that Dir.mkdir returns 0:
mkdir( string [, integer] ) → 0
If you need the name of the directory you're creating, put it in a variable before you call Dir.mkdir:
results_dir = "/results/#{Time.now.strftime('%m-%d-%Y_%H:%M:%S')}"
Dir.mkdir(results_dir)
puts results_dir # => /results/01-18-2016_14:58:38
P.S. Avoid using colons (:) in file and directory names. It can cause issues on some systems, including OS X and Windows.

How to provide config file for Ruby command line utility written in Ruby?

I have a command line utility written in Ruby using GLI framework. I would like to have configuration for my command line utility in my home directory, using Ruby itself as DSL to handle it (similar to Gemfile or Rakefile).
I have in class ConfigData in folder lib/myapp. The class looks like following way:
class ConfigData
##data = {}
class ConfigItem
def initialize
#data = {}
end
def missing_method(name, *args)
#data[name] = args[0]
end
end
def self.add(section)
item = ConfigItem.new()
yield item
##data[section]=item
end
end
Now, what I would like to have, is the config file, preferrably with name Myappfile, in current working folder, with the following content
add('section1') do |i|
i.param1 'Some data'
i.param2 'More data'
end
When this code was included between class and end of ConfigData, it worked fine. But now I would like to have it placed in the working folder, where I start the application.
I tried require('./Myappfile') between class and end of ConfigData, but it doesn't work for me. I tried to read the source codes of rake, but it is not very much clear to me.
Any hint how this can be solved?
To evaluate code within the context of an instance, which is what you want to do, you need the instance_eval() method. Never, ever, use normal eval. Ever. Anyway, here's how you'd load your fingi file and get the data:
config = ConfigData.new
config.instance_eval(File.read("Myconfig"))
#Access configuration data here from the config object
That simple. After you've loaded the object in that way, you can access values of the object.
WARNING: This is not very secure. This is actually a gaping security hole. Here's the secure version:
f = Fiber.new {str = File.read("Myconfig"); $SAFE = 4; config = ConfigData.new; config.instance_eval(str); Fiber.yield config}
confdata = f.resume
#Access configuration data here from confdata.
This executes the external code in a (sort of) sandbox, so that it can't do anything dastardly.
Also, why don't you just use a YAML config? Unless configuration needs to run code like pwd or access RUBY_VERSION, YAML is much simpler and more secure, in addition to being more failproof.

How do I find the path of a template file using ERB?

I am using embedded ruby (ERB) to generating text files. I need to know the directory of the template file in order to locate another file relative to the template file path. Is there a simple method from within ERB that will give me the file name and directory of the current template file?
I'm looking for something similar to __FILE__, but giving the template file instead of (erb).
When you use the ERB api from Ruby, you provide a string to ERB.new, so there isn’t really any way for ERB to know where that file came from. You can however tell the object which file it came from using the filename attribute:
t = ERB.new(File.read('my_template.erb')
t.filename = 'my_template.erb'
Now you can use __FILE__ in my_template.erb and it will refer to the name of the file. (This is what the erb executable does, which is why __FILE__ works in ERB files that you run from the command line).
To make this a bit a bit more useful, you could monkey patch ERB with a new method to read from a file and set the filename:
require 'erb'
class ERB
# these args are the args for ERB.new, which we pass through
# after reading the file into a string
def self.from_file(file, safe_level=nil, trim_mode=nil, eoutvar='_erbout')
t = new(File.read(file), safe_level, trim_mode, eoutvar)
t.filename = file
t
end
end
You can now use this method to read ERB files, and __FILE__ should work in them, and refer to the actual file and not just (erb):
t = ERB.from_file 'my_template.erb'
puts t.result

Resources