TDDing the real thing.
We did a little TDDing on a spike to learn about FTP, and that’s the main area of ignorance that I’m aware of. (Of course, it’s the “unknown unknowns” that really get you. We’ll find out about those soon enough.) Therefore …
Today Tozier and I will do a Quick Design Session and start TDDing something that will, I hope, turn into the actual app. At this moment, I am not at all sure just where we’ll start or what it will turn into. My intention, however, is to try to build the “whole real thing”, not some component of it, like the folder mover or the file pusher or other bits.
T’s not here yet so I’m thinking on my own. Risky, I know, but sometimes there’s just no choice. Right now I’ve got these cards:
Trigger
JekyllRunner
FolderMover
(Jekyll)
FilePusher
T shows up and we discuss these cards. We realize that we really want to synchronize the iPad folder with the “real” rj.com source folder. That is, we want to move things only if they’re newer. This would allow me to create an article on the iPad, publish it, then edit later at home. If we don’t consider the iPad folder dates, I’d have to remember to delete that folder, or the article would be rolled back to the iPad version. So we rename:
FileSynchronizer
T reminds me that we have no handling for Git and we almost certainly want to commit and push. If we don’t, bad things will happen. So …
GitCommitAndPush
We create a couple of folders for our tests to use. We already don’t like their names. We mess around a bit and wind up with this test:
def test_elementary_flow
# sync the files
# run jekyll
# if it worked
# ftp the files
# commit and push
# is everything in ipad_articles also in mac_articles?
# is everything in mac_articles also in _site/articles?
# is everything in _site/articles also on ronjeffries.com?
# ipad = runner.getIpadArticles
init_folders
runner = JekyllRunner.new
runner.sync
Dir.chdir("_ipad_articles")
from_directory = Dir.glob("**/*").to_set
Dir.chdir("../_mac_articles")
to_directory = Dir.glob("**/*").to_set
diff = from_directory - to_directory
assert(from_directory.to_set.subset?(to_directory.to_set), "missing: #{diff.inspect}")
end
def init_folders
FileUtils.rm_rf("./_mac_articles")
FileUtils.mkdir("./_mac_articles")
FileUtils.mkdir_p("./_mac_articles/articles/article-3")
FileUtils.touch("./_mac_articles/articles/article-3/index.md")
end
The comments are our notes about what we want the thing to do. You’ll notice the line runner.sync
. We’ve decided to expose some internal methods of our nascent JekyllRunner, so that we can see if the files are correctly copied down into our target folder.
We’ve decided that the top-level source folder (_ipad_articles
) will contain the full tree that is to be copied into the jekyll source, that is, it’ll start with /articles. (N.B. This is surely the wrong idea.) Anyway, the Dir.glob
stuff lets use check whether the target folder contains all the desired files. As the test is written, it will be only the desired files, because we empty it in the test. Since that’s in the test, we are “confident” that the code will not destroy existing articles. For values of “confident”.
We built the following nearly trivial class:
class JekyllRunner
def sync
FileUtils.cp_r("_ipad_articles/.","_mac_articles")
end
end
This actually works. A question one might ask is whether, since Ruby’s cp_r “just does” what we want, should we have wasted all this time on a test. That’s up to you, but I’m glad now that we did and I expect to be even more glad later, when we screw something up.
Tozier notices that we didn’t have use to_set
on the globs to produce our error message. So we’ll change that and run again. (We do need it in the assert but we hadn’t factored those out anyway.)
We also decide to add a “pre-existing” folder and article in the target folder, and to make sure they are preserved. The resulting code, likely good for today, is:
require "minitest/autorun"
require "net/ftp"
require "./passwords"
require "set"
class Test_JekyllRunner < Minitest::Test
def init_folders
FileUtils.rm_rf("./_mac_articles")
FileUtils.mkdir("./_mac_articles")
FileUtils.mkdir_p("./_mac_articles/articles/article-3")
FileUtils.touch("./_mac_articles/articles/article-3/index.md")
end
def test_elementary_flow
# sync the files
# run jekyll
# if it worked
# ftp the files
# commit and push
# is everything in ipad_articles also in mac_articles?
# is everything in mac_articles also in _site/articles?
# is everything in _site/articles also on ronjeffries.com?
# ipad = runner.getIpadArticles
init_folders
runner = JekyllRunner.new
runner.sync
Dir.chdir("_ipad_articles")
from_directory = Dir.glob("**/*")
Dir.chdir("../_mac_articles")
to_directory = Dir.glob("**/*")
diff = from_directory - to_directory
assert(from_directory.to_set.subset?(to_directory.to_set), "missing: #{diff.inspect}")
assert(File.exists?("articles/article-3/index.md"), "did not preserve old contents")
end
end
class JekyllRunner
def sync
FileUtils.cp_r("_ipad_articles/.","_mac_articles")
end
end
We have some issues with what’s here. First, the folder names are compiled into both our tests and our tiny JekyllRunner
. Second, the folder setup itself isn’t quite what we intend. We’re close but need to sort it out in our heads. In the future, we’ll presumably parameterize JekyllRunner.new
to accept the source and target folder names.
We believe that we’re handling arbitrary depth but would like to confirm that. We need to clarify our mental model of the current blog structure and how the ipad folder relates to it.
All this is for another day. Thanks for tuning in.