Saturday, February 5, 2011

SOLID Ruby

Sandi Metz's presentation of Bob Martin's SOLID design principles for Ruby developers and her slides.

Sandi used DI to setup her opportunity to mock the downloader.
class PatentJOB 
  attr_reader :downloader

  def initialize(downloader = PatentDownloader.new)
    @downloader = downloader
  end

  def run 
    temp = downloader.download_file
  end
end
require 'tempfile'
class PatentDownloader
  def download_file 
    temp = Tempfile.new('patents')
    tempname = temp.path
    temp.close

    Net::FTP.open('localhost', 'foo', 'foopw') do |ftp|
      ftp.getbinaryfile('Public/prod/patents.csv', tempname)
    end
    tempname
  end 
end
And she provides this YAML and a class that uses that YAML to create
defaults: &defaults 
  ftp_host:      localhost
  ftp_login:     foo
  ftp_password   foopw
  ftp_filename:  patents.csv
  ftp_path:      Public/test

test: 
  <<: *defaults

development:
  <<: *defaults

production:
  ftp_path:     Public/prod
  <<: *defaults
class Config 
  attr_reader :data :env
  def self.config_path 
    file.join('config', 'external_resources')
  end

  def initialize(opts) 

  end 

  def define_methods_for_environment(names) 
    names.each do |name| 
      class_eval <<-EOS
        def #{name} 
          data[env]['#(name)']
        end
        EOS 
      end
    end
  end
And this allows us to refactor to more a loosely coupled design:
require 'tempfile'
class PatentDownloader 
  attr_reader :config
  
  def initialize(config=Config.new(:filename => 'patent.yaml'))
    @config = config
  end

  def download_file
    temp = Tempfile.new(config.ftp_filename)
    tempname = temp.path
    temp.close

    Net::FTP.open(config.ftp_host, config.ftp_login, config.ftp_password) do |ftp|
      ftp.getbinaryfile(config.ftp_path, tempname)
    end
    tempname
  end
end