Saturday, October 20, 2012

Add URL to Pinboard with Alfred

Just wrote a tiny little ruby script to add URLs to Pinboard using Alfred.

Get it on github.

Chaptrs Photo Browser

Still waiting for my app to enter the Mac App Store review process.

Come on Apple, the app automatically arranges people's photos. People will love it! Review it already :)

http://chaptrs.com

Quicksilver vs Alfred

So if you're like me, you're a long-time Quicksilver user who think that its noun-verb-noun model rules the world. In some cases, the first noun can be an action/script, so practically, the model expands into a verb or verb-noun model. And with the ability to assign keyboard shortcuts to any manifestation of the model and the ability to assign gestures to any key combination, the possibilities become endless.


Alfred is a relatively new player in this arena of "launcher apps". Its been in my radar for awhile, but really, how can I possibly move away from the awesomeness of the Quicksilver model. One day however, I find myself purchasing its Powerpack features (I really have no idea why), so now I'm forced to give it a good spin (money has been invested!).


Having used Alfred for a good two weeks, I must say, it feels faster than Quicksilver and I'm liking its different approach. Using Alfred is a little like using Mozilla's Ubiquity, albeit not as elaborate or powerful. The idea is however similar: you get one text box to enter your query and Alfred acts on that query depending on the syntax. So you could make Alfred work like the Safari Omnibar and type "g [query]" to search Google with [query]. And you can add query shortcuts for all your frequently-used sites: Wikipedia, Google Images, imdb, Rotten Tomatoes, etc. I've even added a shortcut to Google's I'm Feeling Lucky so typing "lq [site]" will take me directly to the site.


More than that these Omnibar-like shortcuts, Alfred is extensible just like Quicksilver. You can hook-up any old Apple Script, Shell Script, etc to Alfred and have it run the script [keyword] with [query] whenever you type like so "[keyword] [query]". There's already a tonne of extensions written for Alfred, all ready for your free shopping. You can also use scripts as Alfred actions so you can run them on selected files / folders, instead of a query string.

So in summary, I think that whatever you want / can do in Quicksilver can be done just as simply in Alfred. The difference is subtle and subjective. Alfred may look aesthetically better for some people, but that could just be the new leather smell thats clouding your judgement. We can't know for sure without a proper A/B testing on an unbiased population sample. The real difference is in their execution models. In most cases, while Quicksilver  makes you think of a noun first, Alfred makes you think of a verb first. Which model is better really depends on the user, preference / comfort, and a fair comparison after prolonged use. For me, I'm sticking with Alfred or at least until the new leather smell wears off.

Friday, October 19, 2012

Using the ANTLR C++ Grammar for C Target

From the ANTLR site:
ANTLR is a language tool that provides a framework constructing recognisers, interpreters, compilers, and translators from grammatical descriptions containing actions in a variety of target languages.
Unfortunately, the grammars publicly listed at the site don't normally come with an easy to follow set of instructions. This post will hopefully provide such help for the C++ grammar for C Target.

First, you will need to install the ANTLR C runtime library, only then can you use the ANTLR C++ grammar files.

The ANTLR C Runtime Library

An alternate set of instructions can be found at the ANTLR site.
  1. Download the library  (v3.4 at the time of this post)
    wget http://www.antlr.org/download/C/libantlr3c-3.4.tar.gz
  2. Untar:
    tar -zxvf libantlr3c-3.4.tar.gz
  3. Configure:
    ./configure --enable-64bit
    (Note that you need to use the --enable-64bit flag if your machine is capable, otherwise you'll run into problems later on)
  4. Compile:
    make
  5. Fix any compilation errors. For example, if you encountered the error "/usr/include/gnu/stubs.h:7:27: error: gnu/stubs-32.h: No such file or directory", then a quick Google search would lead to this stack overflow question, which will suggest installing glibc-devel.i386 via yum if your machine is running CentOS 5.8
  6. Repeat steps 3-5 until there are no compilation errors
  7. Install:
    sudo make install
  8. Export the library path:
    export LD_LIBRARY_PATH=/usr/local/lib/:$LD_LIBRARY_PATH
    
    

The ANTLR C++ Grammar

  1. Download the ANTLR java library. You will need this to process the .g grammar file.
    wget http://www.antlr.org/download/antlr-3.4-complete.jar
  2. Download the grammar file
    wget http://www.antlr.org/grammar/1295920686207/antlr3.2_cpp_parser4.1.0.zip
  3. Extract files:
    unzip antlr3.2_cpp_parser4.1.0.zip
  4. Process the .g grammar file:
    • java -jar /path/to/antlr-3.4-complete.jar CPP_grammar_.g
    • Rename the resulting code files to C++
      mv CPP_grammar_Lexer.c CPP_grammar_Lexer.cpp
      mv CPP_grammar_Parser.c CPP_grammar_Parser.cpp
  5. Edit CPP_grammar_Parser.cpp and comment out  line 29639 (there is an extra ');' present) that will prohibit the file from compiling
  6. Edit cpp_full_prog.cpp at line 239 and change
    input = antlr3AsciiFileStreamNew(fName);
    with:
    input = antlr3FileStreamNew(fName, 4);
    The integer 4 refers to the constant ANTLR3_ENC_8BIT found in antlr3defs.h (in the C runtime library installed in the first section)
  7. Compile and link:
    g++ -o cpp_full_prog cpp_full_prog.cpp CPP_grammar_Lexer.cpp CPP_grammar_Parser.cpp Helper/*.cpp -I/usr/local/include/ -I./antlr_include/ -L/usr/local/lib/ -lantlr3c
For more information on how to massage information out of the lexer, tokens, or parser, you can refer to the C runtime library API

Essentially, the ANTLR C runtime library works by defining a series of structs with dynamically assigned function pointers in each struct. This way, each struct can be assigned with the functions that should be used for that struct. This however, makes for a little weird looking function calls like "currToken->getType(currToken)".

Friday, September 14, 2012

Displaying Remember The Milk (RTM) Tasks on Your Mac Desktop

So if you're like me, you're day-to-day work revolves around a GTD/TODO/Task manager of some sort. If you haven't tried or heard of Remember The Milk, its pretty much free to use. If I remember correctly, you only need to pay the yearly subscription (which really, is at a bargain price if you use it like I do) if you want to use the mobile apps.

I really love RTM because of its deep integration into existing services. You can create tasks by tweeting, sending emails, sending IMs, on your Google Calendar, on your Gmail, etc. Now, to the heart of the post: RTM gives you an atom feed of your tasks via some generated URL. So we can use this and massage the data into whatever we want :D

Geektool, on the other hand, is a dandy Mac tool that lets you, among other things, run scripts and display the results on your desktop. So basically, our workflow to display RTM tasks on a Mac desktop is simple:

  1. Install Geektool.
  2. Grab your RTM tasks atom feed url. There's one for each list, I'd get one for "All Tasks".  
  3. And setup Geektool to run the following ruby script. Done!
The only configuration you need to do for the script is change the URL and TEMP_FILE to point to your atom feed url and to a temporary file which the script will create to cache the last-fetched atom feed.

#!/usr/bin/env ruby

require 'rexml/document'
require 'open-uri'
require 'date'

URL ="YOUR_ATOM_FEED_URL_GOES_HERE"
TEMP_FILE = "ABSOLUTE_PATH_FOR_THE_CACHED_ATOM_FEED_GOES_HERE"

doc = nil
begin
doc = REXML::Document.new(open(URL).read)
rescue SocketError
  File.new(TEMP_FILE).each_line { |line|
    puts line
  }
  exit
end

tasks = {}
dates = []
REXML::XPath.each(doc, "//entry") { |node|
  title = REXML::XPath.first(node, "title/text()").to_s
  title = title[0..91] + "..." if title.size > 95
  date = REXML::XPath.first(node, "content/div/div/span[@class='rtm_due_value']/text()").to_s
  list = REXML::XPath.first(node, "content/div/div/span[@class='rtm_list_value']/text()").to_s
  short_date = date
  unless short_date.match(/\sat\s/).nil?
    short_date = date.scan(/^(.*)\sat/).first.first
  end

  break if (Date.strptime(short_date, "%a %d %b %y") - Date.today).to_i > 7
  break if date == "never"

  if tasks[short_date].nil?
    dates << short_date
    tasks[short_date] = [] if tasks[short_date].nil?
  end
  tasks[short_date] << {
    :title => title,
    :list => list,
    :date => date
  }
}
File.open(TEMP_FILE, 'w') { |f|
  dates.each { |d|
    f.puts d
    puts d
    tasks[d].each { |val|
      time = "        - "
      unless val[:date].match(/\sat\s/).nil?
        time = val[:date].scan(/\sat\s(.+)$/).first.first.gsub(/^(\d):/,'0\1:') + " - "
      end
      f.puts "#{time}#{val[:title]} (#{val[:list]})"
      puts "#{time}#{val[:title]} (#{val[:list]})"
    }
    f.puts ""
    puts ""
  }
  f.puts "[Updated: #{Time.now.to_s}]"
  puts "[Updated: #{Time.now.to_s}]"
}




Thursday, August 23, 2012

Lessons on Core Data

Aside from the standard principles (e.g., one context per thread, do not pass managed objects between threads), in the end, what worked for me in developing a multi-threaded Core Data application was the following guidelines:

  1. Always create/update your managed objects off the main thread
  2. Always get permanent IDs for your managed objects upon creation. If you are creating many (100+) of these managed objects in a loop, create an array of uninitialised objects before you enter the loop so you can obtain permanent IDs for all the empty objects with only one trip to the persistent store. You can always delete the unused uninitialised objects later on.
  3. Always fetch managed objects off the main thread. Specify that you only want the objectIDs to be returned. After which, you can pass an array of the objectIDs to the main thread for it to fetch using a single predicate of "self in %@". You can assert that the returned array has the same count as the objectIDs if you want.
  4. Use MagicalRecord for a house of easier and accessible utility methods, but understand what it means to have nested (related) contexts and background saves.
In particular point #2 above is really important to avoid some quirkiness and bugs (or unexpected behaviour may be a better term) that the current implementation of Core Data has. More details by Whitney Young.

Solving Printing Crashes on OS X Mountain Lion

Kudos to the tip from https://discussions.apple.com/thread/4213152?start=0&tstart=0
  1. Open Terminal.app
  2. cd /Library/Printers/hp/PDEs
  3. rm hpPostScriptPDE.plugin
  4. Done!
This solved my problems printing to my home HP printer and office Lexmark printers.