Upload to imgur from applescript

A little scrip

on run {input, parameters}
  tell application "Finder"
    -- convert file paths to posix
    set imageList to {}
    set linkList to {}
    repeat with i from 1 to (count input)
      set end of imageList to POSIX path of (item i of input as alias)
      set file_name to name of (item i of input as alias)
    end repeat

    -- no images selected
    if (count imageList) is 0 then
      display dialog "No image files selected" with title "Imgur uploader" buttons {"Quit"} default button "Quit"

      display notification "Preparing to upload " & (count imageList) & " images" with title "Uploaded Started"

      repeat with i from 1 to (count imageList)
        set apiKey to "26ff5c40cbedf50e7f81124ab473c1cc"
        set curlCommand to "curl -F \"key=" & apiKey & "\" -F \"image=@" & item i of imageList & "\" http://api.imgur.com/2/upload"
        set answer to do shell script curlCommand
        set atid to AppleScript's text item delimiters
        set AppleScript's text item delimiters to "<original>"
        set link to text item 2 of answer
        set AppleScript's text item delimiters to "<"
        set link to text item 1 of link
        set AppleScript's text item delimiters to atid
        set end of linkList to link
      end repeat
    end if
  end tell

  set links to joinList(linkList, " ")
  set the clipboard to links

  display notification "Successfully Uploaded Screenshot and Copied the URL to the Clipboard" with title "Uploaded Finished" sound name "Purr"

end run

to joinList(aList, delimiter)
  set retVal to ""
  set prevDelimiter to AppleScript's text item delimiters
  set AppleScript's text item delimiters to delimiter
  set retVal to aList as string
  set AppleScript's text item delimiters to prevDelimiter
  return retVal
end joinList

Versioning in the URL?

After showing my friend Guille the API I was working on he said:

guillec: whats with the versioning in the URL?
guillec: dont talk to me
guillec: ever again

I love hypermedia APIs, so why do I have a version in the URL? Aren't we supposed to version resources and not the entire API?

Well, I have my reasons.

But first, I do agree. This feels like a smell. If I see an API that does this I normally would make snarky comments. My gut tells me it is the result of poorly thought out API, a dated approach, or some other anti-pattern.

So, why do I put the version in the URL?

Because: It is hard to do things right.

So a little back story. A few constraints I have to work within. I am working at a little company on a small system that is being integrated by partners, often contractors or junior developers who just want to check it off a list and move on. So they want to crank out something with the minimum amount of work necessary.

Lets think about how they are going to use the API.

  • They will cut corners
  • They will hard code URIs
  • They will not set the headers properly
  • They will assume the media type
  • They will complain if the API is hard to use
  • They will complain if something breaks (no matter how many corners they have cut)

That's all compleatly fair. I want to make their job easier, and I don't want them to push back on integrating (lest an important deal fall through).

So now I think about what my goals are.

  • I want to make it easy to integrate
  • I would prefer if the code behind it was clean and maintainable
  • I want my API to appear to be well thought out and properly designed (For example if Guille was evaluating my service, I want him to be impressed)
  • I need to be able to change the API as our product evolves

A few constraints

I have been told by a few very smart and accomplished developers that the solution is to control the API and the client. But given my "little company/small system" I don't have the cycles to create and maintain Ruby, Node, PHP, Java and ColdFusion libraries -- and that's just the clients we have now. So it needs to be a common denominator, which is the HTTP API.

The same goes for media types. I know I can correctly report the media type I support throught content negotiation, but by the time I get that far I've lost most developers. Also to save time and money I am only supporting one media type. Just dont' have time to build out the others. Much less keep them all up to date. So effectively forcing a Hypermedia api into single media type. Not really ideal, but pragmatically what can be supported for now.

The compromise

Be as progressive as I can in the Hypermedia API. Pick a solid media type and stick to it. The point is to not reinvent new problems. HAL, JSON+collection, JSON API -- just pick one that works for you.

Expect the consumer to cut corners. Plan for hard coded URLs. Resource IDs and URLs to be stored in their system. Improper use of headers such as content type, accepts, even authentication.

My Approach

  • Handle the hardcoded URLs by versioning via the API
  • Force the content type to be JSON
  • Make it easy to Authorize
    • Authorization header (the documented and recomended way)
    • Session (logged in users can view the API in their browser)
    • Query Param (the It Just Works™ method, this way I email a url that will work for debugging or other support, but I don't publish this in the docs)

I hope I can find a middle ground that will make the 9-to-5er happy as well as appease the zealous hypermedia advocate.

Things would be differnent with different constraints. The API is not stable, things will need to change. I don't need many different representations of the resources. The clients consuming it will be the simpliset thing that works.

Are you using Hypermedia APIs in production and dealing with customers, zealots and unreasonable free loaders? I am curious how you handle this. Drop me a line or leave a comment.

Ignoring people on IRC

Want to block, filter and ignore people (and bots) on IRC? Here's what you do.

How to make #rspec on freenode usable.

/ignore GitHub*!*@*

Command Format:

/ignore nickname!username@userhost


Match nicknames starting with "csex":

/ignore csex*!*@*

Match the username "csexton":

/ignore *!csexton@*

Match everyone on a .info domain:

/ignore *!*@*.info

Ruby Retrocession Retrospective

Photo Credit: @elight

This last weekend Arlington Ruby hosted our semi regular unconference known as Ruby Retrocession. Or RetroRuby when you have a character limit.

As is normal in an Open Space event, we held a retrospective at the end. I thought it would be good to write some of that up. To help me remember, or perhaps help someone else who is participating or organizing something like this.

Moar Coffee

We wanted to supply coffee, lunch and an after party/social event. We did this...mostly.

We had good coffee in the morning, but failed to get any for the afternoon. The morning Coffee came from Northside Social and was pretty tasty, we just got too caught up in running things that we should have delegated to someone to run and get things at the right time.

This brings up a good point. I think in the future I would find one volunteer who's sole responsibility is coordinating food. They can delegate, but it's their job to make sure it gets done. That person shouldn't be doing anything else other than attending. They would also have a means to pay.

I had a blast picking up the tab for 100 of my closest friends at El Pollo Rico, but it would have been smarter to have someone else there to swipe the card.

Explain better up front

The second most valuable feedback we had was to explain what the expectations are. With the format we had it is on the participants to get value out of the events. We needed to explain that better. A few points:

  • Rule of two feet: If you are in a session and it is not helpful -- get up and go. It's not insulting to anyone for you to leave. It's better to get up and wonder out than pull out the computer and reading email.
  • Coordinate smaller groups. If there something in particular you are interested in, or need help on -- ask folks. See if you can get a little side group that can hack on something or discuss a narrower topic. This needed to be encouraged more. Perhaps some dedicated space for just this. Also a time for people to announce what they are working on or need help with.

Structure on the Newbie Track is a good thing

The newbie track had much more structure, and it turns out that is a good thing. I had almost nothing to do with it (aside from being volentold to help with one of the sessions) and it was all Allison and Kalimar. They really did a fantastic job. All the feedback we got from the beginners was glowing. The only comments were that an in-between complete beginner (no clue about the command line) and beginner at ruby (programed before just new to the ruby eco system) would have been helpful for some folks.


To all the volunteers and sponsors we had this year, you guys rock. Thanks. Without your help we couldn't have pulled this off.

Use pow to serve your static html

I love Pow, and use it with Rails and Sinatra all the time. However I also maintain a few sites that are just plain old HTML files. Or generate plan old HTML files like Jekyll and Middleman.

I thought it would be nice to do local development on these using Pow, since it is on my machine already.

Turns out it is really easy.

  1. In .pow make a directory with the project name
  2. Create a symlink called public to the static content

Say I wanted to view this site locally at http://codeography.dev/ this is what I would do:

cd ~/.pow
mkdir codeography
ln -s ~/src/codeography/_site codeography/public

And now Pow will serve up the site locally for me at http://codeography.dev. Awesome.

Wait, wut?

How does this work?

Pow will serve static content from the public directory under your project. Normally you link to the top level directory and it happens to have a public folder contained -- per the Rails convention. Well, I just tricked pow.

See, pow just looks in ~/.pow for projects it can serve. It know to follow symlinks.

Instead of this:

~/.pow/(myproj --> ~/src/myproj)/public/index.html`

I just put make a folder and stick a public symlink:

~/.pow/myproj/(public --> ~/src/myproj)/index.html`

Pretty handy little trick. And this way don't have to remember which port Jekyll or Middleman uses.

You should remember this won't re-generate your site if you are using tools like Jekyll and Middleman, you still have to run that manually.