147

I have this statement:

File.open(some_path, 'w+') { |f| f.write(builder.to_html)  }

Where

some_path = "somedir/some_subdir/some-file.html"

What I want to happen is, if there is no directory called somedir or some_subdir or both in the path, I want it to automagically create it.

How can I do that?

7 Answers 7

181

You can use FileUtils to recursively create parent directories, if they are not already present:

require 'fileutils'

dirname = File.dirname(some_path)
unless File.directory?(dirname)
  FileUtils.mkdir_p(dirname)
end

Edit: Here is a solution using the core libraries only (reimplementing the wheel, not recommended)

dirname = File.dirname(some_path)
tokens = dirname.split(/[\/\\]/) # don't forget the backslash for Windows! And to escape both "\" and "/"

1.upto(tokens.size) do |n|
  dir = tokens[0...n]
  Dir.mkdir(dir) unless Dir.exist?(dir)
end
Sign up to request clarification or add additional context in comments.

8 Comments

Oh ok. I meant the core, not the stdlib. Either way, that's fine. This works. Thanks!
I added a core-only solution to my answer: Be aware, however, that it essentially reimplements FileUtils.mkdir_p (which is the method dedicated to your use case)
Note that FileUtils#mkdir_p works even if the directory hierarchy already exists (it just does nothing) so this solution can be compressed into this can one-liner plus a require statement: FileUtils.mkdir_p(File.dirname(some_path))
@JosephK - for me this (misleading) EEXIST error ended up being a permission issue.
|
99

For those looking for a way to create a directory if it doesn't exist, here's the simple solution:

require 'fileutils'

FileUtils.mkdir_p 'dir_name'

Based on Eureka's comment.

1 Comment

This is @Eureka's comment - "Note that FileUtils#mkdir_p works even if the directory hierarchy already exists (it just does nothing) so this solution can be compressed into this can one-liner plus a require statement: FileUtils.mkdir_p(File.dirname(some_path))"
27
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)

1 Comment

You may run into race conditions using this method, the directory could get created after File.exists? runs but before Dir.mkdir is executed.
9

How about using Pathname?

require 'pathname'
some_path = Pathname("somedir/some_subdir/some-file.html")
some_path.dirname.mkpath
some_path.write(builder.to_html)

2 Comments

It works with some_path.dirname.mkpath instead of some_path.dirname.mkdir_p
+1 on mkpath. Also if you just have the directory and not the path, there's no need for dirname, e.g. Pathname("somedir/some_subdir").mkpath will work the same way.
7

Based on others answers, nothing happened (didn't work). There was no error, and no directory created.

Here's what I needed to do:

require 'fileutils'
response = FileUtils.mkdir_p('dir_name')

I needed to create a variable to catch the response that FileUtils.mkdir_p('dir_name') sends back... then everything worked like a charm!

2 Comments

doesn't make sense. why you need to catch the return?
@huanson , I didn't need to catch the return... but the logic didn't work until I created response = FileUtils.mkdir_p('dir_name'). If I didn't create this variable, FileUtils.mkdir_p('dir_name') wasn't working for me... or at least that's what I recall happened (this answer is more than 1 year old). I wouldn't be surprised if a newer version of Ruby fixes this issue.
1

Along similar lines (and depending on your structure), this is how we solved where to store screenshots:

In our env setup (env.rb)

screenshotfolder = "./screenshots/#{Time.new.strftime("%Y%m%d%H%M%S")}"
unless File.directory?(screenshotfolder)
  FileUtils.mkdir_p(screenshotfolder)
end
Before do
  @screenshotfolder = screenshotfolder
  ...
end

And in our hooks.rb

  screenshotName = "#{@screenshotfolder}/failed-#{scenario_object.title.gsub(/\s+/,"_")}-#{Time.new.strftime("%Y%m%d%H%M%S")}_screenshot.png";
  @browser.take_screenshot(screenshotName) if scenario.failed?

  embed(screenshotName, "image/png", "SCREENSHOT") if scenario.failed?

Comments

1

The top answer's "core library" only solution was incomplete. If you want to only use core libraries, use the following:

target_dir = ""

Dir.glob("/#{File.join("**", "path/to/parent_of_some_dir")}") do |folder|
  target_dir = "#{File.expand_path(folder)}/somedir/some_subdir/"
end

# Splits name into pieces
tokens = target_dir.split(/\//)

# Start at '/'
new_dir = '/'

# Iterate over array of directory names
1.upto(tokens.size - 1) do |n|

  # Builds directory path one folder at a time from top to bottom
  unless n == (tokens.size - 1)
    new_dir << "#{tokens[n].to_s}/" # All folders except innermost folder
  else
    new_dir << "#{tokens[n].to_s}" # Innermost folder
  end

  # Creates directory as long as it doesn't already exist
  Dir.mkdir(new_dir) unless Dir.exist?(new_dir)
end

I needed this solution because FileUtils' dependency gem rmagick prevented my Rails app from deploying on Amazon Web Services since rmagick depends on the package libmagickwand-dev (Ubuntu) / imagemagick (OSX) to work properly.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.