Rake & FTP

Rake is a build tool for ruby, similar to C’s make or Java’s ant/maven.

Basically you can define a lot of tasks, which can be executed, may have prerequisites and can do a lot boring tasks, from file operations to building gems.

One great thing is that you can easily deploy your whole project to your FTP server. Searching around I did not find a lot of information about that – this is the reason I’ll write about it.

Rake::FtpUploader
Rake has a built in class for handling simple FTP uploads, but this class is not well documented. You can use it this way:

1
2
3
4
5
6
7
8
9
require 'rake'
require 'rake/contrib/ftptools'
 
task :upload_all do
  Rake::FtpUploader.connect('/path/on/server', 'your_host', 'your_user', 'your_pw') do |ftp|
    ftp.verbose = true # gives you some output
    ftp.upload_files("./your/favorite/folder/**/*")
  end
end

The ftp.upload_files method takes a wildcard parameter, which is the same as you would feed into the Dir[wildcard] class. At the end, the FtpTools just use Dir to find all files. For the wildcard it is good to know, that a single * says “upload all files and folders”, but a **/* says “upload all files and folders in a recursive way, including subfolders and their files. Thus said, “./**/*” would upload everything from the base dir of your rakefile.

Extending Rake’s FtpTools for deletion
In my case I was using rake to upload files for a PHP project – written with the symfony framework. Symfony – as many other frameworks – has a cache which needs to be cleared if some settings for the view change. As the cache is simply a folder on the FTP server, I thought that rake could be used to easily clear the cache. Reminding myself of the fact that everything in Ruby is just an object, I just extended the FtpUploader class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
module Rake
  class FtpUploader
 
    # Deletes all files in a folder like cache/management. Does
    # not delete folders
    def delete_files(folder)
      delete_files_recursive(folder)
    end
 
    private
 
    # starts with a folder, like cache/management and deletes
    # all files recursively BUT not the folders.
    def delete_files_recursive(file_or_folder)
      folder = true
      begin
        @ftp.chdir(file_or_folder)
      rescue
        # this is a file, no folder => delete
        folder = false
      end
 
      if folder
        file_list = @ftp.nlst('*')
        file_list.each { |f| delete_files_recursive(f) }
        @ftp.chdir('..')
      else
        puts "Delete #{file_or_folder}" if @verbose
        delete file_or_folder
      end
 
    end
 
    # deletes a single file
    def delete(file)
      puts "Deleting #{file}" if @verbose
 
      begin
        @ftp.delete(file)
      rescue
        puts "Could not delete file #{file}"
      end
    end
  end
end

This snippet only deletes files, no folders. In my case this is wanted, as I’m not sure what the cache does if you destroy the folder structure, but nevertheless it could be easily extended. After all files of a folder are deleted, after the @ftp.chdir(‘..’) is executed, the folder could be deleted.
Usage

1
2
3
4
5
6
7
namespace :cache do
  task :clean
    Rake::FtpUploader.connect('/path/on/server', 'your_host', 'your_user', 'your_pw') do |ftp|
      ftp.delete_files('cache/subfolder')
    end
  end
end