First Article from Dropbox Transfer
Thank you for pressing the self-destruct button. –Spaceballs
I’m placing this article in Dropbox/_articles_from_ipad/017-08ff/spaceballs/
. With luck, er I mean due to our incredible skill, when we run our new MVP iPad transfer program, the article will appear on my web site, with all due indexes updated. When Tozier gets here, our mission will be to make that happen, with as little carnage as possible.
The first question in my mind, even before T gets here, is whether _articles_from_ipad
is the right folder, or perhaps _articles
, which is also present in Dropbox. Lesson to learn: name folders with more meaningful names. A check of the code may help me determine this. I find this test:
def set_up_for_really_jekylling
# root here is /Users/ron/programming/test-ftp
new_files_folder = '/Users/ron/Dropbox/_articles_from_ipad'
jekyll_folder = '/Users/ron/programming/rj-com'
ftp_host = 'localhost' # ftp.ronjeffries.com ?
ftp_target_folder = '/Users/ron/programming/test-ftp/_scary_target' # httpdocs
password_prefix = 'TEST_' # PROD_
return JekyllRunner.new(new_files_folder, jekyll_folder,
ftp_host, ftp_target_folder, password_prefix)
end
This tells me that the intention we had, the last time we met, was to use the _articles_from_ipad
folder as our source folder. Super.
This also gives me a frisson of fear, when I see the ftp_host
and ftp_target_folder
setting, because we have still not pointed this thing at the real site and let it do anything. Let’s explore that fear, hoping that it will suggest a test, and some code, to provide the necessary confidence to turn this thing loose.
Fear, still the mindkiller …
Clearly a rogue FTP writing my site could do very bad things. I could create a full backup, but there are a vast number of files. I do have the ability to regenerate the whole site in Jekyll, and if I were to do that and push it up with my normal FTP tool, Transmit, it should restore OK. That’s not a thing I want to do lightly, and certainly not one I want to do from the coffee shop. However, it does mean that we probably can’t do permanent harm.
In addition, my ISP allegedly does backups and could therefore allegedly restore the site. The reliability of that process has not been tested and while I like these people, I don’t trust the backup. I trust my own build more, since it is set up to enable me to move the whole site anywhere with nothing but FTP.
The only other thing I can think of that could go wrong would be if the transfer were to put files where they don’t belong. That should be mostly harmless. If they don’t overwrite anything that should be there, there will be extra files but no one will link to them. If they do overwrite something, restoring the site would put the right stuff back.
So, in principle, I should feel pretty good about this. And yet, I have fear. Fear is nature’s way of telling us to look the hell out, and it is sometimes appropriate and sometimes not. No one is likely to die from this thing going wrong, but I could be inconvenienced, which would be bad, and embarrassed, which would be worse, since I’d have to tell the world about it.
T arrives. We chat. We agree that the biggest question before us is the fact that my web site has data in the httpdocs
folder under its root, so we have to get the folder settings just right.
Tozier asks whether we could mock up a set of files and a manifest that will move just one easily identified file to the right place (or a discernibly wrong place). We look at the code:
require "net/ftp"
require "./passwords"
class JekyllRunner
...
def ftp_the_files
move_the_categories
move_feed_xml
move_index_html
move_the_articles
end
...
def move_feed_xml
move_batch("#{@jekyll_folder}/_site/", '.', ['feed.xml'])
end
...
def run
move_ipad_files
run_jekyll
ftp_the_files
end
end
If we set up a JekyllRunner with the parameters pointing to the generated site on my Mac, and to the real FTP location, and then call move_feed_xml
, we can check whether that file goes to the right place. If it does, we should feel much more confident. Let’s do that and see what happens.
We’ll copy the old file somewhere, create one we can recognize, run the app, and then check with Transmit to see what’s up there. Or maybe have our test re-download it (Tozier suggests). That’s better. Will we do it? Will we do it right? We don’t know yet, wait and see.
# def test_in_live
# jr = set_up_for_really_jekylling
# jr.run
# end
def test_move_feed_to_site
jr = set_up_for_really_jekylling
end
Building from a sketched test from last time, we write a test that doesn’t test anything yet. We update the set up method:
def set_up_for_really_jekylling
# root here is /Users/ron/programming/test-ftp
new_files_folder = '/Users/ron/Dropbox/_articles_from_ipad'
jekyll_folder = '/Users/ron/programming/rj-com'
ftp_host = 'ftp.ronjeffries.com'
ftp_target_folder = 'httpdocs'
password_prefix = 'PROD_'
return JekyllRunner.new(new_files_folder, jekyll_folder,
ftp_host, ftp_target_folder, password_prefix)
end
My biggest concern is the ftp_target_folder
. Should it be httpdocs
, or perhaps /httpdocs
. I guess we’ll find out. Tozier wants to run now. Basically he wants to test the initialize method, I guess. Never refuse to run the tests, so here goes:
They run and Tozier feels better. I guess I do, too, but I’m not going to admit it. Now we’ll back up our old feed.xml
and enhance the test.
Tozier suggests that we make a backup of the feed, and write a test that moves a new fake feed, checks to see that it’s there, then puts the old one back. I demur. My concern is that if the move doesn’t work, the fix won’t work either. So I propose to let it do the move, then check manually first to see if it’s there. Then, we’ll talk about extending the test. So …
Air:rj-com ron$ cd _site
Air:_site ron$ cp feed.xml feed2.xml
Air:_site ron$ echo "hi there" >feed.xml
Air:_site ron$ cat feed.xml
hi there
Air:_site ron$
def test_move_feed_to_site
jr = set_up_for_really_jekylling
jr.move_feed_xml
end
Are we ready to set this loose? We decide, after a few other random things, to use Transmit to watch the site. We observe the feed.xml in its old state, and it is good. Now we run the tests. They hang. We instrument:
def move_batch(local_site_folder, ftp_target_folder, folders_and_files )
perform_in_folder(local_site_folder) do
puts "connecting"
ftp_connection = ftp_connected_to_target
puts "connected"
ftp_connection.chdir(ftp_target_folder)
puts "on folder"
folders_and_files.each do |file|
puts "moving a file"
ftp_one_file(ftp_connection, file)
end
puts "closing"
ftp_connection.close
puts "closed"
end
end
We hang moving a file. (I kind of wish I’d said “done moving a file”.) With some added printing, we run again and it hangs. We chat, giving it time enough to time out, with this result from our tests:
Run options: --seed 54425
# Running:
.connecting
connected to .
on folder
moving a file: feed.xml
E
...
Finished in 72.522028s, 0.1241 runs/s, 0.1379 assertions/s.
1) Error:
Test_JekyllRunner#test_move_feed_to_site:
Net::ReadTimeout: Net::ReadTimeout
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/protocol.rb:158:in `rescue in rbuf_fill'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/protocol.rb:152:in `rbuf_fill'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/protocol.rb:134:in `readuntil'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/ftp.rb:1108:in `gets'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/ftp.rb:1113:in `readline'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/ftp.rb:290:in `getline'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/ftp.rb:301:in `getmultiline'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/ftp.rb:319:in `getresp'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/ftp.rb:352:in `block in sendcmd'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/monitor.rb:211:in `mon_synchronize'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/ftp.rb:350:in `sendcmd'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/ftp.rb:430:in `transfercmd'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/ftp.rb:547:in `block (2 levels) in storbinary'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/ftp.rb:199:in `with_binary'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/ftp.rb:546:in `block in storbinary'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/monitor.rb:211:in `mon_synchronize'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/ftp.rb:545:in `storbinary'
/Users/ron/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/net/ftp.rb:694:in `putbinaryfile'
/Users/ron/programming/test-ftp/jekyllrunner.rb:104:in `ftp_one_file'
/Users/ron/programming/test-ftp/jekyllrunner.rb:73:in `block (2 levels) in move_batch'
/Users/ron/programming/test-ftp/jekyllrunner.rb:71:in `each'
/Users/ron/programming/test-ftp/jekyllrunner.rb:71:in `block in move_batch'
/Users/ron/programming/test-ftp/jekyllrunner.rb:44:in `call'
/Users/ron/programming/test-ftp/jekyllrunner.rb:44:in `perform_in_folder'
/Users/ron/programming/test-ftp/jekyllrunner.rb:65:in `move_batch'
/Users/ron/programming/test-ftp/jekyllrunner.rb:93:in `move_feed_xml'
/Users/ron/programming/test-ftp/test-jekyll-runner.rb:61:in `test_move_feed_to_site'
As we read this stack we see that the FTP is trying to
We clean up the local test so the setup format matches our newer one:
def set_up_for_jekyll_testing
FileUtils.rm_rf("./_target")
FileUtils.mkdir("./_target")
FileUtils.mkdir("./_target/articles")
FileUtils.mkdir("./_target/categories")
FileUtils.rm_rf("./test_jekyll_site/articles")
ipad_folder = '/Users/ron/Dropbox/_source'
jekyll_folder = './test_jekyll_site'
ftp_host = 'localhost'
ftp_target_folder = 'programming/test-ftp/_target/'
password_prefix = 'TEST_'
return JekyllRunner.new(ipad_folder, jekyll_folder,
ftp_host, ftp_target_folder, password_prefix)
end
This lets us better inspect the two, and Tozier notices that the target folder ends in a slash in the local test, which works, and not in the remote, which doesn’t. In for a penny, let’s try that:
def set_up_for_really_jekylling
# root here is /Users/ron/programming/test-ftp
new_files_folder = '/Users/ron/Dropbox/_articles_from_ipad'
jekyll_folder = '/Users/ron/programming/rj-com'
ftp_host = 'ftp.ronjeffries.com'
ftp_target_folder = 'httpdocs/'
password_prefix = 'PROD_'
return JekyllRunner.new(new_files_folder, jekyll_folder,
ftp_host, ftp_target_folder, password_prefix)
end
Still hangs, same way we think. We think we’ll try FTP’s debug_mode
. It seems to me that we should turn off all the tests but our favorite, as well.
def move_batch(local_site_folder, ftp_target_folder, folders_and_files )
perform_in_folder(local_site_folder) do
puts "connecting"
ftp_connection = ftp_connected_to_target
ftp_connection.debug_mode = true
puts "connected to #{ftp_target_folder}"
ftp_connection.chdir(ftp_target_folder)
puts "on folder"
folders_and_files.each do |file|
puts "moving a file: #{file}"
ftp_one_file(ftp_connection, file)
puts "done moving #{file}"
end
puts "closing"
ftp_connection.close
puts "closed"
end
end
We run some debug mode and learn very little. The file’s just not going, though the connection seems to be working. The FTP object says it is connected to /httpdocs
. Tozier is concerned about the slash. Interestingly there is no slash in our input, so where did it come from?
As we flail, we check Transmit with a restart and it shows feed.xml with zero length. This tells us, we think, that our test has actually managed to touch and empty the file, but not put data into it.
We realize we’ve never directly tested using FTP to put a file on my site, back in our other earlier tests. We’d better do that: there’s apparently something about how we’re using FTP. We move into our old test of ftp, and produce this:
require "minitest/autorun"
require "net/ftp"
require "./passwords"
class Test_FTP < Minitest::Test
# utility functions
...
def ftp_to_site
ftp = Net::FTP.new('ftp.ronjeffries.com')
host = Object.const_get('Passwords::' + 'PROD_' + 'USER')
pass = Object.const_get('Passwords::' + 'PROD_' + 'PASSWORD')
ftp.passive = true
ftp.login(host,pass)
puts "#{ftp.pwd}"
puts "#{ftp.status}"
# puts "top list #{ftp.list.inspect}"
ftp.chdir('./httpdocs')
ftp
end
...
# actual tests
def setup
@folder = Dir.pwd
FileUtils.rm_rf("./_target")
FileUtils.mkdir("./_target")
end
def teardown
Dir.chdir(@folder)
end
...
def test_read_site
puts "test_read_site"
ftp = ftp_to_site
ftp.debug_mode = true
puts "#{ftp.pwd}"
# puts "file: #{ftp.gettextfile('feed.xml').inspect}"
puts "list #{ftp.nlst}"
# puts "feed: #{ftp.gettextfile('feed.xml')}"
puts "end test_read_site"
ftp.putbinaryfile('to_move.txt')
end
end
From this test, which we checked manually for now, we learned that we needed passive mode. When we set passive in the ftp_to_site
method, our test began to produce the right results, so far only shown in our console. (We promise ourselves that we’ll improve this test.) But wait! We had already set passive in the failing test:
def move_batch(local_site_folder, ftp_target_folder, folders_and_files )
perform_in_folder(local_site_folder) do
puts "connecting"
ftp_connection = ftp_connected_to_target
ftp_connection.debug_mode = true
ftp_connected_to_target.passive = true
puts "connected to ftp, chdir #{ftp_target_folder}"
ftp_connection.chdir(ftp_target_folder)
puts "on folder #{ftp_connection.pwd}"
folders_and_files.each do |file|
puts "moving a file: #{file}"
ftp_one_file(ftp_connection, file)
puts "done moving #{file}"
end
puts "closing"
ftp_connection.close
puts "closed"
end
end
See? There it is right there, passive=true
. Oh. Look again. We just hammered a convenient new member variable into whatever ftp_connected_to_target returns, namely a new, unused, instance of FTP. Our setting of passive has no effect. It is null, and void. It is a dead parrot.
Excuse me while I curse a bit. We fix the problem:
def move_batch(local_site_folder, ftp_target_folder, folders_and_files )
perform_in_folder(local_site_folder) do
puts "connecting"
ftp_connection = ftp_connected_to_target
ftp_connection.debug_mode = true
ftp_connection.passive = true
puts "connected to ftp, chdir #{ftp_target_folder}"
ftp_connection.chdir(ftp_target_folder)
puts "on folder #{ftp_connection.pwd}"
folders_and_files.each do |file|
puts "moving a file: #{file}"
ftp_one_file(ftp_connection, file)
puts "done moving #{file}"
end
puts "closing"
ftp_connection.close
puts "closed"
end
end
And all the tests run, just as we expected three hours ago. Wow. Now of course we have debug statements all over. We’ll clean those up Thursday. For now, let’s think about what we could have done to avoid this long and deep rat hole.
Retrospective …
We were fairly confused because it was timing out, and Tozier remembered the passive
mode from of old. We decided to put it in. We put it in the wrong place, the tests didn’t run. We decided we’d better write tests in our earlier test_ftp.rb
, which had never actually written to the real site.
We messed with that rather too long, because we had, we thought, eliminated passive
as something that mattered. So we walked through to the same place as in our original test. We googled again why it’s not returning anything, and so we again put passive
in. We continued to run our little test and it seemed at first to return no data from get. We finally noticed that, despite confusing Ruby documentation, that it just creates a file in the current folder with the same name as the file you get. This caused a great insight: “IT’S BEEN WORKING ALL ALONG”.
That being the case, we looked back at the test_jekyll_runner
test, because it “should” have been working too. Tozier spotted the typo (which was an auto-complete error). Had we typed out the variable name originally, I believe, we’d likely not have made the error. We were just unlucky that the auto-completed name also accepted passive=true
.
Except that I don’t like answers like “we were just unlucky”. What might we have done better? What’s to learn other than “pay better attention”?
We should probably have turned sooner to our simpler test. FTP wasn’t working, and we have a test file for FTP. The failure was in our JekyllRunner
tests, which obfuscated things. We “should” have looked at our older tests, noticed that we didn’t ever connect to a real FTP and started digging in sooner.
Note that we had not run those tests for a long time, and they didn’t work at all, because of our new passwords.rb
format. Something something about a suite of tests instead of just running the ones we care about. Wouldn’t really have made a difference here but it would be better practice.
Had we turned to the simpler test sooner, we might have had fresher mind when we rediscovered the need for passive
. We might have said wait what we did that and looked at the bad line sooner. Maybe. Anyway, there’s a lesson here about turning quickly to the more basic tests, and a lesson about running all the tests all the time.
Beyond turning to our simpler tests sooner, what might have helped?
Tozier notes that we have a lot of variables and methods whose names start with ftp_
. Is this a problem? Yes, it is. Why, Ron, you ask.
Well, grasshopper, what those names tell us is that in our program there is an idea, the “ftp thing” that is trying to be born. Our JekyllRunner
does a lot of FTP stuff, and a lot of non-FTP stuff, and they’re only broken out by a weak1 naming convention. So there’s an abstraction missing. The code does not “express all the programmer’s ideas”, as we were taught long ago.
We give ourselves a bit of a back-pat for having made some headway, with our connection object, but obviously we’ve not done enough.
Ron recalls another place where there seems to be ideas missing. We have vast tracts of arrays of strings that we pass around. Object-oriented programming 101 tells us that strings are a weak (q.v.) abstraction and that they are always calling for a smarter object. Bottom line, this code, despite all the cleaning we have done, is messy enough to confuse us.
Think about this! We’ve got less than 400 lines of code and tests, and that’s enough to leave us confused. If we press forward with this kind of thing on a real project, we’ll have 4,000 lines, 40,000 lines, a half a million lines of code that we struggle to understand. This tiny program is a microcosm that tells us how clean we need to be.
Honestly, we were feeling pretty good about the code. We were planning, this morning, to ship our MVP. And we still may do that. But the code is not good enough to be maintainable.
Next Thursday
We talk about what we might do next time. T suggests that we need to remove all our debugging instruments. I ask “was there a moment where that many put
statement should have triggered a reset?” T nods. Anyway we’ll clean those out.
Then we face a decision. Make it run (which I now am rather confident it does), or make it better? We’re under strong pressure from our Product Owner to get this article out, and there’s no way to do it until this program works.
We’re not confident enough to do it today, so the soonest is Thursday.
What else might we do? We might build an object of some kind, whatever the code is asking for, make that work (heck, maybe even test it), and then think about deploying. What we’ll do, for sure, is reflect again, with fresh minds and eyes, and decide then.
Tozier thinks he’d like to finish what we set out to do today, namely a single JekyllRunner
level test that moves a single test file. I think I agree. That test needs elaboration and we now probably know enough FTP that we can put a file up and then read it back, and perhaps even delete it.
NARRATOR: They don’t know enough. They still don’t know how to get the text out of an FTP’d file.
We have more to do, such as the cron
job, and we can manually run the thing to put up our current article. We need to build the cron
, deciding issues like how often we’ll really run this. We think we need to clean up the paths that fly all around this thing. That may be a call for another object or two. Additionally, I’ve been thinking about materializing a Manifest
object that represents the information about what’s in the iPad folder and therefore what needs to be moved. I’ve been thinking that if we had a Manifest
, we could put dates in it and at some future time have incremental build.
Tozier wants more confidence around moving binary and text files. We’ve been thinking we can use binary for everything, because we really don’t want to work out what types files actually are, but we don’t know for sure how well that’ll work.
At that, our brains are full.
See you Thursday …
Thursday!
We mill around a bit, refreshing our minds. We remove a bunch of debugging puts
from our test-ftp
and it all runs. Skim what follows and we’ll talk about the important bits just below:
require "minitest/autorun"
require "net/ftp"
require "./passwords"
class Test_FTP < Minitest::Test
# utility functions
def ftp_connected_to_target
ftp = Net::FTP.new('localhost')
ftp.passive = true
host = Object.const_get('Passwords::' + 'TEST_' + 'USER')
pass = Object.const_get('Passwords::' + 'TEST_' + 'PASSWORD')
ftp.login(host,pass)
ftp.chdir('programming/test-ftp/_target')
ftp
end
def ftp_to_site
ftp = Net::FTP.new('ftp.ronjeffries.com')
host = Object.const_get('Passwords::' + 'PROD_' + 'USER')
pass = Object.const_get('Passwords::' + 'PROD_' + 'PASSWORD')
ftp.passive = true
ftp.login(host,pass)
ftp.chdir('./httpdocs')
ftp
end
def ftp_folder_exists?(ftp, folder_string)
split = folder_string.split("/")
proposed_folder = split.last
ppl = split[0...-1]
proposed_prefix = "./" + ppl.join("/")
ftp.list(proposed_prefix).any? { |name| name.match(proposed_folder) }
end
def ftp_mkdir_safely(ftp, folder_string)
ftp.mkdir(folder_string) unless ftp_folder_exists?(ftp, folder_string)
end
# actual tests
def setup
@folder = Dir.pwd
FileUtils.rm_rf("./_target")
FileUtils.mkdir("./_target")
end
def teardown
Dir.chdir(@folder)
end
def test_read_site
ftp = ftp_to_site
ftp.putbinaryfile('to_move.txt')
end
def test_target_is_empty
target = Dir.glob('_target/*')
assert_equal(0,target.length)
end
def test_connect_finds_no_files
ftp = ftp_connected_to_target
list = ftp.list('*')
ftp.close
assert_equal(0,list.length)
end
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
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)
Dir.chdir(oldDir)
end
def test_make_subfolders_safely
ftp = ftp_connected_to_target
ftp_mkdir_safely(ftp, "subfolder")
ftp_mkdir_safely(ftp, "subfolder")
ftp_mkdir_safely(ftp, "otherfolder")
ftp_mkdir_safely(ftp, "otherfolder")
ftp_mkdir_safely(ftp, "otherfolder/subsubfolder")
ftp_mkdir_safely(ftp, "otherfolder/subsubfolder")
ftp.putbinaryfile("test-ftp.rb","otherfolder/subsubfolder/RUBY.rb")
ftp.close
exists = Dir.exist?("_target/subfolder")
assert(exists,"folder not made")
exists = Dir.exist?("_target/otherfolder/subsubfolder")
assert(exists,"subfolder not made")
end
def test_jpg_cant_be_puttexted
ftp = ftp_connected_to_target
Dir.chdir("/Users/ron/Dropbox/_source")
ftp.putbinaryfile('pic.JPG','../poc.JPG')
ftp.putbinaryfile('a.txt', '../a.txt')
## we inspect these manually. so sue me.
end
end
Our newest test is the only one we care about:
def ftp_to_site
ftp = Net::FTP.new('ftp.ronjeffries.com')
host = Object.const_get('Passwords::' + 'PROD_' + 'USER')
pass = Object.const_get('Passwords::' + 'PROD_' + 'PASSWORD')
ftp.passive = true
ftp.login(host,pass)
ftp.chdir('./httpdocs')
ftp
end
def test_move_file_to_site # checked manually
ftp = ftp_to_site
ftp.putbinaryfile('to_move.txt')
end
Note that we just moved the file and looked. We didn’t automate reading it back. This may come back to haunt us: I promise to confess if it does.
These tests gave us the confidence to add a file-moving test to our JekyllRunner
tests, which we ran once and also commented out:
# following test we checked manually and now trust it
# cf. hold my beer
# def test_move_test_file_to_site
# jr = set_up_for_really_jekylling
# testfile = "#{jr.jekyll_folder}/_site/test-file-do-not-read.txt"
# FileUtils.touch(testfile)
# jr.move_test_file
# end
def move_test_file
move_batch("#{@jekyll_folder}/_site/", '.', ['test-file-do-not-read.txt'])
end
Note that we just put a test method into JekyllRunner
. Tozier points out that we could reopen JR in our test and inject a method into it, thus not contaminating JR’s source with test methods. We don’t remember the syntax, though we do have the Internet right here. In addition this seems to me to be pretty deep in the bag of tricks.
Chet wonders if it would be better to subclass a TestJekyllRunner
and put the method there. I’m feeling like “meh” on both these ideas. T’s concern is that when we tried calling the inside method move_batch
from the test, it didn’t hook up correctly, and creating the move_test_file
method in JekyllRunner
did work. I’m disinclined to chase code that was invading the class under test in any case.
We have checked that a file moves to the correct place on my web site, using the JekyllRunner
as set up in our tests. This gives me good but not great confidence that a correctly-configured JekyllRunner
, running directly from command line, will correctly copy down iPad articles, run Jekyll, and put the results on my site.
Now I have to ask myself Do you feel lucky, punk? Am I willing, finally, to set up a main program running JR, and set it loose manually? I guess after I push everything here to GitHub, I am.
OK. What do we have to do to run a JekyllRunner
by hand? Tozier suggests:
- Make sure this article is saved in the iPad input folder.
- Construct a new
JekyllRunner
with the arguments as in our tests. - Run it
- Look and see what happened.
I guess we can do the second item2 with a main in the jekyllrunner.rb
file. Well, no! That would mean that if we accidentally run it here in our editor, it would do its thing. We’ll create a new file, jr.rb
just for this purpose.
Tozier says that at this point one would normally create a Ruby Gem, install it, and then it’d be available with a suitable require. This is a yak I didn’t see coming and frankly I’m trying to scare it away. I want to know why we don’t just copy the JR file into my site, along with the passwords file.
We may want to package this up later, figure out a release build and such but for now I’m inclined to copy the two files into my Jekyll source folder. OK, that’s done, now to make jr.rb
.
require "./jekyllrunner"
new_files_folder = '/Users/ron/Dropbox/_articles_from_ipad'
jekyll_folder = '/Users/ron/programming/rj-com'
ftp_host = 'ftp.ronjeffries.com'
ftp_target_folder = 'httpdocs/'
password_prefix = 'PROD_'
jr = JekyllRunner.new(new_files_folder, jekyll_folder,
ftp_host, ftp_target_folder, password_prefix)
jr.run
We run it, and the fact that you’re reading this shows that it worked.