Maybe this time ...
Yes, well, this got published accidentally, a bit too soon. More cryptic than usual. But no one complained, that’s odd. Are you out there?
When Tozier arrives, he raises the question: if a file and/or folder already exists do we want to overwrite it? The answer is that whatever is in our source folder is assumed to be the version we want. We don’t remove anything but we will overwrite even a newer copy with what’s in the Dropbox source folder. (I can imagine changing this story later. This is the story for now.)
This question makes us want to test whether FTP put overwrites, or fails when the file exists. I predict it will overwrite but that’s not how we do this. We test:
def test_upload_files
ftp = ftp_connected_to_target
oldDir = Dir.pwd
Dir.chdir("/Users/ron/Dropbox/_source")
source = Dir.glob('**/*').sort
source.each do |file_name|
if File::directory? file_name
ftp_mkdir_safely(ftp, file_name)
else
ftp.putbinaryfile(file_name,file_name)
ftp.putbinaryfile(file_name,file_name)
end
end
ftp.close
Dir.chdir(oldDir)
Dir.chdir("_target")
target = Dir.glob('**/*').sort
assert_equal(source, target)
# puts target
Dir.chdir(oldDir)
end
We begin by duplicating the putbinaryfile
call. This will fail if attempts to overwrite fail. We are assuming – and you know what happens when you assume – that if the write doesn’t fail, it will overwrite.
It doesn’t fail, so our answer looks like overwriting is what we get. Since it’s what we want, we’re nearly happy.
But we’re trying to build Confidence1 here, so we write another test, this time putting new contents over the first one:
def test_overwrite_works
ftp = ftp_connected_to_target
oldDir = Dir.pwd
Dir.chdir("/Users/ron/Dropbox/_source")
ftp.putbinaryfile("a.txt", "overwritten.txt")
ftp.putbinaryfile("b.txt", "overwritten.txt")
expected = File.read('b.txt')
ftp.close
Dir.chdir(oldDir)
stuff = File.read('_target/overwritten.txt')
assert_equal(expected, stuff)
end
Cute, huh? And not really harder to write. Now we’re really sure that FTP:putbinaryfile does overwrite without complaint, and that’s what we want.
Let’s now see about end-to-end.We have a bunch of notes about how it’s supposed to work and we sketch our test:
# copy _source to working_folder/articles
# run jekyll (in working_folder)
# ftp only the copied and jekylled files from working_folder/_site/articles to remote site
# with <any>.md -> <any>.html
# check_contents:
# the YAML worked
# the jpegs are still viewable
# categories are done and uploaded
def test_end_to_end
site = ftp_pointing_somewhere_safe
jr = JekyllRunner.new(site)
jr.run
read_site_and_check_contents
end
Naturally this isn’t valid code yet but we certainly have a failing test, so we’re allowed to do work.
We think the next step is to install jekyll in the test folder. I have a major concern: not screwing up the existing jekyll installation. We just do a jekyll new and figure we’ll work out any folder issues. We now have a naked Jekyll installation under test-ftp folder. We run Jekyll build
and it works. (I consider this to be a miracle and expect that with one more I’ll be canonized.)
We have our source folder and it has subfolders the way we want them, and files in the folders. This was good enough for testing our code to move from the source into the site, but we’ll need to have some markdown files to check Jekyll. The issue here is that the site source will have .md
files but we need to move the corresponding .html
files from the Jekyll _site
folder. So for a valid test we need some markdown. We decide to put this off, since we’re not running Jekyll yet anyway.
We press on to improve our new class so that it moves the files:
class JekyllRunner
def initialize(ftp_to_site)
@ftp = ftp_to_site
end
def run
move_ipad_files
run_jekyll
ftp_the_results_to_site
end
end
This, of course, is what I was taught to call “Programming By Intention”. You just write calls to the things you want to have happen and then implement the things.
After some milling about, we write a test to drive our non-existent implementation:
def test_file_copy
FileUtils.rm_rf("./test_jekyll_site/articles")
ipad_folder = '/Users/ron/Dropbox/_source/.' # dot is key
jekyll_folder = './test_jekyll_site'
ftp = nil
jr = JekyllRunner.new(ipad_folder, jekyll_folder, ftp)
jr.move_ipad_files # sorry, Demeter
assert(File.exist?('./test_jekyll_site/articles/a.txt'), "can't find a.txt")
end
Strictly speaking, a user of JekyllRunner should never need or use access to the move_ipad_files
function: it’s internal. So we apologize to Demeter for not following her Excellent Suggestion. This drives out some implementation, which you can see below. That test worked quickly so we also put in a quick one that tests running Jekyll.
require "minitest/autorun"
require "net/ftp"
require "./passwords"
require "set"
class Test_JekyllRunner < Minitest::Test
def setup
@folder = Dir.pwd
end
def teardown
Dir.chdir(@folder)
end
# copy _source to working_folder/articles
# run jekyll (in working_folder)
# ftp only the copied and jekylled files from working_folder/_site/articles to remote site
# with <any>.md -> <any>.html
# check_contents:
# the YAML worked
# the jpegs are still viewable
# categories are done and uploaded
def test_file_copy
FileUtils.rm_rf("./test_jekyll_site/articles")
ipad_folder = '/Users/ron/Dropbox/_source/.' # dot is key
jekyll_folder = './test_jekyll_site'
ftp = nil
jr = JekyllRunner.new(ipad_folder, jekyll_folder, ftp)
jr.move_ipad_files # sorry, Demeter
assert(File.exist?('./test_jekyll_site/articles/a.txt'), "can't find a.txt")
end
def test_jekyll_run
FileUtils.rm_rf("./test_jekyll_site/articles")
ipad_folder = '/Users/ron/Dropbox/_source/.' # dot is key
jekyll_folder = './test_jekyll_site'
ftp = nil
jr = JekyllRunner.new(ipad_folder, jekyll_folder, ftp)
jr.move_ipad_files # sorry, Demeter
jr.run_jekyll # oops, Demeter, my bad
assert(File.exist?('./test_jekyll_site/_site/articles/a.txt'), "can't find jekyllated a.txt")
end
def test_setup_teardown_restores_chdir
result = (`pwd`).chomp
assert_equal(@folder, result)
end
def test_chdir_affects_where_backtick_runs
Dir.chdir('/Users/ron')
result = (`pwd`).chomp
assert_equal('/Users/ron', result)
end
end
class JekyllRunner
def initialize(ipad_folder, jekyll_folder, ftp_to_site)
@ipad = ipad_folder
@jekyll_folder = jekyll_folder
@ftp = ftp_to_site
end
def move_ipad_files
FileUtils.cp_r(@ipad,"#{@jekyll_folder}/articles")
end
def run_jekyll
pwd = Dir.pwd # TODO fix this to be more reasonable
Dir.chdir(@jekyll_folder)
`jekyll build`
Dir.chdir(pwd)
end
def run
move_ipad_files
run_jekyll
# ftp_the_results_to_site
end
end
The test for Jekyll running is trivial. We just run it and then look to see if there are any files in the _site
folder.
There was an Eror™ even in this trivial test and implementation, of course. We didn’t remember to destroy the _site
folder before running the test, which meant that we were not really sure whether the run had worked this time, or just some time in the past.
At this point we called it quits for the day. See you Thursday.
-
Confidence is a term I’m trying out for use in the New Framework series. ↩