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.