Why can't I use "mkdir" with a Pathname object? - ruby

I've recently been trying out the Pathname lib, and one thing I want to do is use mkdir to create some directories.
I looked up the documentation and it says it wraps up mkdir but everytime I try to use it I`m getting this error:
irb(main):006:0> p = Pathname.new('/tmp')
=> #<Pathname:/tmp>
irb(main):007:0> a = p.mkdir("123adam")
TypeError: can't convert String into Integer
from /usr/lib/ruby/1.8/pathname.rb:975:in `mkdir'
from /usr/lib/ruby/1.8/pathname.rb:975:in `mkdir'
from (irb):7
from /usr/lib/ruby/1.8/fileutils.rb:1589
Can anyone explain what I`m doing wrong here.

You need to specify the directory you want to create with Pathname and then call mkdir.
This should work:
p = Pathname.new('/tmp/123adam')
p.mkdir
The argument you can supply are the permissions for the new directory.

Out of interest, the reason why you get "can't convert String into Integer" is because Pathname.mkdir is actually a wrapper around Dir.mkdir as follows:
def mkdir(*args) Dir.mkdir(#path, *args) end
The path represented by the Pathname object is passed as the first parameter to Dir.mkdir, followed by any parameters passed to Pathname.mkdir. The second parameter for Dir.mkdir is the numeric access permissions that you would like the directory created to have. Hence in your example "123adam" is being passed to Dir.mkdir where a number is expected.

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')

How to create a shortcut of a required function (`cp = FileUtils.cp`)?

I just want to call cp instead of FileUtils.cp. This is turning to be surprisingly hard to do in Ruby!
In Javascript this would simply be: cp = FileUtils.cp. This doesn't work in Ruby because its paren-less calling doesn't allow assigning this way (Ruby thinks I'm calling FileUtils.cp but I'm trying to assign it, I get the error: wrong number of arguments (given 0, expected 1) (ArgumentError)).
I tried alias but alias cp FileUtils.cp doesn't work because of the dot (I get the error: syntax error, unexpected '.', expecting end-of-input). alias_method would be for creating an alias within FileUtils, like FileUtils.cp2, but that's not what I want to do.
cp = FileUtils.method :cp works BUT my new shortcut is a "second-class" function, whenever I use it I have to call it as cp.call instead of just cp, thus reducing the brevity of my shortcut and forcing me to remember this new way of calling some functions.
Is there a way to simply get cp be FileUtils.cp? Thanks!
You can create a function in global space
def cp(src, dest, options = {})
FileUtils.cp(src, dest, options = {})
end

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.

Show error message for every argument that is not defined in Ruby program

I'm creating a program in Ruby for replacing content of multiple files in one directory, and there are four arguments:
dir for defining directory
ext for defining extension
find for defining string to find
replace for defining string that replace string defined as find
Right now, this is start of the program:
dir, ext, find, replace = ARGV
dir, ext, find, replace = ARGV.shift
raise "Missing argument for directory" unless dir
raise "..." unless ext
#etc
However, when I run this program without defining arguments, it shows only first argument that is not defined (directory) with RuntimeError and imediatelly closes program. Is there any different approach for this?
I can offer you such variant:
missing_parameters = ["dir","ext","find","replace"].select{|name| eval(name).nil?}
raise "Missing parameters for #{missing_parameters.join(',')}" unless missing_parameters.empty?
But frankly speaking, i don't like the usage of eval :)

How to specify a file path using '~' in Ruby?

If I do:
require 'inifile'
# read an existing file
file = IniFile.load('~/.config')
data = file['profile'] # error here
puts data['region']
I get an error here:
t.rb:6:in `<main>': undefined method `[]' for nil:NilClass (NoMethodError)
It goes away if I specify an absolute path:
file = IniFile.load('/User/demo1/.config')
But I do not want to hardcode the location. How can I resolve ~ to a path in Ruby?
Ruby has a method for this case. It is File::expand_path.
Converts a pathname to an absolute pathname. Relative paths are referenced from the current working directory of the process unless dir_string is given, in which case it will be used as the starting point. The given pathname may start with a “~”, which expands to the process owner’s home directory (the environment variable HOME must be set correctly). “~user” expands to the named user’s home directory.
require 'inifile'
# read an existing file
file = IniFile.load(File.expand_path('~/.config'))
When given ~ in a path at the command line, the shell converts ~ to the user's home directory. Ruby doesn't do that.
You could replace ~ using something like:
'~/.config'.sub('~', ENV['HOME'])
=> "/Users/ttm/.config"
or just reference the file as:
File.join(ENV['HOME'], '.config')
=> "/Users/ttm/.config"
or:
File.realpath('.config', ENV['HOME'])
=> "/Users/ttm/.config"

Resources