Hardscrabble šŸ«

By Maxwell Jacobson

See also: the archives and an RSS feed

onscreen texts and I'm so Happy I'm Ecstatic

August 20, 2014

People have been linking to this terrific video by Tony Zhou about how film and television depicts onscreen text messaging:

(I saw it on Kottke which has some additional context)

The video brings up some memories for me, from when I wanted to make movies, and used a similar technique1. My short film ā€œIā€™m so happy Iā€™m Ecstaticā€ was uploaded to Vimeo in May 2010, just two months before Sherlock debuted on BBC.

Zhou isnā€™t the first to notice this trend. In April 2013, Rachel Dodes Whortman wrote an article about it for the Wall Street Journal (ā€œFrom Talkies to Textiesā€). When I saw the article I felt the need to reply:

I think Iā€™m less petulant now than the nerd who wrote that tweet2. I definitely donā€™t think I was the first to do that (Zhouā€™s video provides a few earlier examples, most embarrassingly 2008ā€™s Sex Drive which I did totally see) and I definitely didnā€™t do it as elegantly as Sherlock.

But some small petty part of me wants to be in videos like that or to maybe keep making video things.

  1. I think I wrote it into the screenplay for the short, but I want to give credit to my friend Sarah Mayo who co-edited it with me and made it work ā€“ the few times Iā€™ve seen people watch it, thatā€™s their favorite part by farĀ 

  2. (but then, I am writing this blog post)Ā 

composing a Parslet parser from modules and making code climate happy

July 27, 2014

Iā€™m making very slow and intermittent progress on smashcut, my Fountain screenplay parser written in Ruby. At this rate Iā€™ll have a usable version in a few years or so šŸ™‚. Iā€™m going to talk about it a little bit here and then talk about how a small refactor boosted my Code Climate GPA for the project from 1.9 to 3.56.

(For those who donā€™t know: Code Climate is a very neat website which analyzes your code and points out improvement areas. They provide free analysis to open source projects like smashcut and paid analysis of private projects. They also maintain a cool blog about code design and techniques at http://blog.codeclimate.com/.)

Today on a train ride I reorganized the smashcut codebase a bit. The heart of the project is a file called fountain_parser.rb which describes the grammar of a Fountain screenplay as a list of rules. The syntax for defining a rule uses a DSL provided by Parslet, a great Ruby gem for exactly this purpose. Hereā€™s the code example from their get started page:

require 'parslet'

class Mini < Parslet::Parser
  rule(:integer) { match('[0-9]').repeat(1) }
  root(:integer)
end

Mini.new.parse("132432")  # => "132432"@0

This is a very simple grammar with only one rule in addition to the required root (which names the rule that is expected to come first). If you were to define a grammar for something more complex, like a screenplay or even a programming language, you could expect there to be many, many more rules for defining specific, small things like operator characters and return charactersand then also abstract things like a characterā€™s monologue or a programā€™s function.

At some point, your parser class will get quite long. I think this is kind of to be expected and not necessarily a bad thing. But it sure hurts your Code Climate GPA, which drops at the sight of long and complex classes. It also balks at code outside of methods, which is I think unfairly punishing to projects using DSL-style libraries.

It took some figuring out, but it is possible to compose your parser from modules. That example above could be rewritten like this:

require 'parslet'

module NumberRules
  include Parslet
  rule(:integer) { match('[0-9]').repeat(1) }
end

class Mini < Parslet::Parser
  include NumberRules
  root(:integer)
end

Mini.new.parse("132432")  # => "132432"@0

This is pretty nice when you have more than a few rules, and Code Climate rewarded me with a coveted A grade: https://codeclimate.com/github/maxjacobson/smashcut/compare/0cd1d78b...c2668c0eā€¦ well, for some of the classes anyway.

EDIT 2015-01-08: Hrmph, that link is dead now. Suffice it to say that the changes from this pull request improved the GPA from 1.9 to 3.56.

An Alfred Workflow for adding the frontmost Firefox tab to OmniFocus with AppleScript

July 13, 2014

Saw a link about using tools to take long-lasting tabs out of your browser and put them somewhere useful like Evernote or OmniFocus rather than let them tax your mind and memory. I wanted in.

Sadly, there was only support for Chrome and Safari, not Firefox, which I continue to stubbornly prefer. I assumed at first this was simply a reflection of Firefox not being particularly popular among Mac users1 and resolved to patch the gap.

In order to interact with your running Mac applications, people often use AppleScript, a kind of strange programming language that comes with Macs and lets you script or automate user tasks. So whenever I want to close a tab without forgetting that it might matter somehow, I have to do these steps manually:

  • open the omnifocus quick entry panel with the global hotkey
  • write some kind of task title
  • go back to the browser
  • copy the URL
  • go back to the quick entry panel
  • type āŒ˜+', the keyboard shortcut to open the notes field
  • paste the URL
  • type āŒ˜+' again to return to the title field
  • press enter twice

Imagine having two dozen tabs open and kind of wanting to do that for each of them? Prime candidate for automation!

Given that this is my first AppleScript, Iā€™m bizarrely confident that I can figure this out. I assume I can just ask Firefox for the list of currently open URLs, write some kind of loop, and then send OmniFocus a bunch of messages (in the background maybe) and have it all just work easily. Pretty quickly I learn that, unlike Safari and Chrome, Firefox exposes nothing to AppleScript. Itā€™s scriptable insoafar as every app is scriptable ā€“ you can perform user actions like clicking and typing, but it doesnā€™t expose anything nicely, and Mozillaā€™s bugtracker has multiple tickets dating back several years of people saying they wish Firefox supported AppleScript without much movement as far as I can tell.

No worry! Iā€™ll find some clever solution. I open AppleScript Editor and start trying to figure out how to even do any of this.

tell application "FirefoxNightly" # I'm using the nightly build, and I have to specifically say so
  activate # this opens or switches to the application
end tell

a digression about running AppleScripts

You can write small things like this in the AppleScript Editor app and run them from there. This is what I was doing while writing the thing and tinkering. But itā€™s not a great interface, really, as far as text editors go. Another option is to use the texteditor of your choice and run it from the terminal by writing osascript your_file.scpt. Thereā€™s something kind of trippy about running something like this, and seeing your operating system react:

[hardscrabble] [hardscrabble.net] [my-first-applescript branch]
ā‡„ osascript -e 'display alert "hello world"'
button returned:OK

Later on you can use tools like Alfred to run your scripts from anywhere at any time.

back to struggling with this code

So if Firefox doesnā€™t expose anything to us, how can we get the list of tabs open? I went down a few dead ends. I spent several minutes trying to make a bookmarks group of the currently opened tabs, then export the bookmarks, then parse the exported file before I realized I was definitely going the wrong way and I definitely didnā€™t know how to do parse JSON in AppleScript.

After a little more googling I came upon a comment on Bugzilla which provides a way to get the current pageā€™s URL at least. Hereā€™s how:


tell application "FirefoxNightly"
  activate
  delay 2 # sleeps 2 seconds because idk... it doesn't work otherwise
  tell application "System Events"
    tell process "firefox"
      keystroke "l" using {command down} # āŒ˜+l is the keyboard shortcut to focus on the address bar
      keystroke "c" using {command down} # āŒ˜+c copies the current URL to the clipboard
      set tabUrl to get the clipboard    # stores the clipboard in a variable
    end tell
  end tell
end tell

display alert tabUrl

Yikesā€¦ AppleScript is interesting because itā€™s an awkward, not fun language but also super powerful if youā€™re a kind of nerdy Mac power user, or maybe even developer who wants to harness the power of GUI applications in their project. Fortunately the next major version of OS X, Yosemite, has a JavaScript API for doing all the same stuff. This might be my last AppleScript too, because of that.

Now that I have the URL I want to do something better than display an alert. Some googling points me to a 2012 MacSparky blog post about AppleScripting with OmniFocus. The language is still awkward (set theProject to first flattened project where its name = "Finance"), but this is significantly better than Firefox because it exposes an API at all. Since then, thereā€™s been a major 2.0 update to OmniFocus, but I guess they made an effort to maintain consistency because these examples still work. I donā€™t really blame Mozilla because theyā€™re maintaining applications across every operating sytem that will support them and are very consistent. In fact I wish there existed OmniFocus for Linux but, like, lol. Everyone compromises!

OK so I can get the current URL and add a task to OmniFocus. A little more digging and this is what I came up with:

tell application "FirefoxNightly"
  activate
  set taskTitle to name of front window

  tell application "System Events"
    tell process "firefox"
      keystroke "l" using {command down}
      keystroke "c" using {command down}
      set taskNote to get the clipboard
    end tell
  end tell
end tell

tell application "OmniFocus"
  tell quick entry
    open
    make new inbox task with properties {name:taskTitle, note:taskNote}
  end tell
end tell

Itā€™s kind of a dinky version of what I originally wanted to make. Itā€™s only for the current tab, and when itā€™s done the quick entry panel is still open (by design: so I can still edit the details).

I packaged it as an Alfred Workflow here so I can invoke it from wherever by typing āŒ˜+space and then just writing tab.

I wonder if this is useful to other people!

  1. Recently on the Accidental Tech Podcast I was surprised to hear the hosts be super harsh to Firefox. This tweet makes the argument that Firefox isnā€™t relevant anymore and I donā€™t really know if thatā€™s true, but I think it should be.Ā 

My New Computer

June 29, 2014

Hereā€™s a screenshot of my desktop as of right now:

My desktop

Itā€™s an Intel NUC. Itā€™s tiny. Itā€™s mounted to the back of my 23ā€ Monitor I bought like 4 years ago. Itā€™s really clever ā€“ those 4 standard screws that are meant for mounting the monitor to an arm or the wall can instead have the computer mounted to it. Itā€™s replacing an old 40 pound (maybe, Iā€™m totally guessing) Dell tower hand me down from my dadā€™s office.

Itā€™s running Ubuntu 14.04 LTS1 with the Gnome Metacity Flashback desktop. I really like this desktop because it leaves a ton of space for stuff on the screen, but has access to all the stuff you need. I havenā€™t used Linux very long, just as a secondary computer for two years or so, but nonetheless Iā€™m nostalgic for this desktop beacuse itā€™s the one I first used on a very underpowered netbook. I enjoy its multiple desktop indicators in the bottom right corner, which show the position of the windows in silhouette. I like pressing ctrl+alt+arrow to go between them, and ctrl+alt+shift+arrow to drag the active window with me.

I figured out today how to remove icons from the top left launcher panel. I could not figure it out at all, and was grateful to see others struggling with the same. I took the opportunity to add some nice web bookmarks.

I took a few minutes today to read about Mint Linux and considered switching, but I donā€™t really like how it looks. Iā€™m worried that this particular look that I use isnā€™t very popular and wonā€™t be maintained for long, so I want to share that I like it this way and I recommend it, especially for programmers and writers interested in a simple and clean user experience thatā€™s not plugged in to all of their existing notification-generating lives.

My new computer isnā€™t actually very new. I got it nearly two months ago. I havenā€™t given much thought to the idea of using Linux as a primary computer. I think that would be pretty hard given how used I am to OS X, but itā€™s a terrific secondary computer.

  1. Unlike most version numbers, this means it came out in April 2014. LTS stands for ā€œLong Term Supportā€Ā 

whoa, ruby's alias is weirder than I realized

June 23, 2014

Just saw some ruby code using the alias method, and did a quick and routine google to find some examples of how it works, especially as compared to the alias_method method.

This blog post and some others recommend to use alias_method over alias and Iā€™m going to agree, but for different reason: calling alias looks weird to me.

This looks perfectly normal to me:

class Whatever
  def whatever
    "whatever"
  end

  alias_method :something, :whatever
end

Whatever.new.whatever #=> "whatever"
Whatever.new.something #=> "whatever"

alias_method is a method that takes two arguments and theyā€™re separated by commas. Under the hood I donā€™t know what itā€™s doing but thatā€™s never stopped me from calling a method, and I know how to call a method.

This looks goofy to me:

class Whatever
  def whatever
    "whatever"
  end
  alias :something :whatever
end
Whatever.new.whatever #=> "whatever"
Whatever.new.something #=> "whatever"

What even is that? It looks like weā€™re calling a method, but the arguments arenā€™t comma-separatedā€¦ it feels weird.

I guess this probably isnā€™t a great reason to prefer one programming technique over another, but for me itā€™s harder to understand and therefore remember, and what I really like about Ruby is that itā€™s simple ā€“ almost everything is an object or a method, which follow some set of learnable rules.

alias_method is a method; alias is a keyword, like def or class and it can be even syntactically weirder:

class LowfatKefir
  def probiotic?
    true
  end
  alias tasty? probiotic?
end
LowfatKefir.new.tasty? #=> true

Not only do you not need to comma-separate the ā€œargumentsā€ to alias, but they donā€™t have to be symbols either which feels like another violation of my understanding of how this language works, which is that we use symbols when we want to reference methods without invoking them, because referencing the method always invokes it.

selecta

May 5, 2014

One of my favorite things about my vim setup is the way I open files. For a little while I used ctrlp and then I switched to Gary Bernhardtā€™s selecta script.

selecta is cool in part because of how itā€™s designed to be more than a vim plugin, although thatā€™s mainly what I use it for. The readme gives a few examples of how else it could be used and other people come up with weirder stuff.

Also cool is the means of distribution. Recently someone added it as a homebrew formula, making it easy to install and keep it up-to-date on a Mac, but until then, the recommended move was to download a raw ruby script, make it executable, and put it on your PATH, and then I guess hope to remember to update it occasionally. If you want to use it in vim, you can copy and paste a vimscript function from the readme into your vimrc.

Hereā€™s the code from the readme:

" Run a given vim command on the results of fuzzy selecting from a given shell
" command. See usage below.
function! SelectaCommand(choice_command, selecta_args, vim_command)
  try
    silent let selection = system(a:choice_command . " | selecta " . a:selecta_args)
  catch /Vim:Interrupt/
    " Swallow the ^C so that the redraw below happens; otherwise there will be
    " leftovers from selecta on the screen
    redraw!
    return
  endtry
  redraw!
  exec a:vim_command . " " . selection
endfunction

" Find all files in all non-dot directories starting in the working directory.
" Fuzzy select one of those. Open the selected file with :e.
nnoremap <leader>f :call SelectaCommand("find * -type f", "", ":e")<cr>

I made one change, which was to use ctrl+p instead of leader+f, because Iā€™d trained my fingers on ctrl+p and didnā€™t have the reserves of peace & discipline to consider changing. Other than that I left it as-is, until today.

I wanted to edit my .gitignore file, and noticed that it wasnā€™t opening. Not a big deal, I wrote :e .gitignore and survived.

The comment says it opens all files in all non-dot directories. Itā€™s kind of true but not exactly, as it ignores dotfiles regardless of their location. I definitely want it to ignore files in dot directories (.git and .sass-cache come to mind), but I donā€™t want it to ignore most dotfiles.

selecta works like this: you pipe some list of information into it, it breaks it apart, lets you select one line from the list, and then pipes that selection out to the next command. This vim plugin runs the bash command find * -type f, which prints the list of non-dot files (-type f excludes directories, * excludes dots) and then pipes it to the vim edit command :e.

Since setting this up months ago Iā€™ve learned and used the find command a few times but had no idea it was powering my fuzzy opener thing! Something opaque opened up a little. So I edited that last part like this:

" Find all files and all non-dot directories
" starting in the working directory
" and edit the chosen thing
nnoremap <c-p> :call SelectaCommand("find . -not -path './.*/*'", "", ":e")<cr>

The main changes:

  • dotfiles now show up, so I have easier access to files like .gitignore and .travis.yml
  • directories now show up, so I can jump directly to specific directories in netrw
  • now thereā€™s a leading ./ in the list of options because find * and find . have a different output?

I made a before and after gif.

I think I like this! But actually what if it sorted the files by last modified date?

refactoring very old code

April 13, 2014

Before I went to Flatiron School last summer I cobbled together a web app called Layabout. This weekend, for the first time in a very long time, I worked up the nerve to look at the source code. You may have heard the phrase ā€œcode smellā€ but Iā€™m not sure it applies to this code. This code is like that ominous stink youā€™d almost rather ignore?

Hmm.

Layabout is an Instapaper client that focuses on the videos youā€™ve bookmarked. I was the only one who ever used it. I stopped using it in part beause it was buggy, and in part because I wanted to try out Pocket for a little while, which ended up being most of a year.

Inspired by John Resigā€™s recent blog post ā€œWrite Code Every Dayā€ encouraging employed programmers to maintain side projects in a sustainable, I decided to revisit Layabout and see if anything Iā€™ve learned in the year since I last edited it would come in handy. I also enjoy laughing at myself from the past.

The app had been running on Heroku unused for a year. When I logged in1 and tried to watch a video, it immediately crashed. I found the codebase locally and ran heroku logs to try to get some more information about the crash but it simply said it was a 500 error. Hmm.

So I tried running the app locally2 and was surprised to find that it worked even worse than it did on Heroku! Online at least I could type my password in before it crashed, but locally no pages were rendering at all. Instead it was complaining about not being able to find the views. I googled the error message and learned that some versions of Sinatra require you to explicitly tell it where your views are. I had never had this error before, but I was having it now. I looked at my Gemfile and saw that in my list of dependencies, I didnā€™t specify my desired version number for any of them. Code smell! You gotta lock that down, because things can change and break, especially when you donā€™t edit your code for over a year.3

After resolving that error I was able to reproduce the crash and see a much more verbose error message. You know how some YouTube videos have embedding disabled? Iā€™m using a library, ruby-oembed, to fetch embed codes from video URLs, and it raises an exception when the video has embedding disabled. I used to think that meant my thing needed to crash but Iā€™ve since learned about exception handling. Hereā€™s a before:

def get_embed (vid_site, id)
  if vid_site == :youtube
    url = "http://www.youtube.com/watch?v=#{id}"
    return OEmbed::Providers::Youtube.get(url).html
  elsif vid_site == :vimeo
    url = "http://www.vimeo.com/#{id}"
    return OEmbed::Providers::Vimeo.get(url, maxwidth: "500", portrait: false, byline: false, title: false).html
  elsif vid_site == :hulu
    url = "http://www.hulu.com/watch/#{id}"
    return OEmbed::Providers::Hulu.get(url).html
  else
    return "<p>Failed to get embed code</p>"
  end
end

And after:

def get_embed (vid_site, id)
  begin
    case vid_site
    when :youtube
      OEmbed::Providers::Youtube.get("http://www.youtube.com/watch?v=#{id}").html
    when :vimeo
      OEmbed::Providers::Vimeo.get("http://www.vimeo.com/#{id}", maxwidth: "500", portrait: false, byline: false, title: false).html
    when :hulu
      OEmbed::Providers::Hulu.get("http://www.hulu.com/watch/#{id}").html
    else
      "Unsupported site"
    end
  rescue Exception => e
    puts e.inspect
    "<p>Sorry, couldn't get the embed code for this one. Maybe it doesn't allow embedding? Or maybe it was deleted? Sorry.</p>"
  end
end

(I guess I didnā€™t know about case statements or implicit return values then either.)

Itā€™s still not that great honestly. I wish it were more object oriented. I wish there were tests. I wish I could open source it without exposing my Instapaper API keys. I know itā€™s possible to scrub your history and Iā€™m going to look into that, and then keep them in a separate file ignored by git; or maybe Iā€™ll look into getting new keys. But Iā€™m glad to be revisiting this project because itā€™s where I got excited about web development. One last before and after:

Layabout needs to ignore bookmarks that it doesnā€™t know how to embed. It also needs clean URLs to reliably lookup their embed codes. Hereā€™s an old method which helped with this task:


def grok_url (url)
  # TODO support hulu short urls
  if url =~ /youtube\.com\/embed\//
    id = url.match(/\/embed\/([A-Za-z0-9_-]+)/)[1].to_s
    site = :youtube
  elsif url =~ /youtube\.com/
    id = url.match(/v=([A-Za-z0-9_-]+)/)[1].to_s
    site = :youtube
  elsif url =~ /youtu\.be/
    id = url.match(/(http:\/\/youtu.be\/)([A-Za-z0-9\-_]+)/)[2].to_s
    site = :youtube
  elsif url =~ /vimeo\.com\/m\//
    id = url.match(/\/m\/(\d+)/)[1].to_s
    site = :vimeo
  elsif url =~ /vimeo\.com/
    id = url.match(/vimeo\.com\/([\d]+)/)[1].to_s
    site = :vimeo
  elsif url =~ /hulu\.com\/watch/
    id = url.match(/watch\/([\d]+)/)[1].to_s
    site = :hulu
  else
    return false, false, false
  end
  return true, site, id
end

I used it like this:

video_links = Array.new
if folder_id == :readlater
  all_links = ip.bookmarks_list(:limit => 500)
else
  all_links = ip.bookmarks_list(:limit => 500, :folder_id => folder_id)
end
all_links.each do |link|
  if link["type"] == "bookmark" # filters out the first two irrelevant items
    is_video, vid_site, video_id = grok_url link["url"]
    if is_video == true
      link["video_id"] = video_id
      link["title"] = cleanup_title link["title"] # prob not necessary
      link["vid_site"] = vid_site
      link["description"] = make_clicky link["description"]
      video_links.push link
    end
  end
end
@videos = video_links

Honestly I think this is cool code even though itā€™s not how Iā€™d write it now, because this is what it looks like to make it work with limited knowledge. I didnā€™t know about any fancy iterators. I legit didnā€™t know about object oriented programming. I didnā€™t know itā€™s not necessary to check if a boolean == true, but it wasnā€™t hurting anyone! Whenever Iā€™m talking to beginners and see code that doesnā€™t take advantage of less-than-obvious techniques, but works, Iā€™m psyched.

Anyway this is what I changed it to yesterday:

@videos = (if folder_id == :readlater
  ip.bookmarks_list(:limit => 500)
else
  ip.bookmarks_list(:limit => 500, :folder_id => folder_id)
end).map do |link|
  if link['type'] == 'bookmark' # filters out the first two irrelevant items
    snob = FilmSnob.new(link['url'])
    if snob.watchable?
      link.tap do |link|
        link['video_id'] = snob.id
        link['title'] = cleanup_title link['title']
        link['vid_site'] = snob.site
        link['description'] = make_clicky link['description']
      end
    end
  end
end.compact

I donā€™t even necessarily think thatā€™s better, but it satisfies my learned allergy to local variables4 and makes me feel cool for using map and tap.

Ah, and whatā€™s FilmSnob? Letā€™s revisit Resigā€™s blog post:

I decided to set a couple rules for myself:

  1. I must write code every day. I can write docs, or blog posts, or other things but it must be in addition to the code that I write.
  2. It must be useful code. No tweaking indentation, no code re-formatting, and if at all possible no refactoring. (All these things are permitted, but not as the exclusive work of the day.)
  3. All code must be written before midnight.
  4. The code must be Open Source and up on Github.

Because Layabout isnā€™t currently open-sourceable, I extracted part of it into a rubygem called film_snob. Itā€™s basically just that grok_url method, but made object-oriented, tested, and designed to be more extensible in case I ever want to support more than three video sites. I hope to also extract the embedcode fetching from Layabout into film_snob, but without the ruby-oembed dependency.


update I did implement the oembed stuff!! Yeah!!

  1. By giving my Instapaper credentials directly to the app, which I now know is kind of a no-no in the age of Oauth (though Iā€™ve given my Instapaper credentials directly to some other apps like Reeder and Tweetbot so idk. Definitely a smell.Ā 

  2. Which was totally possible because all of the API keys were hardcoded into the app. Smell!Ā 

  3. Whatā€™s weird to me is that the version number seems to have regressed? The assumption that views are in a folder called views was introduced in 1.1, and I seem to have been taking advantage of that, but then it forgets to assume that? I donā€™t know. Versions are hard.Ā 

  4. except the snob variable which Iā€™m not sure how to avoid; suggestions welcome!!Ā 

Atom Editor

April 13, 2014

I tried out Atom Editor, the new text editor from GitHub.

Iā€™m excited that GitHub is the kind of company that makes a new text editor. GitHub plays such a big part in my life as a programmer that itā€™s easy to forget that theyā€™re basically irrelevant to almost all my friends and family. But maybe they wonā€™t be forever.

Atom looks and feels uncannily like Sublime Text. I saw some speculation that GitHub had acquired Sublime Text and I kind of hope thatā€™s true, although thereā€™s been no confirmation or real indication that this is the case. Itā€™s very flagrant. But, there are several ways in which itā€™s much better:

  • It comes with a simple, native package manager
    • textmate does too, I think, but itā€™s SO CONFUSING Iā€™ve been trying to figure out how it works for years
    • sublime requires a third party thing which is confusing to install, but otherwise awesome
    • vim also requires a third party thing which is kind of confusing to install, but then, itā€™s vim, and everything is confusing
  • it reliably auto-installs its command line utility for launching the editor without needing to do any fancy symlinking business unlike Sublime
  • itā€™s written mostly in CoffeeScript and has many open source components to look at and maybe learn from
  • has some (but less than I expected) native integration with Git and GitHub, for example:
    • in the footer it will tell you how many lines were added and removed in this file since the last commit
    • has a native package to open the current file on GitHub

For the above reasons, this is probably the text editor that Iā€™ll recommend to people who are new to programming.


I wrote the above when I first got the beta a few weeks ago and expected to elaborate before posting. I havenā€™t used Atom very much since thenor even heard much about it. I went to a vim meetup and an emacs meetup last week. I use vim but Iā€™m curious to learn what emacs is all about. One of the speakers at the vim meetup said he uses and practices vim because it makes him more productive. I donā€™t know if I buy that ā€“ Iā€™ve seen people be amazingly productive with a bunch of tools. But I think it makes it more fun and enjoyable, which I guess can lead to productivity. Iā€™m reminded of a meme you hear in the writing community: writing about writing isnā€™t the point, writing is.

shebangs and bins

January 19, 2014

I had a thought the other night as I was falling asleep: I guess I could create my own shebang? I asked Siri to remind me later and fell asleep.

OK whatā€™s a shebang? Iā€™m going to try to explain my understanding of it step by step, forgive me if this is obvious or boring.

So letā€™s say you write a ruby program that looks like this:

# hello.rb
puts "hello world"

And you run it from the command line by writing ruby hello.rb and hitting enter.

Or maybe write basically the same thing in python like this:

# hello.py
print "hello world"

And run it from the command line by writing python hello.py and hitting enter.

And the same thing in CoffeeScript

# hello.coffee
console.log "hello world"

And run it by writing coffee hello.coffee and hitting enter.

Three different files are interpreted as three different programming languages by three different intrepreters. We indicate which language it is written in and should be interpreted as in two ways in those examples:

  1. which command we invoked (ruby, python, and coffee)
  2. the file extension (rb, py, coffee)

The file extension isnā€™t really necessary. See:

# hello.sandwich
puts "hello world"

? This file can be run with ruby hello.sandwich and it works totally fine. Probably continue to use conventional file extensions because they signal to your text editors and friends what type of file they are. But I think itā€™s kind of helpful to know that theyā€™re not totally necessary or magical.

OK so what if you want to call this program without specifying the interpreter every single time? This is where shebangs come in:

#!/usr/bin/env ruby
# hello.sandwich
puts "hello world"

Now this is a file with a nonsense file extension but something new: the first line (the shebang) indicates which program is meant to interpret this file. Interestingly, itā€™s not a direct path to that binary file on your file system (which in the case of Ruby programmers who use rvm to switch between Ruby versions, is often changing ā€“ mine is currently at /Users/maxjacobson/.rvm/rubies/ruby-2.1.0-p0/bin/ruby but only because Iā€™m checking out the newly released Ruby 2.1.0), but instead consults your environment and says ā€œhey, Iā€™m looking for something called ruby, who wants to handle this one?ā€

How do you call this file if you donā€™t specify the interpreter? Basically you just provide a path to the file, like this: ./hello.sandwich. Youā€™ll probably get this error: bash: ./hello.sandwich: Permission denied. Iā€™m sure there are very good security reasons for this but itā€™s kind of annoying honestly. You need to run chmod +x hello.sandwich which transforms the script from a generic text file to an ā€œexecutableā€ file. If you do that, now you can run ./hello.sandwich and your program will run thru the ruby interpreter.

What is the ./ part? Itā€™s kind of ugly, and itā€™s the next thing we want to get rid of. Itā€™s a path to the file. The same way you might run cd .. to change directories up a directory, you can run cd . to remain in your current directory. I know programming has irrevocably ruined my sense of humor because I now find it funny that you can run cd ././././././. to remain where you are. So ./hello.sandwich is a path to a file in the current directory.

Letā€™s actually rename the file to have no file extension (mv hello.sandwich sandwich) and run it again ./sandwich. Still works. Itā€™s totally possible to run this file without the ./, but we need to put it somewhere special. We need to put it on our $PATH, which is a list of all of the places on your computer you keep these executable files. To see your $PATH you can run echo $PATH and youā€™ll see them spat out all colon separated. You can move your file to one of those directories or create a new directory, probably located at ~/bin or ~/Dropbox/bin (via), and add it to your $PATH.

$PATH is a global variable in your command line environment and it can be edited like this: export PATH="whatever" but probably donā€™t do that because it will totally overwrite the variable in your current terminal session and you wonā€™t have access to any of the executables you depend on and you wonā€™t be able to do anything. The common way to append a directory to your $PATH is like this: export PATH=~/bin:$PATH, which sets the variable to ~/bin, a colon, and then everything that used to be in the variable. Perfect. If you want this directory to always be accessible, not just in the current terminal session, you should add that line of code to your ~/.bashrc or ~/.bash_profile file1 and it will be included in every new terminal session.

So, if your sandwich file is in a directory thatā€™s on your $PATH, and it has the shebang that specifies its interpreter, you can now run simply sandwich from anywhere on your computer and it will call that program. Itā€™s kind of amazing how it does it, right? You write the word sandwich, hit enter, and it looks thru a list of directories for a file with that name; when it finds one, it checks its permissions, then looks at the first line to figure out how to interpret the file; that line points to an environment-checking program and passes in the argument ā€œrubyā€2, which finds the correct ruby interpreter, and then calls it, evaluating the remainder of the program.

OK so thatā€™s awesome. But what if I want to write this program:

#!/usr/bin/env sandwich
# grilled_cheese
make_sandwich(
  ingredients: [
    "rye",
    "american cheese",
    "some sprinkles of gorgonzola",
    "maybe some sweet chili sauce",
    "butter"
    ],
  directions: [
    "get ingredients",
    "assemble a sandwich",
    "melt a little butter in a pan",
    "put the sandwich in the pan",
    "apply some downward pressure",
    "flip",
    "apply some downward pressure",
    "enjoy"
  ]
)

OK so I put ā€œsandwichā€ in my shebang instead of a real programming language. What. Haha.

Letā€™s make the grilled_cheese file executable and put it in our bin, and then run grilled_cheese. We want to confirm that itā€™s looking up and finding the sandwich program and running it. When we see ā€œhello worldā€ again, we know it is. But itā€™s not making a sandwich! But then, how can it? Our code isnā€™t even being evaluated; if it were, we would probably have an error because the make_sandwich method is not availabe in the Ruby Standard Library (unfortunately).

Letā€™s edit our sandwich program to get a sense of what it knows about our grilled cheese program.

#!/usr/bin/env ruby
# sandwich
puts ARGV.inspect

Now when I run grilled_cheese, instead of seeing ā€œhello worldā€ I see ["/Users/maxjacobson/bin/grilled_cheese"]. Ahaa! Just because we invoke the sandwich program from the grilled cheese program doesnā€™t mean we will automatically evaluate the grilled cheese code. We just pass the filename as an argument, much like when we run ruby hello.rb. Itā€™s a lot like if we ran sandwich grilled_cheese.

OK so knowing that, maybe our sandwich file should look something like this:

#!/usr/bin/env ruby
# sandwich

def make_sandwich(options)
  options[:ingredients].each do |ingredient|
    print "Finding #{ingredient}..."
    sleep 1
    print " done!\n"
  end
  options[:directions].each do |step|
    print "Doing #{step}..."
    sleep 1
    print " done!\n"
  end
end

eval File.read(ARGV.first)

So, this defines our missing method, takes in the file as an argument, reads the file as a string, and then evaluates the file with eval. OK so the only thing I know about eval is that you probably should never use it because itā€™s dangerous af. But maybe this is the kind of context where it makes sense?


Iā€™m sure there are actually practical reasons to use custom-written programs in the shebangs of other custom-written programs and if you think of any please let me know.

  1. Unfortunately still donā€™t have a really solid understanding of the difference between those.Ā 

  2. env is available to use from the command line as well: try running env ruby -vĀ 

updating a jekyll/github blog from the iPad, or anywhere, without touching git

January 12, 2014

UPDATE April 2015: Hereā€™s a much, much better way to do it: How to easily post to your Jekyll blog from your iPhone or iPad

First of all: if anyone subscribes to my RSS feed, I apologize for flooding it today with test posts as I futzed with this.

Today I wrote my first Editorial workflow and also my first Python script. Fun day!

Editorial is a power user text editor for the iPad. Before today I never really used it but I think Iā€™ll use it more now.

This project was inspired by a recent episode of the tech podcast Core Intuition. They were discussing blogging software. One of the hosts, Daniel Jalkutt, makes the Mac blogging app MarsEdit, and theyā€™ve both been around long enough to have really interesting thoughts on the space. MarsEdit allows bloggers to post to most of the major blog systems but not static blogs like Jekyll (what I use for this blog), Pelican, Octopress (which is built on Jekyll), Roots, or any number of other, similar projects.

Which is understandable because those donā€™t really expose an API for it to integrate with. But what if they did? (haha)

Iā€™m going to step away from discussing the broader context of static blog tools and focus specifically on Jekyll blogs deployed to GitHub pages, which is the bulk of my experience.

Hereā€™s how I generally write and publish posts, before today:

  • clone or pull the git repo
  • use poole to run poole draft "Title Of Post", which reates a new markdown file for me
  • use vim to write the post
  • serve the blog locally to make sure it looks right
  • when Iā€™m done, run poole publish _drafts/title_of_post.md, which moves the file into the _posts directly and inserts the current time into the configuration at the top
  • use git to commit and push the post up to GitHub, which will re-compile the site using jekyll build

I can do this easily on the computer and itā€™s pretty nice. I can do this on the iPad using Prompt to SSH into a DigitalOcean server and itā€™s okay. I can do the same on my iPod Touch and itā€™s kind of awkward and overly small.

There are a few major advantages to having a static blog. To me, the main boon is the lack of a database. Itā€™s definitely an advantage that thereā€™s no server-side computation going on ā€“ the HTML is available to serve immediately after being requested ā€“ but maybe itā€™s a disadvantage that thereā€™s no server-side API. In theory, a static blog API could do several things:

  • create posts
  • update posts
  • delete posts
  • render posts as JSON

What Iā€™ve created today only does the first thing. Itā€™s pretty simple. It would need to do all of those, and probably much more Iā€™m not currently imagining, to integrate with something like MarsEdit. But itā€™s enough to do something pretty cool: now I can write posts in Editorial on my iPad (Iā€™m writing this one that way) which has a browser built-in for research and an embarrassment of possibilities for extensibility because itā€™s somehow snuck the Python scripting language into Appleā€™s walled garden.

Hereā€™s a demo video (sorry itā€™s so long):

Hereā€™s the source of the Rails project that serves as a Jekyll API: maxjacobson/jekyll_api. Hereā€™s the Editorial workflow which will currently only work for me, because Iā€™m not sure the best way to share the source for something like this thatā€™s not just a script, but a chain of macros and scripts.

Actually, hereā€™s the most-relevant chunk of the workflow, my first python script, somewhat out of context:

#coding: utf-8
import workflow, requests

script_params = workflow.get_parameters()

auth_token = script_params["auth_token"]
blog_id = script_params["blog_id"]

post_slug = workflow.get_variable("slug")
post_text = workflow.get_variable("text")

params = {
  'auth_token': auth_token,
  'post[slug]': post_slug,
  'post[text]': post_text
}

response = requests.post("http://pairwith.maxjacobson.net:3000/blogs/" + blog_id + "/publish", params=params)

if response.status_code == 200:
  workflow.set_output("Successfully posted")
else:
  workflow.set_output("Something went wrong")

As I mention in the video, in theory I can deploy this app to an actual production environment (note the weird URL if you like) and allow other people to use it. I probably wonā€™t.

Alright now Iā€™m going to press ā€œPostā€. Iā€™m kind of nervous! Wish me lu