Folder creation bug in Adobe ExtendScript ToolKit

This is the story of an Adobe ExtendScript ToolKit bug. A bug that took two hours to figure out. It’s unlikely that anyone else will ever run into the same bug. On the off chance someone does, maybe they’ll find this post and it’ll save them some time.

I’m building a JavaScript scaffold for Photoshop automation scripts. Something to help build them with less overhead. It will provide config file parsing, logging and simplified file creation. One aspect is that it will automatically make folders when necessary. This is where the bug popped up.

The scaffold logs errors when it can’t complete a request. To test the error messages when it can’t create a directory, I tried to make a folder at the root of the file system. Since that’s not a place normal users have the ability to write to, it should have failed. It didn’t.

Running:

var testFolder = new Folder("/ExtendScriptTestDir");
$.writeln("Create should return false. Got: " + testFolder.create());
$.writeln("Exists should return false. Got: " + testFolder.exists);

Produces this the first time:

// Create should return false. Got: true
// Exists should return false. Got: false

And this on subsequent runs:

// Create should return false. Got: true
// Exists should return false. Got: true

This makes no sense for three reasons:

  1. There is no way the script should be able make directories at the top of the file system.
  2. Checking for it should have given the same result the first time through as the following times.
  3. When actually looking for it, there is no directory where the script reports there is.

After a lot of fruitless command line exploration and googling, I tracked down what’s going on. Instead of making the directory at the top of the file tree (e.g. /ExtendScriptTestDir), it’s created in the /Volumes directory (e.g. /Volumes/ExtendScriptTestDir). Then, for some odd reason, the exists check doesn’t see it immediately but picks it up after a short period of time.

I can’t explain the behavior, but at least it’s consistent. More importantly, as log as there is at least one sub directory thing behave as expected:

var testFolder = new Folder("/ExtendScriptTestDirAlt/subdir");
$.writeln("Create should return false. Got: " + testFolder.create());
$.writeln("Exists should return false. Got: " + testFolder.exists);

// Always produces:
// Create should return false. Got: false
// Exists should return false. Got: false

The moral of the story: don’t try to make a folder at the root of the file system.


Note: This was all using a Mac running 10.9.2 and the CS6 version of Photoshop and ExtendScript Toolkit. I started to open a bug with adobe, but they don’t list ExtendScript Toolkit in their drop down of apps and the login page timed out. I’m putting a note in the scaffold’s docs about the bug. Maybe they’ll see it there.

 

Backyard wildlife - Sandhill Cranes

Snowy Egrets and Great Blue Heros frequent my backyard. Now, a new type of avian guest has appeared: Sandhill Cranes. Since their heads are Crimson and White, I’d like to think they are Bama fans.

Roll Tide, birds. Roll Tide.

 

What I'm Running - Open Apps - March 1st, 2014

Macs don’t crash much. They also handle themselves well when a ton of apps are running. This was brought home when a co-worker saw me do a Cmd+Tab app switch. Here’s a shrunken version of my active app icon bar that popped up.

From left to right:

nvAlt My grimoire and external brain
Sublime Text 2 The go-to text editor
Photoshop CS6 The standard (and last version available without a monthly subscription)
ExtendScript Toolkit For making Photoshop JavaScriptsex
Oxygen XML Editor Totally worth the expensive cost XML tool
iTerm2 The better terminal for Mac
CodeRunner The handy code snippet tester
Chrome Google’s Netscape Navigator
Lightroom 5 Main photo workflow tool
Photo Mechanic 5 First step on the photo workflow path
BBEdit Backup text editor
Soulver The reimagined calculator
SQLite Database Browser Way better than the command line (beware sourceforge adons)
Dash The pain reliever for looking at documentation
Finder Apple’s happy app
Firefox Mozilla’s Netscape Navigator
Safari Apple’s Netscape Navigator
DayOne Daily note taking
Preview Non-Adobe PDF viewer
1Password Bringer of sanity and security
ScreenFlow Maker of screencasts
Better Rename 9 Bulk renaming of files without the command line
Spotify The replacement for my 20,000 song mp3 library
System Preferences Machine tweaker
TextExpander Auto-Expander for text snippets
Console The tool to peak behind the machine curtain a little
Terminal For long running items that would be distracting in iTerm2
MAMP Pro The Linux, Apache, MySQL, PHP stack, but for Mac
Stickies Post-it notes for the virtual desktop
OmniFocus A fighting chance against the ever-growing TODO list
Activity Monitor Checking performance when this many apps are running
Contacts Names, numbers, addresses, etc…
Dictionary For those times when auto-correct fails (or you don’t trust it)
Transmit FTP, SFTP, etc…
Quicksilver Most used app - Don’t let the position in the list fool you
Calendar Days, Weeks, Months… Time flies.
iTunes Relegated to a Podcast tool in the post-Spotify world

Of course, this post wouldn’t be complete without also showing the apps running in the menu bar:

Again, from left to right:

Hazel “Automated Organization for your Mac”
LiveReload Reload browsers automatically after editing files (no more constant app switching)
Quicksilver Menubar access to the go-to app launcher
ScreenFlow Not currently recording
DayOne Unnecessary, but doesn’t appear to be able to be disabled
TextExpander Menu access to snippets when you forget the keyboard way
Dropzone Drag things here and make them go elsewhere
Divvy Hotkeys for shuffling and positioning windows
Dropbox File syncing done right (referral link)
Monosnap Quick and easy screengrab posting
Alfred Alternate app launcher and automation tool (current overshadowed by Quicksilver)
i1 Profiler Shame on X-rite for not updated software even though the hardware is discontinued
Flycut Continuing the search for a multi-clipboard tool that works with my brain
Caffeine Single click to keep monitor from going to sleep during presentations and processing
Houdini Tries to keep the desktop clean by hiding apps after a time
GeekTool Sends output from scripts to the desktop background
Symantec AntiVirus Must have for the corporate network
1Password Menu bar access to password sanity and security
VMware Fusion To run Linux and Windows machines inside a Mac
Messages Another icon that won’t die
MenuMeters The next two icons are: Disk Activity and CPU usage
TimeMachine Only use this if you care about keeping your files and data
Bluetooth status I wonder who came up with the name “Bluetooth”
WiFi status Signal strength at a glance
Volume Don’t really need this since I just use the hotkeys
Batter level Figure out technology to make this irrelevant and make a zillion dollars
Keyboard To look up key combos to make letters with umlauts
Date & Time Day of the week I can remember - Day of the month I need help with
Spotlight Haven’t been able to kill this one
Notifications Haven’t been able to kill this one either

That makes 62 apps (not counting all the behind the scenes stuff). Pretty good considering I haven’t rebooted the machine in a month. The negative aspect is that with all this stuff open, I really put off any software updates that require restarting. Based on this XKCD strip, I’m not alone.

 

Clearing the Adobe ExtendScript ToolKit Console Window

While creating a new JavaScript to automate some day-job Photoshop work, I gave Adobe’s ExtendScript Toolkit a spin. It’s designed specifically for building scripts for Adobe products and it’s pretty nice. Especially the JavaScript Console window that makes it nice and easy to see debugging messages.

ExtendScript ToolKit windows.

By default, this window doesn’t clear itself before each run which degrades a lot of its value. It can be cleared manually before the start of each test run, but that’s a pain. The good news is there’s a way to do it with code. If the script is run from ExtendScript Toolkit itself, this snippet of code will do it:

app.clc();

Unfortunately, that same code causes Photoshop to choke which defeats the purpose. The first solution I came up with to get around this is to check to make sure the script was being run in ExtendScript ToolKit before making the call.

if (app.name === "ExtendScript Toolkit") { 
  app.clc(); 
}

This clears the console when run in ExtendScript ToolKit but not Photoshop. Useful for early stage development but resulting in having to fall back to manually clearing after each Photoshop run. After some digging through the “Interapplication Communication with Scripts” chapter of the “JavaScript Tools Guide CS6” docs, I came up with this:

if (app.name === "ExtendScript Toolkit") { 
  app.clc(); 
}
else {
  var estApp= BridgeTalk.getSpecifier("estoolkit");
  if(estApp) {
    var bt = new BridgeTalk;
    bt.target = estApp;
    bt.body = "app.clc()";
    bt.send();
  }
}

This code checks to see if the script is being run from ExtendScript Toolkit. If so, it clears the console natively. If the script is being run in another app (e.g. Photoshop), it attempts to send the clear message to ExtendScript Toolkit.

An alternate way to clear the console would be to use the Cmd+Shift+C hot-key combo, but I’d much rather let the script do it for me.

 

As a matter of fact, I do own a laser gun

Laser + Trigger = Laser Gun

Though I’ve had it for a while, it only just occurred to me that I own a laser gun. Ten year old me would be so very impressed. At least, until I mentioned it doesn’t vaporize targets. Just measures their surface temperature.

Still: I OWN A LASER GUN!

 

BCS Picks at Work

Folks are work are making picks for who will win the BCS Championship game. Guess which one is mine.

 

An Event Apart 2013 (San Francisco) Links

The San Francisco instance of the An Event Apart 2013 conference is in the books. An overwhelming amount of great, up-to-the-minute information on web design and development was shared. This post contains the links I compiled for future reference. Since an oft repeated theme of the conference is that we should all make an effort to share knowledge, I’m posting them here for posterities sake.

[Note to attendees: Yes. About 95% of the conference was focused on responsive design. No. This site is not responsive. Someday, I’ll fix that. But if I waited until that happened, I’d never get anything posted.]

Speakers

This collection was built by running a script over the URLs I bookmarked. The source of the HTML returned was parsed. The text form the HTML <title> tag was used for the link. The content from the <meta name=”description”> tag was used for the follow up description. Lots of sites didn’t have the meta tag which is why several links don’t have follow up descriptions. It’s interesting to me to see how sites are treating those two tags.

 

Jekyll (and GitHub Pages) Liquid Date Formatting Examples

Summary (tl;dr)

These examples provide tested code snippets for displaying several date formats on a Jekyll site1. They should also work on GitHub Pages, Shopify or anything else that uses Liquid. An alternate title for this post should be:

Everything you wanted to know about formatting dates in Jekyll but were afraid to ask.


Overview

Jekyll2 (the simple, blog aware, static website generator) uses Shopify’s Liquid Template Engine3. Displaying dates is done using the “{{ page.date }}” supplied Liquid tag4. With no other alteration, the dates produced look something like:

2013-11-29 00:00:00 -0500

If there are designs that use that format, they are few and far between. Creating friendlier looking dates is done by applying Liquid’s “date:” filter. For example, the tag/filter combination:

{{ page.date | date: '%B %d, %Y' }}

produces more reader friendly dates like:

November 29, 2013

Much better, but depending on the date, subtle design issues show up. For example, during the first nine days of each month “leading zeros” crop up (e.g. “August 03, 2013” instead of “August 3, 2013”). Other gotchas with the basic Liquid filters include:

  1. Adding a period behind the abbreviated month names has to be adjusted to handle May. For example, “Aug. 16, 2013” is fine. “May. 16, 2013” is not.
  2. September is generally abbreviated “Sept.” instead of Liquid’s default “Sep.”
  3. To comply with the AP style guide the months April, May, June and July should not be abbreviated. Similar alterations are necessary to meet with the guidelines from the Chicago Manual of Style.

Most designs go with the available defaults. Either using a format that doesn’t have these issues or, more frequently, letting the details slip. The information still gets across and every web site has a punch list of potential modifications that stretches to the horizon. So, I understand putting off finding a solution, but not having proper date formatting has always bugged me. While solving the issue for myself5, I decided to put together this post as a public reference as well. I don’t yet have the Ruby chops to contribute directly to Jekyll, but I can provide this reference to give back a little at least.

This set of Liquid date filters solves the issues listed above and explores a few other formatting options. Each one provides a solution for a specific display format and is provided with four output examples for the following date: 1) May 3, 2013, 2) July 4, 2013, 3) September 23, 2013 and 4) November 26, 2013. These examples demonstrate if/how the various formatting issues are handled. After the examples, a few snippets of code for individual elements are provided. With these samples, just about any date format desired should be within easy reach.


Liquid Date Formatting Examples for Jekyll

  • ISO 8601 Date5

    {{ page.date | date: "%Y-%m-%d" }}
    

    Output Example 1: 2013-05-03
    Output Example 2: 2013-07-04
    Output Example 3: 2013-09-23
    Output Example 4: 2013-11-26

  • U.S. Numeric Style with Four Digit Years (With leading zeros.)

    {{ page.date | date: "%m/%d/%Y" }}
    

    Output Example 1: 05/03/2013
    Output Example 2: 07/04/2013
    Output Example 3: 09/23/2013
    Output Example 4: 11/26/2013

  • U.S. Numeric Style with Four Digit Years (Leading zeros removed.)

    {{ page.date | date: "%-m/%-d/%Y" }}
    

    Output Example 1: 5/3/2013
    Output Example 2: 7/4/2013
    Output Example 3: 9/23/2013
    Output Example 4: 11/26/2013

  • U.S. Numeric Style with Two Digit Year (Leading zeros removed.)

    {{ page.date | date: "%-m/%-d/%y"}}
    

    Output Example 1: 5/3/13
    Output Example 2: 7/4/13
    Output Example 3: 9/23/13
    Output Example 4: 11/26/13

  • Outside U.S. Style with Full Month Name (Leading zeros removed.)

    {{ page.date | date: "%-d %B %Y"}}
    

    Output Example 1: 3 May 2013
    Output Example 2: 4 July 2013
    Output Example 3: 23 September 2013
    Output Example 4: 26 November 2013

  • Outside U.S. Style with Non-English Full Month Name (Leading zeros removed.)

    This example uses the German names for months. Any other language can be used by simply substituting the proper names for each month.

    <!-- Whitespace added for readability -->
    {% assign m = page.date | date: "%-m" %}
    {{ page.date | date: "%-d" }}
    {% case m %}
      {% when '1' %}Januar
      {% when '2' %}Februar
      {% when '3' %}M&auml;rz
      {% when '4' %}April
      {% when '5' %}Mai
      {% when '6' %}Juni
      {% when '7' %}Juli
      {% when '8' %}August
      {% when '9' %}September
      {% when '10' %}Oktober
      {% when '11' %}November
      {% when '12' %}Dezember
    {% endcase %}
    {{ page.date | date: "%Y" }}
    

    Output Example 1: 3 Mai 2013
    Output Example 2: 4 Juli 2013
    Output Example 3: 23 September 2013
    Output Example 4: 26 November 2013

  • U.S. Style with Full Month Name (Leading zeros removed.)

    {{ page.date | date: "%B %-d, %Y" }}
    

    Output Example 1: May 3, 2013
    Output Example 2: July 4, 2013
    Output Example 3: September 23, 2013
    Output Example 4: November 26, 2013

  • U.S. Style with Full Month Names and Ordinalized Days (Leading zeros removed.)

    <!-- Whitespace added for readability -->
    {% assign d = page.date | date: "%-d"  %}
    {{ page.date | date: "%B" }} 
    {% case d %}
      {% when '1' or '21' or '31' %}{{ d }}st
      {% when '2' or '22' %}{{ d }}nd
      {% when '3' or '23' %}{{ d }}rd
      {% else %}{{ d }}th
      {% endcase %}, 
    {{ page.date | date: "%Y" }}
    

    Output Example 1: May 3rd, 2013
    Output Example 2: July 4th, 2013
    Output Example 3: September 23rd, 2013
    Output Example 4: November 26th, 2013

  • U.S. Style with AP Month Abbreviations and Ordinalized Days (Leading zeros removed.)

    <!-- Whitespace added for readability -->
    {% assign d = page.date | date: "%-d" %} 
    {% assign m = page.date | date: "%B" %} 
    
    {% case m %}
      {% when 'April' or 'May' or 'June' or 'July' %}{{ m }}
      {% when 'September' %}Sept.
      {% else %}{{ page.date | date: "%b" }}.
      {% endcase %}
    {% case d %}
      {% when '1' or '21' or '31' %}{{ d }}st
      {% when '2' or '22' %}{{ d }}nd
      {% when '3' or '23' %}{{ d }}rd
      {% else %}{{ d }}th
      {% endcase %}, 
    {{ page.date | date: "%Y" }}
    

    Output Example 1: May 3rd, 2013
    Output Example 2: July 4th, 2013
    Output Example 3: Sept. 23rd, 2013
    Output Example 4: Nov. 26th, 2013

  • U.S. Style Full Day and Full Month Names (Leading zeros removed.)

    {{ page.date | date: "%A, %B %-d, %Y" }}
    

    Output Example 1: Friday, May 3, 2013
    Output Example 2: Thursday, July 4, 2013
    Output Example 3: Monday, September 23, 2013
    Output Example 4: Tuesday, November 26, 2013

  • Chicago Manual of Style Day Abbreviations and U.S. Style Date (With “Thurs.” and “Tues.”)

    <!-- Whitespace added for readability -->
    {% assign dy = page.date | date: "%a" %}
    {% case dy %}
      {% when "Tue" %}Tues
      {% when "Thu" %}Thurs
      {% else %}{{ dy }}
      {% endcase %}. 
    ~
    {{ page.date | date: "%B" }} 
    {{ page.date | date: "%-d" }}, 
    {{ page.date | date: "%Y" }}
    

    Output Example 1: Fri. ~ May 3, 2013
    Output Example 2: Thurs. ~ July 4, 2013
    Output Example 3: Mon. ~ September 23, 2013
    Output Example 4: Tues. ~ November 26, 2013

Individual Component Snippets for Liquid Date Formatting

These individual snippets are for a few of the tricker formatting filters. Some that weren’t used in the examples above. For those interested in the approach, the hack I’m using to remove leading zeros is to add “0” to the string. This turns the string into an integer. When the integer is rendered back as a string the leading zero disappears. Hurray for dynamic typing.

  • Numeric Month with Leading Zeros Removed

    {{ page.date | date: "%-m" }}
    
  • Numeric Day with Leading Zeros Removed

    {{ page.date | date: "%-d" }}
    
  • Ordinalized Numeric Day with Leading Zeros Removed

    {% assign d = page.date | date: "%-d" %}
    {% case d %}
      {% when '1' or '21' or '31' %}{{ d }}st
      {% when '2' or '22' %}{{ d }}nd
      {% when '3' or '23' %}{{ d }}rd
      {% else %}{{ d }}th
      {% endcase %}
    
  • AP Style Month Abbreviations

    {% assign m = page.date | date: "%B" %}
    {% case m %}
      {% when 'April' or 'May' or 'June' or 'July' %}{{ m }}
      {% when 'September' %}Sept.
      {% else %}{{ page.date | date: "%b" }}.
      {% endcase %}
    

    (Produces: “Jan.”, “Feb.”, “Mar.”, “April”, “May”, “June”, “July”, “Aug.”, “Sept.”, “Oct.”, “Nov.”, “Dec.”)

  • Chicago Manual of Style Day Abbreviations

    {% assign dy = page.date | date: "%a" %}
    {% case dy %}
      {% when "Tue" %}Tues
      {% when "Thu" %}Thurs
      {% else %}{{ dy }}
      {% endcase %}.
    

    (Produces: “Sun.”, “Mon.”, “Tues.”, “Wed.”, “Thurs.”, “Fri.”, “Sat.”)

  • Chicago Manual of Style Month Abbreviations

    {% assign m = page.date | date: "%B" %}
    {% case m %}
      {% when 'May' or June' or 'July' %}{{ m }}
      {% when 'September' %}Sept.
      {% else %}{{ page.date | date: "%b" }}.
      {% endcase %}
    

    (Produces: “Jan.”, “Feb.”, “Mar.”, “Apr.”, “May”, “June”, “July”, “Aug.”, “Sept.”, “Oct.”, “Nov.”, “Dec.”)

  • Non-English Month Names

    {% assign m = page.date | date: "%-m" %}
    {% case m %}
      {% when '1' %}Januar
      {% when '2' %}Februar
      {% when '3' %}M&auml;rz
      {% when '4' %}April
      {% when '5' %}Mai
      {% when '6' %}Juni
      {% when '7' %}Juli
      {% when '8' %}August
      {% when '9' %}September
      {% when '10' %}Oktober
      {% when '11' %}November
      {% when '12' %}Dezember
    {% endcase %}
    

    (Produces: “Januar”, “Febuar”, “März”, “April”, “Mai”, “Juni”, “Juli”, “August”, “September”, “Oktober”, “November”, “Dezember”)

With that, you should be in pretty good shape. If you can’t directly create what you need from the above samples or snippets you should at least be able to use a similar approach to piece together what you need.


Notes on the Examples

  1. The hour, minute and second parts of the full date/time stamp aren’t being used because Jekyll tends to zero them out.
  2. In some of the examples, the code is split to multiple lines to help readability. If it’s a natural break point where white space already exists, this won’t effect HTML rendering. In some cases, it will introduce unwanted white space. Simply move everything back to one line to create the desired presentation.
  3. Since the original version of this post went up, updates have been made to use the cleaner version of {{ page.date | date: "%-m" }} instead of {{ page.date | date: "%m" | plus:'0' }} and {{ page.date | date: "%-d" }} instead of {{ page.date | date: "%d" | plus:'0' }} to remove leading zeros.
  4. An additional update was made to add an example for non-English month names.

Footnotes

  1. These examples were create on a Mac running OS X 10.8.5 with: Ruby 2.0.0p247 - Jekyll 1.2.1 - liquid 2.5.2. Your mileage may vary.
  2. The main Jekyll website provides a great overview of the software. You can learn even more with a visit to the Jekyll’s GitHub Repo.
  3. Shopify’s Liquid template engine. “A small and fast template language which is quick to learn but very powerful for full customization.”
  4. The Liquid Date Filter offers some basic formatting elements and is the basis of these code snippets. Note that in some cases “post.date” might be required instead of “page.date”.
  5. Observant readers will notice that as of the time this post went live, my design still uses a date format with leading zeros. I have the solution, it just hasn’t been implement yet. It’ll go in with the next set of design changes.
  6. ISO 8601 - “Data elements and interchange formats - Information interchange - Representation of dates and times is an international standard covering the exchange of date and time-related data.” A perfect example of how Time really is wibbly wobbly timey wimey… stuff.