DEV Community

Cover image for Organize Your Downloads Folder with Ruby #2
Mattias Velamsson
Mattias Velamsson

Posted on

Organize Your Downloads Folder with Ruby #2

So, after working with a side-project for about 6 hours today, pulling my hair because CSS makes absolutely no sense to me sometimes, I decided to improve on the Downloads Organizer I made in Ruby the other week.

This is part two / version two. You can view the first article here: Organize Your Downloads Folder with Ruby

So what I added was a way of checking the type of the files, and also organizing them into their own subfolders. To achieve this I used regex to the check the file-endings of the files.

After getting it to work, I had to refactor.. A LOT.. But here are the final results, a couple of functions doing bits and pieces I'll walk you through under the codeblock.

require 'find'
require 'fileutils'

downloads_folder = File.join(Dir.home, 'Downloads')
## Arrays used in the filtering logic.
yearly_folders = ["2019", "2020", "2021", "2022", "2023", "2024", "2025"]

def check_type(file)
  case file
  when /^.*\.(pdf|doc|docx|PAGES|txt)$/i
  when /^.*\.(gif|jpg|jpeg|png|svg|mp4|mov|mp3|mpeg4|heic)$/i
  when /^.*\.(dmg|exe|jar)$/i
  when /^.*\.(zip|tar|rar)$/i

def create_directory(directory, subfolder)
  final_directory = File.join(directory, subfolder)
  Dir.mkdir(final_directory) unless

def move_file(file, directory)
  final_directory = create_directory(directory, check_type(file)), final_directory) unless file === directory

def organize_downloads(downloads_folder, yearly_folders)
  Dir.foreach(downloads_folder) do |file|
    next if file == '.' || file == '..' || yearly_folders.include?(file)

    path = File.join(downloads_folder, file)
    next unless File.exist?(path)

    last_edited = File.mtime(path)
    year = last_edited.strftime('%Y')
    new_directory = create_directory(downloads_folder, year)

    move_file(path, new_directory)

organize_downloads(downloads_folder, yearly_folders)

Enter fullscreen mode Exit fullscreen mode

So, when the app is started, running the organize_downloads method, the same old logic used for getting the files, and year modified as explained in part 1.

When it starts to differ is when assigning new_directory to the value of create_directory.

1 create_directory

In this method, we give it two attributes: directory and year, which is set in organize_downloads. It then creates a new variable, that eventually just returns a path to the location we want to put the file in.

From there we create a new folder with the new path, UNLESS the folder already exist.

And finally, we return the final_folder path.

Now, back to organize_downloads and what happens in move_file

2 move_file

Here, we give it two attributes: path, which basically is the pointer to the file itself, and new_directory which is the return that we got from the create_directory in step 1.

In here, we again create a final_directory, but this time to also include subfolders.

This one is pretty easy, it re-uses the create_directory method again, but to create the subfolder. But instead of having the downloads_folder and year, we instead give it the updated directory(directory) and the return of whatever check_type does (explained in step 3).

And after getting these values, we run the same FileUtils.mvto just move it to its new location.

3 check_type

The last piece of the puzzle to explain, and the easiest. A switch condition that checks if the letters/digits after "." at the end of the filename matches a specific set of filetypes set by me.

How I decided to structure mine was:

  • Media (images/videos)
  • Programs (apps, installers)
  • Documents (PDFs, docs)
  • Archives (rar-files etc)
  • Other (The rest)

I'm sure I've probably missed filetypes etc, but I just wanted to get it to work really.

It then returns a string for whatever match it gets, and then goes on with the logic back in move_file.

And that's it! Version 2.0 of my Ruby app organizing the downloads folder. Good times.

In the future I would like to look at:

  • Add more structure
  • Potentially make it even more dynamic and take inputs
  • GUI? 👀

Top comments (0)