<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Brett Terpstraruby page  - Brett Terpstra</title>
	<atom:link href="http://brettterpstra.com/tag/ruby/feed/" rel="self" type="application/rss+xml" />
	<link>http://brettterpstra.com</link>
	<description>Elegant solutions to complex problems.</description>
	<lastBuildDate>Tue, 22 May 2012 02:49:10 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>Scatterbrains: git as biographer</title>
		<link>http://brettterpstra.com/scatterbrains-git-as-biographer/</link>
		<comments>http://brettterpstra.com/scatterbrains-git-as-biographer/#comments</comments>
		<pubDate>Tue, 08 May 2012 15:00:57 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[dayone]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[logging]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=3983</guid>
		<description><![CDATA[<p>This is another of my attempts at keeping track of my day in an orderly fashion. It’s a pretty simple idea. Given that most of of what I do is stored in git repositories, my commit logs are my best bet for seeing what I’ve accomplished each day. I just needed to pull them together and bundle them up without&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/scatterbrains-git-as-biographer/">Scatterbrains: git as biographer</a></p>]]></description>
			<content:encoded><![CDATA[<p>This is another of my attempts at keeping track of my day in an orderly fashion. It’s a pretty simple idea. Given that most of of what I do is stored in git repositories, my commit logs are my best bet for seeing what I’ve accomplished each day. I just needed to pull them together and bundle them up without having to think about it. If a lot of your work happens in git repositories and you make frequent commits, this might be of use to you.</p>

<p>This script runs nightly and visits a list of local git repositories to extract a log of any commits for the day. It formats them as Markdown and can log them to Day One or just to a plain text file (single file, appended). There’s an accompanying shell command for easily adding the current directory as a repo to check.</p>

<p>This is what my log looks like in nvALT (with a custom theme):</p>

<div id="attachment_4003" class="wp-caption aligncenter" style="width: 617px;  border: 1px solid #dddddd; background-color: #f3f3f3; padding-top: 4px; margin: 10px; text-align:center; display: block; margin-right: auto; margin-left: auto;"><img src="http://cdn2.brettterpstra.com/wp-content/uploads/2012/05/GitLoggerScreenshot.jpg?9d7bd4" alt="GitLogger Screenshot" title="GitLogger" width="607" height="390" class="size-full wp-image-4003" /><p style=' padding: 0 4px 5px; margin: 0;'  class="wp-caption-text">GitLogger log in nvALT</p></div>

<p>Git notes are included, as is body text of the commit if it exists. Formatting creates an unordered list, and short hashes for the commits are added at the end of the commit message, just in case you need them.</p>

<p>As usual, if you’re interested in trying it, I’m happy to share…</p>

<p><span id="more-3983"></span></p>

<h3>Automatic installation</h3>

<p>You can use the following command to automatically install the script and have it run at 11:50pm daily:</p>

<pre><code>/usr/bin/ruby -e "$(/usr/bin/curl -fsSL https://raw.github.com/gist/2633967/ed3f35df05ba12f1ab7176c12ad174d5210b38f1/gitlogger-install.rb)"
</code></pre>

<h3>Manual install</h3>

<ul>
<li>Save the script below (<a href="https://gist.github.com/2632346">gist link</a>) as <code>gitlogger.rb</code> in a script or bin folder on your drive.</li>
<li>Use the variables at the top to set it up for logging to Day One (<code>dayone = true</code>), or enter a path to a text file (<code>textlog = "path/to/text.md"</code>). If you don’t want to use text file logging, just set that option to “false”.</li>
<li>If you’re logging to Day One, see <a href="http://brettterpstra.com/logging-with-day-one-geek-style/">this post</a> for more instructions. This script doesn’t need the <code>dayone</code> CLI tool, but if you’re not using iCloud for storing your journal, you’ll need to modify the paths to point to your journal file.</li>
</ul>

<script src="http://gist.github.com/2632346.js"></script>

<p>The second little script (<a href="https://gist.github.com/2632356">gist link</a>) is a bash function for adding whatever directory I’m working in to the list that the logger uses to find repos for logging. For consistent results in building the repo list, run the <code>glog</code> command from the base directory of the git repository. Set it once and forget it. You can remove repos from logging by editing the file at <code>~/.gitlogger</code>. You can also just create that file by hand: each line is a repo, with the title first, followed by a colon, followed by the path.</p>

<ul>
<li>Add this function to your <code>~/.bash_profile</code> to be able to mark the current directory for logging by typing <code>glog Alias</code>, where “Alias” is the name you want to appear for the repo in your log.</li>
</ul>

<script src="http://gist.github.com/2632356.js"></script>

<p>To automate the script, I suggest using <code>launchd</code> (the OS X version of <code>cron</code>). Use <a href="http://www.peterborgapps.com/lingon/">Lingon</a> or copy the code below into a file called <code>com.yourusername.gitlogger.plist</code> and save it in <code>~/Library/LaunchAgents/</code>. After creating the file, you’ll want to run <code>launchctl load ~/Library/LaunchAgents/com.yourusername.gitlogger.plist</code> to get it started (or log out and back in, but that takes too long). The code as is will set up the logger to run at 11:50pm every night (in your local time). You’ll want to edit the Label and the ProgramArguments values to match your setup.</p>

<pre><code>
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
&lt;plist version=&quot;1.0&quot;&gt;
&lt;dict&gt;
    &lt;key&gt;Label&lt;/key&gt;
    &lt;string&gt;com.brettterpstra.gitlogger&lt;/string&gt;
    &lt;key&gt;ProgramArguments&lt;/key&gt;
    &lt;array&gt;
        &lt;string&gt;/usr/bin/ruby&lt;/string&gt;
        &lt;string&gt;/Users/ttscoff/scripts/gitlogger.rb&lt;/string&gt;
    &lt;/array&gt;
    &lt;key&gt;StartCalendarInterval&lt;/key&gt;
    &lt;dict&gt;
        &lt;key&gt;Hour&lt;/key&gt;
        &lt;integer&gt;23&lt;/integer&gt;
        &lt;key&gt;Minute&lt;/key&gt;
        &lt;integer&gt;50&lt;/integer&gt;
    &lt;/dict&gt;
&lt;/dict&gt;
&lt;/plist&gt;
</code></pre>

<p>Maybe this will be of use to <em>somebody</em>. Code contributions and suggestions welcome, just follow the links on the gists above to fork. My personal git workflow works well with this logging statement, but you might want to modify it to log tags only, etc. Let me know what you do with it!</p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/automating-taskpaper-to-day-one-logs/' rel='bookmark' title='Automating TaskPaper to Day One logs'>Automating TaskPaper to Day One logs</a></li>
<li><a href='http://brettterpstra.com/logging-with-day-one-geek-style/' rel='bookmark' title='Logging with Day One, geek style'>Logging with Day One, geek style</a></li>
<li><a href='http://brettterpstra.com/a-quick-voodoopad-script-plugin-go-to-today/' rel='bookmark' title='A Quick VoodooPad Script Plugin — Go To Today'>A Quick VoodooPad Script Plugin — Go To Today</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/scatterbrains-git-as-biographer/">Scatterbrains: git as biographer</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/scatterbrains-git-as-biographer/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Experiment: CSV to MMD tables with color coding</title>
		<link>http://brettterpstra.com/experiment-csv-to-mmd-tables-with-color-coding/</link>
		<comments>http://brettterpstra.com/experiment-csv-to-mmd-tables-with-color-coding/#comments</comments>
		<pubDate>Fri, 30 Mar 2012 00:48:33 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[googledocs]]></category>
		<category><![CDATA[multimarkdown]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=3824</guid>
		<description><![CDATA[<p>This is a quick experiment with Google Docs, CSVs, MultiMarkdown tables and CSS3 selectors. It’s really hackish, but it’s a start. I wanted to take Google Docs spreadsheet output and turn it into a clean table with color coding and my own styling. Here’s what I’ve done so far (example output here): Downloaded the iOS text editor spreadsheet as a&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/experiment-csv-to-mmd-tables-with-color-coding/">Experiment: CSV to MMD tables with color coding</a></p>]]></description>
			<content:encoded><![CDATA[<p>This is a quick experiment with Google Docs, CSVs, MultiMarkdown tables and CSS3 selectors. It’s really hackish, but it’s a start. I wanted to take Google Docs spreadsheet output and turn it into a clean table with color coding and my own styling. Here’s what I’ve done so far (example output <a href="http://assets.brettterpstra.com/csv2mmd/example.html">here</a>):</p>

<ol>
<li>Downloaded the <a href="http://brettterpstra.com/crowdsourcing-ios-text-editor-comparisons/">iOS text editor spreadsheet</a> as a CSV</li>
<li>Opened in Numbers for minor edits

<ul>
<li>Added a row of <code>:-----:</code> alignment syntax under first (header) row</li>
<li>Removed long text (HTML tables are a pain with wrapping and overflow)</li>
</ul></li>
<li>Exported a new CSV with changes</li>
<li>Regex magic to convert CSV to MMD table (almost automatically, still 5 minutes of hand editing)</li>
<li>Applied basic CSS3 using inline <code>&lt;style&gt;</code> tags in the Markdown

<ul>
<li>turn empty cells dark grey</li>
<li>highlight any cell with content (minus first column) in green</li>
<li>cells with a full-width colspan (tailored to this table, 11 columns) turn lighter grey</li>
</ul></li>
<li>Load it up in <a href="http://markedapp.com">Marked</a> and output the results with embedded style</li>
</ol>

<h3>Pretty pictures</h3>

<p>The cleaned up spreadsheet in Numbers</p>

<p><img src="http://assets.brettterpstra.com/csv2mmd/ioseds.numbers.jpg" alt="The cleaned up spreadsheet in Numbers" /></p>

<p>The converted table in MultiMarkdown</p>

<p><img src="http://assets.brettterpstra.com/csv2mmd/ioseds.md.jpg" alt="The converted table in MultiMarkdown" /> (after using Fletcher’s table cleanup script)</p>

<h3>The CSS:</h3>


<div class="wp_syntax"><div class="code"><pre class="css">table th <span class="br0">&#123;</span>
	<span class="kw1">white-space</span><span class="sy0">:</span><span class="kw2">nowrap</span>
<span class="br0">&#125;</span>
table td<span class="re2">:nth-</span>child<span class="br0">&#40;</span>n<span class="sy0">+</span><span class="nu0">2</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
	<span class="kw1">background</span><span class="sy0">:</span> <span class="kw2">rgb</span><span class="br0">&#40;</span><span class="nu0">156</span><span class="sy0">,</span> <span class="nu0">230</span><span class="sy0">,</span> <span class="nu0">144</span><span class="br0">&#41;</span><span class="sy0">;</span>
	<span class="kw1">font-weight</span><span class="sy0">:</span><span class="kw2">bold</span><span class="sy0">;</span>
	<span class="kw1">color</span><span class="sy0">:</span><span class="kw2">rgb</span><span class="br0">&#40;</span><span class="nu0">95</span><span class="sy0">,</span> <span class="nu0">191</span><span class="sy0">,</span> <span class="nu0">68</span><span class="br0">&#41;</span><span class="sy0">;</span>
	<span class="kw1">vertical-align</span><span class="sy0">:</span><span class="kw2">middle</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
table td<span class="re2">:empty </span><span class="br0">&#123;</span>
	<span class="kw1">background</span><span class="sy0">:</span> <span class="re0">#777</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
table td<span class="br0">&#91;</span>colspan<span class="sy0">=</span><span class="st0">&quot;11&quot;</span><span class="br0">&#93;</span> <span class="br0">&#123;</span>
	<span class="kw1">background</span><span class="sy0">:</span> <span class="re0">#aaa</span>!important
<span class="br0">&#125;</span></pre></div></div>


<h3>The hack Ruby CSV→MMD code:</h3>


<div class="wp_syntax"><div class="code"><pre class="ruby"><span class="co1">#!/usr/bin/ruby</span>
&nbsp;
input = STDIN.<span class="me1">read</span>
&nbsp;
<span class="co1"># find quoted cells and replace commas inside quotes with placeholder</span>
input.<span class="kw3">gsub!</span><span class="br0">&#40;</span><span class="sy0">/</span><span class="st0">&quot;([^,].*?)&quot;</span><span class="sy0">/</span>m<span class="br0">&#41;</span> <span class="br0">&#123;</span> <span class="sy0">|</span>quoted<span class="sy0">|</span>
	quoted.<span class="kw3">gsub</span><span class="br0">&#40;</span><span class="sy0">/</span><span class="br0">&#91;</span>\n\r<span class="br0">&#93;</span><span class="sy0">*/</span>,<span class="st0">''</span><span class="br0">&#41;</span>.<span class="kw3">gsub</span><span class="br0">&#40;</span><span class="sy0">/</span>,<span class="sy0">/</span>,<span class="st0">'zXzX'</span><span class="br0">&#41;</span>
<span class="br0">&#125;</span>
<span class="co1"># replace remaining commas with table divider (pipe)</span>
input.<span class="kw3">gsub!</span><span class="br0">&#40;</span><span class="sy0">/</span>,<span class="sy0">/</span>,<span class="st0">&quot;| &quot;</span><span class="br0">&#41;</span>
<span class="co1"># remove quotes from quoted cells</span>
input.<span class="kw3">gsub!</span><span class="br0">&#40;</span><span class="sy0">/</span><span class="br0">&#40;</span>\<span class="sy0">|</span> <span class="sy0">|</span>^<span class="br0">&#41;</span><span class="st0">&quot;(.*?)&quot;</span><span class="sy0">/</span>,<span class="st0">&quot;<span class="es0">\\</span>1<span class="es0">\\</span>2&quot;</span><span class="br0">&#41;</span>
<span class="co1"># replace placeholders with commas</span>
input.<span class="kw3">gsub!</span><span class="br0">&#40;</span><span class="sy0">/</span>zXzX<span class="sy0">/</span>,<span class="st0">&quot;,&quot;</span><span class="br0">&#41;</span>
&nbsp;
input.<span class="me1">each</span> <span class="br0">&#123;</span> <span class="sy0">|</span>l<span class="sy0">|</span>
	<span class="kw3">puts</span> <span class="st0">&quot;| #{l.strip} |&quot;</span>
<span class="br0">&#125;</span></pre></div></div>


<p>The script takes input on STDIN, so to use it you pipe the contents of the CSV to it with <code>cat</code>:</p>

<pre><code>cat myfile.csv | ~/scripts/csv2mmd.rb &gt; outputfile.md
</code></pre>

<h3>The result:</h3>

<p>You can view a truncated version of the output <a href="http://assets.brettterpstra.com/csv2mmd/example.html">here</a>. Please note that this comparison chart is not complete and does not include even half the apps in the spreadsheet. It’s just for testing!</p>

<p>You can also see the raw MMD code <a href="http://assets.brettterpstra.com/csv2mmd/example.txt">here</a>. It’s straight from the conversion script but has been run through Fletcher Penney’s table cleanup script to make it readable.</p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/an-average-morning/' rel='bookmark' title='An average morning'>An average morning</a></li>
<li><a href='http://brettterpstra.com/a-service-for-writing-multimarkdown-footnotes-inline/' rel='bookmark' title='A Service for writing MultiMarkdown footnotes inline'>A Service for writing MultiMarkdown footnotes inline</a></li>
<li><a href='http://brettterpstra.com/quick-tip-clean-up-your-multimarkdown-tables/' rel='bookmark' title='Quick Tip: clean up your MultiMarkdown tables'>Quick Tip: clean up your MultiMarkdown tables</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/experiment-csv-to-mmd-tables-with-color-coding/">Experiment: CSV to MMD tables with color coding</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/experiment-csv-to-mmd-tables-with-color-coding/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Automating TaskPaper to Day One logs</title>
		<link>http://brettterpstra.com/automating-taskpaper-to-day-one-logs/</link>
		<comments>http://brettterpstra.com/automating-taskpaper-to-day-one-logs/#comments</comments>
		<pubDate>Sun, 26 Feb 2012 05:12:27 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[dayone]]></category>
		<category><![CDATA[productivity]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[TaskPaper]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=3714</guid>
		<description><![CDATA[<p>This is just a quick post to mention a new idea I had while working on Marked today. I was, as usual, using TaskPaper to organize the project and track my progress. I use a different TaskPaper file in the main folder for each project I have going. The last script I posted is effective enough, and I should probably&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/automating-taskpaper-to-day-one-logs/">Automating TaskPaper to Day One logs</a></p>]]></description>
			<content:encoded><![CDATA[<p>This is just a quick post to mention a new idea I had while working on Marked today. I was, as usual, using <a href="http://www.hogbaysoftware.com/products/taskpaper">TaskPaper</a> to organize the project and track my progress. I use a different TaskPaper file in the main folder for each project I have going. The <a href="http://brettterpstra.com/log-taskpaper-archives-to-day-one/">last script I posted</a> is effective enough, and I should probably leave well enough alone, but I had to try one other thing.</p>

<p>I basically wanted to run a script with <code>launchd</code> at 11pm every night that would find <em>all</em> of my TaskPaper files and locate tasks finished during the previous 24 hours, creating a single log entry for them in <a href="http://dayoneapp.com/">Day One</a>. So that’s what I did.</p>

<p>I put a <a href="https://gist.github.com/1913007">gist up on GitHub</a>, and everything you need to know is in the comments at the top. It’s pretty simple: <code>mdfind</code> to Spotlight for recently-changed TaskPaper files, a little string scanning and then create an XML entry in the Day One folder. There are some caveats and potentially necessary customizations, but if you’re at all interested in running something like this, I’ll assume you’re capable of making a couple of tweaks.</p>

<p>By the way, did you see <a href="https://gist.github.com/1903137">Jered Benoit’s script</a> for logging the day’s completed OmniFocus tasks to Day One? Beautiful!</p>

<p>Also, Gabe over at MacDrifter has found Day One to be most useful for it’s intended purpose (personal journaling). Weird, I know<sup id="fnref:fn1"><a href="#fn:fn1" rel="footnote">1</a></sup>, but a really <a href="http://www.macdrifter.com/2012/02/dayone-and-time-travel/">great post you should take a moment to read</a>.</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:fn1">
<p>I kid. It’s weird that <em>I’m</em> using it for more technical things. I feel a need to more eloquently explain why I’ve opted to keep these logs in Day One and how I differentiate the various types of notes in my life, but that’s another post. <a href="#fnref:fn1" rev="footnote">↩</a></p>
</li>

</ol>
</div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/log-taskpaper-archives-to-day-one/' rel='bookmark' title='Log TaskPaper archives to Day One'>Log TaskPaper archives to Day One</a></li>
<li><a href='http://brettterpstra.com/taskpaper-date-scripts/' rel='bookmark' title='Taskpaper Date Scripts'>Taskpaper Date Scripts</a></li>
<li><a href='http://brettterpstra.com/a-few-scripts-for-taskpaper-users/' rel='bookmark' title='A few scripts for TaskPaper users'>A few scripts for TaskPaper users</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/automating-taskpaper-to-day-one-logs/">Automating TaskPaper to Day One logs</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/automating-taskpaper-to-day-one-logs/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>“Open URLs” Dropzone Destination</title>
		<link>http://brettterpstra.com/open-urls-dropzone-destination/</link>
		<comments>http://brettterpstra.com/open-urls-dropzone-destination/#comments</comments>
		<pubDate>Mon, 21 Nov 2011 16:30:01 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[dropzone]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[scripting]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=3212</guid>
		<description><![CDATA[<p>I just added a pull request for my latest Dropzone (review) Destination. It’s called “Open URLs,” and with it you can open text urls from any number of text files, drag text straight from any application, or just click it to scan your clipboard. The files, text or clipboard contents are scanned for any http(s) links and they’re sent to&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/open-urls-dropzone-destination/">“Open URLs” Dropzone Destination</a></p>]]></description>
			<content:encoded><![CDATA[<p><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/11/1321804820_Bookmarks_Black-2.png?9d7bd4" alt="Open URLs icon" title="1321804820_Bookmarks_Black-2" width="256" height="256" class="alignright size-full wp-image-3224" />I just added a pull request for my latest <a href="http://aptonic.com/">Dropzone</a> (<a href="http://brettterpstra.com/app-review-dropzone/">review</a>) Destination. It’s called “Open URLs,” and with it you can open text urls from any number of text files, drag text straight from any application, or just click it to scan your clipboard. The files, text or clipboard contents are scanned for any http(s) links and they’re sent to your default browser using the system <code>open</code> command.</p>

<p>“Open URLs” is ideal if you collect browsing “sessions” in text files or notes in any format (possibly using tools such as <a href="http://brettterpstra.com/tablinks-2-0/">TabLinks</a> or <a href="http://brettterpstra.com/eversave-revisited-now-with-session-restore/">EverSave</a>). I often use TabLinks to save a collection of same-subject links to a note in <a href="http://brettterpstra.com/project/nvalt/">nvALT</a> and then drag and drop the text to re-open the whole session. It’s also very handy when someone emails you a list of urls that you want to open all at once (e.g. a design project with multiple versions and they couldn’t be bothered to make it a Dropbox gallery). If you’re on a page with a list of links and you want to open them all in tabs quickly, just view source, select the section with the links and drag the selection to the Destination in Dropzone. It will ignore duplicates and open one tab per unique link.</p>

<p>I expect this Destination will be available soon from the <a href="https://github.com/aptonic/dropzone-user-scripts">GitHub repo</a>, but you can download it directly below if you’re using Dropzone and it happens to sound useful. <strong>Update:</strong> before I even got this post out, John Winter of Aptonic got this Destination on the <a href="http://aptonic.com/extend.php">Extend page</a> of the Dropzone website. You can grab it there, too!</p>

<div class="download_desc"><p class="download-icon"><a href="http://brettterpstra.com/downloads/OpenURLS.dropzone.zip?9d7bd4" title="Download Open URLs Dropzone Destination (53)"><img src="http://cdn2.brettterpstra.com/wp-content/uploads/downloads/thumbnails/2011/11/1321804820_Bookmarks_Black-2.png?9d7bd4" alt="download image for Open URLs Dropzone Destination" width="64" /></a><br /><a href="http://brettterpstra.com/downloads/OpenURLS.dropzone.zip?9d7bd4" title="Download Open URLs Dropzone Destination (53)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/OpenURLS.dropzone.zip?9d7bd4" title="Download Open URLs Dropzone Destination (53)">Open URLs Dropzone Destination</a> — A Destination for Dropzone that allows you to drag files, text or click to scan your clipboard. All links found in the text are sent to your default browser to open in tabs. <a href="http://brettterpstra.com/open-urls-dropzone-destination/">More Info</a></p></div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/linksmasher-updated-works-once-again/' rel='bookmark' title='LinkSmasher updated, works once again'>LinkSmasher updated, works once again</a></li>
<li><a href='http://brettterpstra.com/app-review-dropzone/' rel='bookmark' title='App Review: Dropzone'>App Review: Dropzone</a></li>
<li><a href='http://brettterpstra.com/app-giveaway-dropzone/' rel='bookmark' title='Mac App Giveaway: Dropzone'>Mac App Giveaway: Dropzone</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/open-urls-dropzone-destination/">“Open URLs” Dropzone Destination</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/open-urls-dropzone-destination/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Marked scripts: nvALT, Evernote, MarsEdit, Scrivener</title>
		<link>http://brettterpstra.com/marked-scripts-nvalt-evernote-marsedit-scrivener/</link>
		<comments>http://brettterpstra.com/marked-scripts-nvalt-evernote-marsedit-scrivener/#comments</comments>
		<pubDate>Tue, 15 Nov 2011 03:38:38 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[marked]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[scripting]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=3177</guid>
		<description><![CDATA[<p>Thus far, Marked has had a great reception and has, overall, worked superbly with a wide range of text editors. I frequently get requests for integration with more complex editors, but have been unable to fulfill them because Marked requires an actual text file to watch. To try and fix this situation, I’ve been scripting “watchers” for various applications which&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/marked-scripts-nvalt-evernote-marsedit-scrivener/">Marked scripts: nvALT, Evernote, MarsEdit, Scrivener</a></p>]]></description>
			<content:encoded><![CDATA[<p><a href="http://markedapp.com"><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/11/newmarkedlogo.png?9d7bd4" alt="" title="newmarkedlogo" width="244" height="281" class="alignright size-full wp-image-3178" /></a>Thus far, <a href="http://markedapp.com">Marked</a> has had a great reception and has, overall, worked superbly with a wide range of text editors. I frequently get requests for integration with more complex editors, but have been unable to fulfill them because Marked requires an actual text file to watch. To try and fix this situation, I’ve been scripting “watchers” for various applications which embed their files inside of bundles or otherwise obscure the actual content files from Spotlight.</p>

<p>I’ve created scripts for <a href="http://notational.net/">Notational Velocity</a>/<a href="http://brettterpstra.com/project/nvalt/">nvALT</a>, <a href="http://www.red-sweater.com/marsedit/">MarsEdit</a>, <a href="http://www.literatureandlatte.com/">Scrivener</a> and <a href="http://www.evernote.com/">Evernote</a>. The scripts will be part of the <a href="http://support.markedapp.com/kb/how-to-tips-and-tricks/marked-bonus-pack-scripts-commands-and-bundles">Marked Bonus pack</a>, but are also available as a separate download here (see the end of the post). I would eventually like to incorporate the functionality directly into Marked, but it will take some time to add the necessary configuration options and make it an elegant fit.</p>

<p><span id="more-3177"></span></p>

<p>The scripts work by using timestamps and polling with Ruby to detect changes within certain files and directories (varies from script to script)<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>. The Evernote, nvALT and MarsEdit scripts can be run without arguments (or as LaunchAgents). The Scrivener script requires the path to the <code>.scriv</code> file for your project as the only argument.</p>

<p>The preview contents are always saved to ‘Marked Preview.md’ in your home folder. When you first start, you may need to create this file manually with a text editor and save it to your home folder. Once the scripts have run once, the file will exist and you can open it in Marked any time you’re working in a “watched” application.</p>

<p>All of this was inspired by <a href="https://gist.github.com/1355558">this gist</a> by <a href="https://github.com/myfreeweb">myfreeweb</a>. His method uses fsevents and Python, which is definitely more elegant in many cases. I switched it over to use Ruby and timestamps because I’m pretty lame with Python right now. If you want to take the basic ideas and convert them to use fsevents, please share! I’ll be happy to host and credit (or just link, if you prefer) any submissions.</p>

<p>I’m certainly taking requests for any other editors you’d like to have “watched,” so drop your vote in the comments.</p>

<h3>Notes:</h3>

<p>For all of these scripts, the easiest way to use them is to put them in a convenient folder (I use <code>~/scripts</code>) and run <code>chmod a+x path/to/script.rb</code> to make them executable. With the exception of the Scrivener script (<code>scrivwatch.rb</code>), you can then just type the path and script name (e.g. <code>~/scripts/everwatch.rb</code>) and hit Enter. They will run and watch for changes in their specific application until you cancel the command by typing <code>Control-c</code>.</p>

<p>The scripts will create a file in your home directory (modifiable in the script) called ‘Marked Preview.md’. Open that file in Marked; Marked will watch that file for changes that the scripts make.</p>

<p>You can create LaunchAgents for any of these (except, again, Scrivener) and run them automatically in the background if you know what you’re doing. If you don’t, you can still use an app like <a href="http://www.peterborgapps.com/lingon/">Lingon</a> to do it.</p>

<h4>Evernote (everwatch.rb)</h4>

<p>To keep the ‘Marked Preview.md’ file synced with whatever note you’re currently editing in <a href="http://www.evernote.com/">Evernote</a>, start the script by running <code>~/path/to/everwatch.rb</code> in Terminal. The script watches for changes to timestamps on any directory in Evernote’s data folder. This shouldn’t need to be adjusted. To update Marked, you’ll need to have “~/Marked Preview.md” open and then hit “Command-S” in your Evernote note. The autosave on Evernote will work, but it takes longer.</p>

<p>The HTML of the note is captured via AppleScript and run through <code>textutil</code> to remove the HTML formatting. This means that embedded images won’t come through, but those probably would have broken anyway. The script is specifically expecting you to write your notes in Markdown. If you’re not, I’m not sure why you’d want a Marked preview anyway…</p>

<p>Even with “Command-S” there’s still a 4–5 second delay on the update, as it takes a bit for Evernote to write out to the file, the script to poll through and notice the change, the content to be pulled via AppleScript and written to the preview file and then for Marked to pick up on the change there. Considering all of that, 4–5 seconds isn’t too bad. If someone can think of a faster way, I’m certainly open to it.</p>

<h4>Scrivener (scrivwatch.rb)</h4>

<p>The <a href="http://www.literatureandlatte.com/">Scrivener</a> script watches the RTF files that Scrivener keeps within the project as you write. If you set Scrivener’s preferences to auto-save 1 second after you stop typing, Marked will stay pretty snappy on the updates without any further intervention.</p>

<p>To launch (assuming you made the script executable as detailed at the beginning of this section), open Terminal and type <code>path/to/scrivwatch.rb /path/to/YourDocument.scriv</code>. Example: <code>~/scripts/scrivwatch ~/Documents/Thesis.scriv</code>. The script will take it from there and run until you interrupt with <code>Control-c</code>.</p>

<p>The files Scrivener stores are Rich Text Format, so Marked can’t view their contents directly. The script runs the most recently-edited file through <code>textutil</code> to convert from RTF to text. That gets passed to the ‘Marked Preview.md’ file for Marked to display. Only the portion of your Scrivener document that you’re currently editing will be displayed. I played with concatenating the whole document for preview, but it’s not easy to determine the order to display all of the individual files in. Again, if anyone has further ideas, please share!</p>

<h4>MarsEdit (marswatch.rb)</h4>

<p>Thanks to Daniel Jalkut for an assist with this one. It watches the <a href="http://www.red-sweater.com/marsedit/">MarsEdit</a> autosave folder for any changes, and then uses AppleScript to get the full contents of the editor (post <em>and</em> continued) when one is detected. Because the autosave can be a bit slow on the draw, it continues updating every second for 10 seconds, whether there’s a change or not. If no more changes are detected within 10 seconds, it chills out in the background until the next one is detected.</p>

<p>Run <code>marswatch.rb</code> to start polling for changes, open ‘Marked Preview.md‘ from your home folder and Marked should start updating the preview automatically as you make changes in a MarsEdit post.</p>

<h4>Notational Velocity/nvALT (nvwatch.rb)</h4>

<p>If you’re using <a href="http://notational.net/">Notational Velocity</a> (or my fork, <a href="http://brettterpstra.com/project/nvalt/">nvALT</a>), you can tell it to save your notes as text files on your drive. This script will watch these text files for updates, then display the contents of the most recently-edited note. It’s a workable solution, at least until I get better integration worked into nvALT directly.</p>

<p>You need to configure the script to point to your chosen folder for note storage, and if you’re using any unique extension, you’ll need to add to or modify the list in the script. It should be pretty obvious what needs to be set if you look at the top of the script.</p>

<h3>Download</h3>

<p>I’m sure I forgot some details in this writeup, so if you have any questions, leave a comment and I’ll add information as needed.</p>

<p>The most recent versions of the watcher scripts are now included the the <a href="http://support.markedapp.com/kb/how-to-tips-and-tricks/marked-bonus-pack-scripts-commands-and-bundles">Marked Bonus Pack</a>.</p>

<p>You can find out more about Marked at the <a href="http://markedapp.com">Marked website</a>.</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:1">
<p>Related tip: you can use <code>sudo opensnoop -p PID</code> (where PID is the PID of the application you’re working with) to view all files that the application touches. It’s a good way to figure out what to watch when working on scripts like these. <a href="#fnref:1" rev="footnote">↩</a></p>
</li>

</ol>
</div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/preview-a-full-scrivener-document-in-marked-live/' rel='bookmark' title='Preview a full Scrivener document in Marked, live'>Preview a full Scrivener document in Marked, live</a></li>
<li><a href='http://brettterpstra.com/scrivwatcher-droplet-an-easier-live-scrivener-preview/' rel='bookmark' title='ScrivWatcher droplet, an easier live Scrivener preview'>ScrivWatcher droplet, an easier live Scrivener preview</a></li>
<li><a href='http://brettterpstra.com/the-second-marked-giveaway/' rel='bookmark' title='The second Marked giveaway!'>The second Marked giveaway!</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/marked-scripts-nvalt-evernote-marsedit-scrivener/">Marked scripts: nvALT, Evernote, MarsEdit, Scrivener</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/marked-scripts-nvalt-evernote-marsedit-scrivener/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>The MindMeister Markdown Showdown</title>
		<link>http://brettterpstra.com/the-mindmeister-markdown-showdown/</link>
		<comments>http://brettterpstra.com/the-mindmeister-markdown-showdown/#comments</comments>
		<pubDate>Tue, 08 Nov 2011 13:30:27 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[guestblogger]]></category>
		<category><![CDATA[markdown]]></category>
		<category><![CDATA[mind mapping]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[scripting]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=3157</guid>
		<description><![CDATA[<p>Welcome Donald Curtis I’d like to welcome Donald Curtis as a guest blogger today, here to talk about a very cool script we worked on to add some Markdown love to MindMeister. It started with a DM from Donald on Twitter with a link to a GitHub gist, and after some back and forth it became a very cool (and&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/the-mindmeister-markdown-showdown/">The MindMeister Markdown Showdown</a></p>]]></description>
			<content:encoded><![CDATA[<h2>Welcome Donald Curtis</h2>

<p><em>I’d like to welcome <a href="http://milkbox.net">Donald Curtis</a> as a guest blogger today, here to talk about a very cool script we worked on to add some <a href="http://daringfireball.net/projects/markdown/">Markdown</a> love to <a href="http://www.mindmeister.com/">MindMeister</a>. It started with a DM from <a href="http://twitter.com/milkypostman">Donald on Twitter</a> with a link to a GitHub gist, and after some back and forth it became a very cool (and useful) tool. Cool enough that I really wanted to share it here. Since the script is his brainchild and almost entirely his handiwork, I thought it would be cool if Donald did the honors.</em></p>

<h3>The backstory</h3>

<p>I am nearing the end of my first semester as a professor at a small liberal arts college.  My graduate school years left me fairly detached from some of the topics I learned in my undergrad years and so I spend a decent amount of time simply re-aquainting myself with material.  It is easy to digest the second time around, but as I process the material I also have to think about how and what to present.</p>

<p>Mind maps have turned out to be an exceptionally useful tool for organizing the key ideas I want to cover.  And for whatever style of teaching I employ, they end up being a great in-class reference.</p>

<p>When I first started toying with the idea of incorporating mind maps, I did the search for the <em>right</em> software tool. I considered software-only solutions such as <a href="http://itunes.apple.com/us/app/ithoughtshd-mindmapping/id369020033?mt=8">iThoughts HD</a> for iOS and <a href="http://www.mythoughtsformac.com/">My Thoughts</a> for Mac, with <a href="http://db.tt/SmevY5nN">Dropbox</a> as the glue. Both of these apps <em>look</em> amazing and sometimes that’s all the motivation you need.</p>

<p><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/11/06_historyview_v4.jpg?9d7bd4" alt="" title="06_historyview_v4" width="300" height="251" class="alignright size-full wp-image-3158" />After reading <a href="http://brettterpstra.com/the-mac-and-ios-mind-mapping-app-extravaganza/">Brett’s Appstravaganza</a> I gave <a href="http://www.mindmeister.com/">MindMeister</a> a spin (because they offer a <em><a href="http://en.wikipedia.org/wiki/Gratis_versus_libre#.22Free_beer.22_vs_.22free_speech.22_distinction">free as in beer</a></em> account). By the time I started paying for the account, I was in. You can read more about the app in Brett’s discussion about mind mapping, but in summary: free trial, web-based (read: multi-platform), decent iOS app, looks good, and roughly equivalent yearly cost to software-only solutions. The added bonus for my application is–on the occasion I have an external link to a picture or movie–MindMeister works on the lecture machines.</p>

<p>Stepping back, I assume teaching isn’t a scenario that a lot of the readership can associate with. Not to worry, mind maps are useful in a wide variety of scenarios.</p>

<p><span id="more-3157"></span></p>

<h3>The MindMeister Markdown Showdown</h3>

<p>MindMeister lacks one feature to satisfy the <a href="http://daringfireball.net/projects/markdown/">Markdown</a> users among us: <em>export to Markdown</em>. <a href="http://brettterpstra.com/quick-tip-mindmanager-to-clean-html/">Brett recently</a> had a <a href="http://brettterpstra.com/tag/quicktip/">Quick Tip</a> that showed how to use <a href="http://www.mindjet.com/">Mindjet MindManager</a> to get a MindMeister map to HTML through <a href="http://daringfireball.net/projects/markdown/">Markdown</a>. I don’t use Mindjet and I didn’t want to really have to install another piece of software and figure out how to migrate my maps through it. After all, the approach was described as a “quick hack” so I assume if I went ahead and described it as a bit contrived–in order to make myself sound smarter–there would be no hard feelings <em>[Ed: there would not]</em>. I would never say such a thing though. It gets the job done.</p>

<p>In order to have a more elegant solution and teach myself <a href="http://www.ruby-lang.org/en/">Ruby</a>, I wrote up a small script that uses the MindMeister API to pull down maps as a markdown-formatted outline. I named the script for clarity as <code>mindmeister2md.rb</code>. With help from Brett to refine much of the output–clean up newlines, fix escaped characters, add notes, add pictures–the core script that I wrote is now very usable.</p>

<h3>Overview</h3>

<p>The script has two functions,</p>

<ul>
<li>list all your maps </li>
<li>output a map as markdown</li>
</ul>

<p>When run with no arguments, the script prints a menu of maps available for you to select:</p>

<pre><code>   Available MindMeister Maps
   ---
    1: Software                      ( 2011-10-31 04:13:58 ) [ 117882387 ]
    2: Internet Layer Protocols      ( 2011-10-27 18:10:26 ) [ 120233062 ]
    3: Network Layer                 ( 2011-10-27 16:41:09 ) [ 119115816 ]
    4: HTML                          ( 2011-10-24 17:59:28 ) [ 117882606 ]
    5: Newbie                        ( 2011-10-24 03:53:40 ) [ 119564927 ]
    6: Ethernet                      ( 2011-10-24 03:53:00 ) [ 116616227 ]
    7: My First iPad Map             ( 2011-10-21 14:19:07 ) [ 119100256 ]
    8: My First iPhone Map           ( 2011-10-21 14:17:30 ) [ 119262965 ]
    9: Threads                       ( 2011-10-20 15:14:29 ) [ 116945908 ]
   10: Computer Hardware             ( 2011-10-12 19:24:19 ) [ 117875898 ]
   11: ACM Programming Tips          ( 2011-10-11 04:03:21 ) [ 117550509 ]
   12: Interprocess Communication    ( 2011-10-04 02:54:56 ) [ 116511362 ]
   Selection: 
</code></pre>

<p>Optionally you can pass either the name of a map (case insensitive) or the map id (given in square brackets in the menu) as argument(s)<sup id="fnref:args"><a href="#fn:args" rel="footnote">1</a></sup>. Both of these would return the “My First iPad Map”,</p>

<pre><code>   ./mindmeister2md.rb my first ipad map
   ./mindmeister2md.rb 119100256
</code></pre>

<p>By default the script outputs the markdown to the screen. There is an optional command-line argument (<code>-o</code>) which will write the markdown to a file.</p>

<p>For the UNIX geeks: Only the generate markdown is sent to standard output (<code>STDOUT</code>).  For Mac users this means the ability to copy the markdown to the copy buffer:</p>

<pre><code>    ./mindmeister2md.rb my first ipad map | pbcopy
</code></pre>

<h3>Configuration</h3>

<p>There is one downside to using the MindMeister API with a script: <em>each user needs to have a <a href="https://www.mindmeister.com/account/api/">MindMeister api key and secret</a> in order to use the script</em><sup id="fnref:doritos"><a href="#fn:doritos" rel="footnote">2</a></sup>. Rather than edit the source code, the script relies on a configuration file named <code>.mindmeister2md</code> in the home directory. The file will be created automatically the first time you run the script; it will complain to you about you needing to update the configuration and specify an API key and your music being too loud.</p>

<p>The configuration file looks like this:</p>

<pre><code>  --- 
   indent: 4
   list_level: 2
   api_key: 
   secret: 
</code></pre>

<p>You get both <code>api_key</code> and <code>secret</code> from the <a href="https://www.mindmeister.com/account/api/">MindMeister api request page</a>. The other two options you can set are:</p>

<dl>
<dt><code>list_level</code></dt>
<dd>The level in the map where lists should begin. At a <code>list_level</code> of 2, the first two levels of the map tree structure are represented as markdown headings, rather than as lists. A <code>list_level</code> of 0 will mean that the map will be exported as a single giant list.</dd>

<dt><code>indent</code></dt>
<dd>specifies the number of spaces that represent a single indent in the list.</dd>
</dl>

<h3>Command Line Options</h3>

<p>Most of the configuration file can also be changed at run-time using optional command line arguments. There is also an option to simply print the maps without actually printing any of them out as well as output to a file.</p>

<p>Usage: mindmeister2md.rb [options] <map id | map name></p>

<dl>
<dt><code>-l, --list</code></dt>
<dd>List available maps and exit.</dd>

<dt><code>-i, --indent &lt;indent&gt;</code></dt>
<dd>Set number of spaces for each indent level. Like temporarily setting <code>indent</code> in the configuration file.</dd>

<dt><code>-s, --listlevel &lt;list_level&gt;</code></dt>
<dd>Set the level at which lists should start. Like temporarily setting <code>list_level</code> in the configuration file.</dd>

<dt><code>-o, --output FILE</code></dt>
<dd>Write output to FILE.</dd>

<dt><code>-h, --help</code></dt>
<dd>Print command-line argument help.</dd>
</dl>

<p>Thanks to Brett for his major contributions to the script–which actually made it useful–and for letting me send him this writeup. You can find me on <a href="http://twitter.com/milkypostman">Twitter</a>, <a href="https://github.com/milkypostman">Github</a>, and I sometimes post bad prose on <a href="http://milkbox.net">milkbox.net</a>.</p>

<p>Check out <a href="https://github.com/milkypostman/mindmeister2md">mindmeister2md on GitHub</a>, or download below.</p>

<h3>Download</h3>

<div class="download_desc"><p class="download-icon"><a href="http://brettterpstra.com/downloads/mm2md" title="Download mindmeister2md (186)"><img src="http://cdn2.brettterpstra.com/wp-content/uploads/downloads/thumbnails/2011/11/mindmeister2mdlogo.png?9d7bd4" alt="download image for mindmeister2md" width="64" /></a><br /><a href="http://brettterpstra.com/downloads/mm2md" title="Download mindmeister2md (186)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/mm2md" title="Download mindmeister2md (186)">mindmeister2md</a> — A Ruby script by Donald Curtis (and Brett Terpstra) to turn MindMeister mind maps into Markdown outlines, complete with notes and images. <a href="http://brettterpstra.com/?p=3157">More Info</a></p></div>

<div class="footnotes">
<hr />
<ol>

<li id="fn:args">
<p>Normal arguments parsing is abused a bit as you don’t need to quote the entire map name (<code>my first ipad map</code> is actually passing 4 arguments which are concatenated together). <a href="#fnref:args" rev="footnote">↩</a></p>
</li>

<li id="fn:doritos">
<p>Since <code>mindmeister2md.rb</code> is not a compiled program and I cannot–don’t want to–give you <em>my</em> API key, you have to get your own; similar to how I feel anytime I have a bag of Doritos. <a href="#fnref:doritos" rev="footnote">↩</a></p>
</li>

</ol>
</div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/quick-tip-mindmanager-to-clean-html/' rel='bookmark' title='Quick Tip: MindManager to (clean) HTML'>Quick Tip: MindManager to (clean) HTML</a></li>
<li><a href='http://brettterpstra.com/when-plain-text-is-wrong/' rel='bookmark' title='When plain text is wrong'>When plain text is wrong</a></li>
<li><a href='http://brettterpstra.com/the-mac-and-ios-mind-mapping-app-extravaganza/' rel='bookmark' title='The Mac and iOS mind mapping app extravaganza'>The Mac and iOS mind mapping app extravaganza</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/the-mindmeister-markdown-showdown/">The MindMeister Markdown Showdown</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/the-mindmeister-markdown-showdown/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Automating random giveaways, v1.0</title>
		<link>http://brettterpstra.com/automating-random-giveaways-v1-0/</link>
		<comments>http://brettterpstra.com/automating-random-giveaways-v1-0/#comments</comments>
		<pubDate>Sat, 01 Oct 2011 17:15:13 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[applescript]]></category>
		<category><![CDATA[automation]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[scripting]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=2879</guid>
		<description><![CDATA[<p>To pick the winners for the Yoink and Listary giveaways, I wrote an AppleScript to make the process completely random and as painless as possible for me. I still have plans for making a similar WordPress plugin, but that’s still in the planning stages. For now, this is working quite well and I thought I’d share it. I doubt many&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/automating-random-giveaways-v1-0/">Automating random giveaways, v1.0</a></p>]]></description>
			<content:encoded><![CDATA[<p><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/10/winduprobot.jpg?9d7bd4" alt="windup robot" title="windup robot" width="200" height="334" class="alignright size-full wp-image-2880" />To pick the winners for the Yoink and Listary giveaways, I wrote an AppleScript to make the process completely random and as painless as possible for me. I still have plans for making a similar WordPress plugin, but that’s still in the planning stages. For now, this is working quite well and I thought I’d share it. I doubt many people have the exact same setup and requirements, but the basic structure might be helpful for other projects.</p>

<p>It’s designed to work with promo code giveaways. So far, that’s what my giveaways have been. If I end up running a giveaway with a different registration method, I’ll extend the script and post an update for anyone interested.</p>

<p>This method currently has two requirements/limitations based on my own needs and setup. First, it only works with Mail.app for collecting entries and sending notifications. Second,  the regular expressions it uses to find the entrants name, email and IP address are based on the standard WordPress comment notification emails. The basic idea here could easily be applied to other AppleScript-able email applications and modified to parse any format, but I’ll leave that up to intrepid readers for now.</p>

<p><span id="more-2879"></span></p>

<h3>How it works</h3>

<p>First, I do a search for the subject line in Mail. All of the comment notifications that WordPress sends me for the post have the same subject line, so that gathers them all very quickly. All that matters is that I can select them all before running the script.</p>

<p>Next, I run the AppleScript. It begins by asking me for the giveaway app’s name and requests my list of promo codes. Then the script pulls the names and the message content from each selected email, passing the information to a Ruby script which scans the content for the actual email address (necessary because the actual sender on the email is always my WordPress address) and entrant’s name/handle and IP. The list of results are then passed to a second Ruby script along with the promo codes. This script checks for duplicates in the emails and IPs, picks enough random numbers (range 0 — number of entrants) to match each code provided (recursive function to avoid duplicate numbers). The Ruby script then opens a new message in Mail.app for each winner, and populates it with recipient, subject, congratulatory message (including code) and signature. It also adds the mailto: link with all of this info to an HTML file on the Desktop for reference.</p>

<p>It needs some improvements, especially in the mail creation routine. It currently uses the <code>mailto</code> AppleScript command, but really should create the outgoing message step by step, which would ultimately allow for complete automation at some point. For the time being I still want to be able to confirm the winners and message content anyway; I’ll automate more when I have full trust in it.</p>

<h4>The AppleScript</h4>


<div class="wp_syntax"><div class="code"><pre class="applescript"><span class="coMULTI">(* 
PickWinners by Brett Terpstra, 2011, freely distributed
Picks random winners from selected WordPress comment notification emails in Mail.app
Checks for duplicate emails and IP addresses
On run, requests Application title and a newline-separated list of promo codes
Picks winners from list for each promo code and opens ready-to-send notification emails in Mail.app
Saves list of winners in HTML file with mailto links (including message and promo code)
*)</span>
&nbsp;
<span class="kw3">set</span> pickWinnerScript <span class="kw3">to</span> <span class="br0">&#40;</span><span class="kw1">path to</span> <span class="kw1">me</span><span class="br0">&#41;</span> <span class="sy0">&amp;</span> <span class="st0">&quot;Contents:Resources:Scripts:pickwinners.rb&quot;</span> <span class="kw2">as</span> <span class="kw1">string</span>
<span class="kw3">set</span> extractEmailScript <span class="kw3">to</span> <span class="br0">&#40;</span><span class="kw1">path to</span> <span class="kw1">me</span><span class="br0">&#41;</span> <span class="sy0">&amp;</span> <span class="st0">&quot;Contents:Resources:Scripts:extractemail.rb&quot;</span> <span class="kw2">as</span> <span class="kw1">string</span>
<span class="kw3">property</span> winners : <span class="st0">&quot;&quot;</span>
<span class="kw3">set</span> _res <span class="kw3">to</span> <span class="kw1">display dialog</span> <span class="st0">&quot;Application name?&quot;</span> <span class="kw1">default answer</span> <span class="st0">&quot;&quot;</span>
<span class="kw3">set</span> appname <span class="kw3">to</span> <span class="kw1">text</span> returned <span class="kw3">of</span> _res
<span class="kw3">set</span> _res <span class="kw3">to</span> <span class="kw1">display dialog</span> <span class="st0">&quot;Enter promo codes&quot;</span> <span class="kw1">default answer</span> <span class="st0">&quot;Paste codes, one per line&quot;</span>
<span class="kw3">set</span> codes <span class="kw3">to</span> <span class="kw1">text</span> returned <span class="kw3">of</span> _res
&nbsp;
<span class="kw3">tell</span> <span class="kw1">application</span> <span class="st0">&quot;Mail&quot;</span>
	<span class="kw3">set</span> _sel <span class="kw3">to</span> <span class="kw2">the</span> <span class="kw1">selection</span>
	<span class="kw3">set</span> entries <span class="kw3">to</span> <span class="br0">&#123;</span><span class="br0">&#125;</span>
&nbsp;
	<span class="kw3">repeat</span> <span class="kw3">with</span> _msg <span class="kw3">in</span> _sel
		<span class="kw3">set</span> nameAndEmail <span class="kw3">to</span> <span class="kw1">do shell script</span> <span class="st0">&quot;<span class="es0">\&quot;</span>&quot;</span> <span class="sy0">&amp;</span> <span class="br0">&#40;</span><span class="kw1">POSIX path</span> <span class="kw3">of</span> extractEmailScript<span class="br0">&#41;</span> <span class="sy0">&amp;</span> <span class="st0">&quot;<span class="es0">\&quot;</span> <span class="es0">\&quot;</span>&quot;</span> <span class="sy0">&amp;</span> <span class="kw1">content</span> <span class="kw3">of</span> _msg <span class="sy0">&amp;</span> <span class="st0">&quot;<span class="es0">\&quot;</span>&quot;</span>
		<span class="kw3">set</span> <span class="kw3">end</span> <span class="kw3">of</span> entries <span class="kw3">to</span> nameAndEmail
	<span class="kw3">end</span> <span class="kw3">repeat</span>
&nbsp;
	<span class="kw3">set</span> <span class="br0">&#123;</span>astid, AppleScript<span class="co2">'</span>s <span class="kw1">text</span> <span class="kw1">item</span> <span class="kw1">delimiters</span><span class="br0">&#125;</span> <span class="kw3">to</span> <span class="br0">&#123;</span>AppleScript<span class="co2">'</span>s <span class="kw1">text</span> <span class="kw1">item</span> <span class="kw1">delimiters</span>, <span class="st0">&quot;
&quot;</span><span class="br0">&#125;</span>
	<span class="kw3">set</span> emails <span class="kw3">to</span> entries <span class="kw2">as</span> <span class="kw1">string</span>
	<span class="kw3">set</span> AppleScript<span class="co2">'</span>s <span class="kw1">text</span> <span class="kw1">item</span> <span class="kw1">delimiters</span> <span class="kw3">to</span> astid
&nbsp;
	<span class="kw3">set</span> winners <span class="kw3">to</span> <span class="kw1">do shell script</span> <span class="st0">&quot;<span class="es0">\&quot;</span>&quot;</span> <span class="sy0">&amp;</span> <span class="br0">&#40;</span><span class="kw1">POSIX path</span> <span class="kw3">of</span> pickWinnerScript<span class="br0">&#41;</span> <span class="sy0">&amp;</span> <span class="st0">&quot;<span class="es0">\&quot;</span>  <span class="es0">\&quot;</span>&quot;</span> <span class="sy0">&amp;</span> appname <span class="sy0">&amp;</span> <span class="st0">&quot;<span class="es0">\&quot;</span> <span class="es0">\&quot;</span>&quot;</span> <span class="sy0">&amp;</span> codes <span class="sy0">&amp;</span> <span class="st0">&quot;<span class="es0">\&quot;</span> <span class="es0">\&quot;</span>&quot;</span> <span class="sy0">&amp;</span> emails <span class="sy0">&amp;</span> <span class="st0">&quot;<span class="es0">\&quot;</span>&quot;</span>
<span class="kw3">end</span> <span class="kw3">tell</span>
&nbsp;
<span class="kw3">if</span> winners <span class="kw3">is</span> <span class="st0">&quot;error&quot;</span> <span class="kw3">then</span>
	<span class="kw1">display dialog</span> <span class="st0">&quot;There was an error generating winners&quot;</span>
<span class="kw3">else</span>
	<span class="kw3">set</span> _btn <span class="kw3">to</span> <span class="kw1">display dialog</span> <span class="st0">&quot;Winners written to &quot;</span> <span class="sy0">&amp;</span> winners <span class="kw1">buttons</span> <span class="br0">&#123;</span><span class="st0">&quot;OK&quot;</span>, <span class="st0">&quot;Open in Safari&quot;</span><span class="br0">&#125;</span>
	<span class="kw3">if</span> button returned <span class="kw3">of</span> _btn <span class="kw3">is</span> <span class="st0">&quot;Open in Safari&quot;</span> <span class="kw3">then</span>
		<span class="kw3">tell</span> <span class="kw1">application</span> <span class="st0">&quot;Safari&quot;</span> <span class="kw3">to</span> <span class="kw1">open</span> <span class="br0">&#40;</span><span class="kw1">POSIX file</span> winners<span class="br0">&#41;</span> <span class="kw2">as</span> <span class="kw1">alias</span>
	<span class="kw3">end</span> <span class="kw3">if</span>
<span class="kw3">end</span> <span class="kw3">if</span></pre></div></div>


<h4>extractemail.rb</h4>


<div class="wp_syntax"><div class="code"><pre class="ruby"><span class="co1">#!/usr/bin/env ruby</span>
&nbsp;
input = ARGV.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot; &quot;</span><span class="br0">&#41;</span>
emailmatch = input.<span class="me1">match</span><span class="br0">&#40;</span><span class="sy0">/</span>E<span class="sy0">-</span>mail : <span class="br0">&#40;</span>.<span class="sy0">*</span><span class="br0">&#41;</span><span class="sy0">/</span><span class="br0">&#41;</span>
namematch = input.<span class="me1">match</span><span class="br0">&#40;</span><span class="sy0">/</span>Author : <span class="br0">&#40;</span>.<span class="sy0">*</span>?<span class="br0">&#41;</span> \<span class="br0">&#40;</span>IP: <span class="br0">&#40;</span>.<span class="sy0">*</span>?<span class="br0">&#41;</span>\<span class="br0">&#41;</span><span class="sy0">/</span><span class="br0">&#41;</span>
&nbsp;
<span class="kw3">print</span> <span class="st0">&quot;#{namematch[1].strip} &lt;#{emailmatch[1].strip}&gt; (#{namematch[2].strip})&quot;</span></pre></div></div>


<h4>pickwinners.rb</h4>


<div class="wp_syntax"><div class="code"><pre class="ruby"><span class="co1">#!/usr/bin/env ruby</span>
<span class="kw3">require</span> <span class="st0">'ftools'</span>
&nbsp;
appname = ARGV<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>
ARGV.<span class="me1">shift</span>
codes = ARGV<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>.<span class="kw3">split</span>
count = codes.<span class="me1">length</span>
ARGV.<span class="me1">shift</span>
input = ARGV<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>.<span class="kw3">split</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="es0">\n</span>&quot;</span><span class="br0">&#41;</span>.<span class="me1">uniq</span>
&nbsp;
norepeat_email = <span class="br0">&#91;</span><span class="br0">&#93;</span>
norepeat_IP = <span class="br0">&#91;</span><span class="br0">&#93;</span>
entries = <span class="br0">&#91;</span><span class="br0">&#93;</span>
&nbsp;
input.<span class="me1">each</span> <span class="br0">&#123;</span><span class="sy0">|</span>entry<span class="sy0">|</span>
  name,email,ip = entry.<span class="me1">scan</span><span class="br0">&#40;</span><span class="sy0">/</span>^<span class="br0">&#40;</span>.<span class="sy0">*</span>?<span class="br0">&#41;</span> <span class="sy0">*&lt;</span><span class="br0">&#40;</span><span class="br0">&#91;</span>^ <span class="br0">&#93;</span><span class="sy0">+</span><span class="br0">&#41;</span><span class="sy0">&gt;</span> \<span class="br0">&#40;</span><span class="br0">&#40;</span>.<span class="sy0">*</span>?<span class="br0">&#41;</span>\<span class="br0">&#41;</span><span class="sy0">/</span><span class="br0">&#41;</span><span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>
  <span class="kw1">unless</span> <span class="br0">&#40;</span>norepeat_email.<span class="kw1">include</span>?<span class="br0">&#40;</span>email<span class="br0">&#41;</span> <span class="sy0">||</span> norepeat_IP.<span class="kw1">include</span>?<span class="br0">&#40;</span>ip<span class="br0">&#41;</span><span class="br0">&#41;</span>
    norepeat_email.<span class="me1">push</span><span class="br0">&#40;</span>email<span class="br0">&#41;</span>
    norepeat_IP.<span class="me1">push</span><span class="br0">&#40;</span>ip<span class="br0">&#41;</span>
    entries <span class="sy0">&lt;&lt;</span> <span class="br0">&#123;</span> <span class="st0">'name'</span> <span class="sy0">=&gt;</span> name, <span class="st0">'email'</span> <span class="sy0">=&gt;</span> email <span class="br0">&#125;</span>
  <span class="kw1">end</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw1">def</span> pick_number<span class="br0">&#40;</span>max,numbers<span class="br0">&#41;</span>
  num = <span class="kw3">rand</span><span class="br0">&#40;</span>max<span class="br0">&#41;</span>
  <span class="kw1">if</span> numbers.<span class="kw1">include</span>? num
    pick_number<span class="br0">&#40;</span>max,numbers<span class="br0">&#41;</span>
  <span class="kw1">else</span>
    <span class="kw2">return</span> num
  <span class="kw1">end</span>
<span class="kw1">end</span>
&nbsp;
randoms = <span class="br0">&#91;</span><span class="br0">&#93;</span>
count.<span class="me1">times</span> <span class="kw1">do</span>
  randoms.<span class="me1">push</span><span class="br0">&#40;</span>pick_number<span class="br0">&#40;</span>entries.<span class="me1">length</span>,randoms<span class="br0">&#41;</span><span class="br0">&#41;</span>
<span class="kw1">end</span>
&nbsp;
winners = <span class="br0">&#91;</span><span class="br0">&#93;</span>
randoms.<span class="me1">each_with_index</span> <span class="br0">&#123;</span><span class="sy0">|</span>winner,i<span class="sy0">|</span>
  entry = entries<span class="br0">&#91;</span>winner<span class="br0">&#93;</span>
  name = entry<span class="br0">&#91;</span><span class="st0">'name'</span><span class="br0">&#93;</span>
  email = entry<span class="br0">&#91;</span><span class="st0">'email'</span><span class="br0">&#93;</span>
  mailto = <span class="sy0">%</span>Q<span class="br0">&#123;</span>mailto:<span class="co1">#{email}?subject=Congratulations #{name}, you won!&amp;body=Here is your promo code for #{appname}: #{codes[i]}%0A%0AThanks for reading!%0A%0A-Brett}</span>
  link = <span class="sy0">%</span>Q<span class="br0">&#123;</span><span class="sy0">&lt;</span>li<span class="sy0">&gt;&lt;</span>a href=<span class="st0">&quot;#{mailto}&quot;</span><span class="sy0">&gt;</span><span class="co1">#{name} &lt;#{email}&gt;&lt;/a&gt;&lt;/li&gt;}</span>
  winners.<span class="me1">push</span><span class="br0">&#40;</span>link<span class="br0">&#41;</span>
  <span class="sy0">%</span>x<span class="br0">&#123;</span>echo <span class="st0">'tell application &quot;Mail&quot; to mailto &quot;#{mailto.gsub(/'</span><span class="sy0">/</span>,<span class="st0">&quot;<span class="es0">\'</span>&quot;</span><span class="br0">&#41;</span><span class="br0">&#125;</span><span class="st0">&quot;'|osascript}
}
&nbsp;
if winners.length == count
  output = &quot;</span><span class="sy0">&lt;</span>ul<span class="sy0">&gt;</span><span class="st0">&quot;
  output += winners.join(&quot;</span>\n<span class="st0">&quot;)
  output += &quot;</span><span class="sy0">&lt;/</span>ul<span class="sy0">&gt;</span><span class="st0">&quot;
&nbsp;
  outfile = File.new(File.expand_path(&quot;</span>~<span class="sy0">/</span>Desktop<span class="sy0">/</span><span class="co1">#{appname}Winners.html&quot;),'w+')</span>
  outfile.<span class="kw3">puts</span> output
  outfile.<span class="me1">close</span>
  <span class="kw3">puts</span> <span class="kw4">File</span>.<span class="me1">expand_path</span><span class="br0">&#40;</span><span class="st0">&quot;~/Desktop/#{appname}Winners.html&quot;</span><span class="br0">&#41;</span>
<span class="kw1">else</span>
  <span class="kw3">puts</span> <span class="st0">&quot;error&quot;</span>
<span class="kw1">end</span></pre></div></div>


<p>It’s not the most elegant solution, but it does the job of ensuring randomness and handling the nitty gritty parts of running a contest.</p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/listary-giveaway-get-yours/' rel='bookmark' title='Listary giveaway, get yours!'>Listary giveaway, get yours!</a></li>
<li><a href='http://brettterpstra.com/five-yoink-promo-codes-up-for-grabs/' rel='bookmark' title='Five Yoink promo codes up for grabs!'>Five Yoink promo codes up for grabs!</a></li>
<li><a href='http://brettterpstra.com/ios-giveaway-writeup/' rel='bookmark' title='iOS App Giveaway: WriteUp'>iOS App Giveaway: WriteUp</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/automating-random-giveaways-v1-0/">Automating random giveaways, v1.0</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/automating-random-giveaways-v1-0/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>GVoice command line SMS revisited</title>
		<link>http://brettterpstra.com/gvoice-command-line-sms-revisited/</link>
		<comments>http://brettterpstra.com/gvoice-command-line-sms-revisited/#comments</comments>
		<pubDate>Thu, 08 Sep 2011 14:07:44 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[google voice]]></category>
		<category><![CDATA[hacks]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[terminal]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/gvoice-command-line-sms-revisited/</guid>
		<description><![CDATA[<p>Thanks to a comment from Evaryont on the original SMS from the command line with Google Voice post, a problem that was causing Error 500s in the script has been solved. Check the original post for the script, and change the line (around line 90) rnrse = newres.match(/‘_rnr_se’: ‘([^’]+)’/)[1] to: rnrse = CGI.escape(newres.match(/‘_rnr_se’: ‘([^’]+)’/)[1]) The CGI escaping should help with&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/gvoice-command-line-sms-revisited/">GVoice command line SMS revisited</a></p>]]></description>
			<content:encoded><![CDATA[<p><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/09/google_voice_128.png?9d7bd4" alt="Google Voice icon" height="128" width="128" class="alignright"></p>

<p>Thanks to a <a href="http://brettterpstra.com/sms-from-the-command-line-with-google-voice/#comment-20950">comment from Evaryont</a> on the original <a href="http://brettterpstra.com/sms-from-the-command-line-with-google-voice/">SMS from the command line with Google Voice</a> post, a problem that was causing Error 500s in the script has been solved.</p>

<p><span id="more-2733"></span></p>

<p>Check the original post for the script, and change the line (around line 90)</p>

<p>rnrse = newres.match(/‘_rnr_se’: ‘([^’]+)’/)[1]</p>

<p>to:</p>

<p>rnrse = CGI.escape(newres.match(/‘_rnr_se’: ‘([^’]+)’/)[1])</p>

<p>The CGI escaping should help with the Google’s changes to the <code>_rnr_se</code> variable. Evaryont also notes that the script works great in Arch Linux with Ruby 1.9.2. For 1.9 compatibility (thanks to David for clarification in the comments below), remove the <code>-rjcode</code> from the hashbang on the first line, and add <code>require 'jcode' if RUBY_VERSION &lt; '1.9'</code> below. Have fun!</p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/sms-from-the-command-line-with-google-voice/' rel='bookmark' title='SMS from the command line with Google Voice'>SMS from the command line with Google Voice</a></li>
<li><a href='http://brettterpstra.com/a-system-service-for-to-url-shortening/' rel='bookmark' title='A System Service for to. url shortening'>A System Service for to. url shortening</a></li>
<li><a href='http://brettterpstra.com/address-book-search-and-skype-from-the-command-line/' rel='bookmark' title='Address Book search and Skype from the command line'>Address Book search and Skype from the command line</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/gvoice-command-line-sms-revisited/">GVoice command line SMS revisited</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/gvoice-command-line-sms-revisited/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>OTask: CLI for OmniFocus</title>
		<link>http://brettterpstra.com/otask-cli-for-omnifocus/</link>
		<comments>http://brettterpstra.com/otask-cli-for-omnifocus/#comments</comments>
		<pubDate>Sat, 02 Jul 2011 19:10:50 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[appscript]]></category>
		<category><![CDATA[omnifocus]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=2410</guid>
		<description><![CDATA[<p>I’ve been sitting on this one for a while, until I got a request from Patrick regarding an OmniFocus CLI and LaunchBar. I thought it might be time to dig this up and post it. A reader named Tony left a comment on my Duplicating Safari browsing sessions post, recommending that I try out appscript for my AppleScript bridging needs.&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/otask-cli-for-omnifocus/">OTask: CLI for OmniFocus</a></p>]]></description>
			<content:encoded><![CDATA[<p><em>I’ve been sitting on this one for a while, until I got a request from <a href="https://twitter.com/#!/pattulus">Patrick</a> regarding an OmniFocus CLI and LaunchBar. I thought it might be time to dig this up and post it.</em></p>

<hr />

<p>A reader named Tony <a href="http://brettterpstra.com/duplicating-safari-browsing-sessions-between-macs/#comment-16853">left a comment</a> on my <a href="http://brettterpstra.com/duplicating-safari-browsing-sessions-between-macs/">Duplicating Safari browsing sessions</a> post, recommending that I try out <a href="http://appscript.sourceforge.net/">appscript</a> for my AppleScript bridging needs. I frequently use system calls to <code>osascript</code> to do this, and I’ve shied away from scripting bridges in the past because they make things much harder to share and distribute. I thought I’d give it a try, though.</p>

<p>I like it. I like it a lot. It’s frustrating that I can’t just hand you this script without a list of requirements and dependencies, but I’m going share it anyway, just to show off some of the appscript’s capabilities.</p>

<p>What I built was a CLI for OmniFocus. I had an AppleScript/Ruby monstrosity that actually worked with TaskPaper, The Hit List, Things and OmniFocus, but that one got out of hand. I took the good parts of it, concentrated on OmniFocus and converted it to appscript in short time. The result is OTask.</p>

<p><span id="more-2410"></span></p>

<h3>Requirements</h3>

<p>You need a few things before this will even think about running for you. RubyGems is a must. I still haven’t figured out if that’s part of the default OS X install or not, but if you have Developer tools, you’ve got it. Then you need the ‘rb-appscript’ gem. You also need the gems ‘chronic’ and ‘amatch’. You can install each by using <code>gem install gemname</code>, and you may have to run the command with <code>sudo</code>, depending on your system’s permissions. If you’re still on board, here are the docs, and the script <a href="http://ttscoff.github.com/OTask/">is on GitHub</a>.</p>

<h4>Documentation</h4>

<p>OTask uses a custom syntax to allow entry of the various elements of an action in one line of text. The following formats can be used anywhere in the line, with the exception of the flag (!) which must be the last character on the line, preceded by a space.</p>

<ul>
<li>@context            (fragment, no spaces)</li>
<li>#project             (fragment, no spaces)</li>
<li>due(due date)        (can be shortened as d(date))</li>
<li>start(start date)    (can be shortened as s(date))</li>
<li>(notes)</li>
<li>!                        (sets task as flagged)</li>
</ul>

<p>Contexts and project specifiers should not include spaces. The algorithm that is used will find the best match for the string you give it, so you only need to include enough of it to distinguish it from other contexts or projects. For example, if I were going to put an action directly into my Markdown QuickTags folder, I could just use “#mdqt” and it will find it. “@corr” will get me the “correspondence” context.</p>

<p>Dates are entered in natural language format. You can type “tomorrow,” “in 3 days,” “next tuesday,” etc. You can also use “+3″ to set a date 3 days from the current day, “+7″ for a week, and so on.</p>

<h3>Command line options</h3>

<p>–h, –help     Displays help message<br />
 –q, –quiet    Output as little as possible, overrides verbose<br />
 –V, –verbose  Verbose output<br />
 –g, –growl    Use Growl for feedback</p>

<h3>Example usage</h3>

<pre><code>$ otask "Write a letter to mom"
</code></pre>

<p>This will put a task into your inbox with the name “Write a letter to mom.” Nothing else will be set, it will wait there for you to pick it up.</p>

<pre><code>$ otask -g "Pick up the kids from school @err #single due(today 3pm) !"
</code></pre>

<p>This creates a new task in a project called Single Tasks, with a context of “errands”, a due date of 3pm on the current day, and flags the task.</p>

<p>The task will go to your inbox by default, and–if provided–project and context will be set. Your settings for automatic cleanup will determine what happens after that. Task elements not specified are left unset.</p>

<p>The <code>-g</code> parameter gives us our feedback via Growl, which is handy if you’re calling it from a background script or application launcher like Quicksilver or LaunchBar.</p>

<pre><code>$ otask "Brainstorm for the morning meeting (Bill had some ideas, it might be worth checking in with him this afternoon) d(tomorrow 8am) #hipstartup @think"
</code></pre>

<p>This will create a task with a note. Everything in parenthesis is removed from the task name and placed into the notes of the action, sans parenthesis. Note that the due date prefix can be shortened to just “d,” (and the start date prefix can be just “s”).</p>

<p>OTask looks for notes in parenthesis, but it can also receive piped input from other applications as a note for the task. If you wanted to include text from a file, the output of a command or the plain-text contents of your clipboard, you can just pipe the output into the command, specifying the rest of the options as usual.</p>

<pre><code>$ pbpaste | otask "Notes from the morning meeting @ref"
</code></pre>

<p>That would take the current contents of your clipboard and make them the attached note on the “Notes from the morning meeting” task (with the context “reference”).</p>

<h4>Calling from LaunchBar (et al.)</h4>

<p>You can do this with any app that can run a script with input, or call it from automated scripts if you could think of a reason to. Below is the AppleScript for a LaunchBar action. Create a new script in AppleScript Editor and paste the code in. Edit the path in the last function to point to wherever you put the otask script. Save the AppleScript as OTask.scpt in <code>~/Library/Application Support/LaunchBar/Actions</code>.</p>

<p>You’ll find the Action in LaunchBar after it indexes. Type ‘ota’ (or as much as you need to get it to come up) and then press space bar. Use the syntax shown above to write out your action and its elements, but leave out the ‘otask’ part and any parameters. Hit return and Growl (you have it <a href="http://growl.info">installed, right?</a>) will tell you what’s up.</p>


<div class="wp_syntax"><div class="code"><pre class="applescript"><span class="kw3">on</span> handle_string<span class="br0">&#40;</span>actionString<span class="br0">&#41;</span>
	<span class="kw3">if</span> <span class="br0">&#40;</span>length <span class="kw3">of</span> actionString <span class="kw3">is</span> <span class="kw2">not</span> <span class="nu0">0</span><span class="br0">&#41;</span> <span class="kw3">then</span>
		<span class="kw3">my</span> runRubyScript<span class="br0">&#40;</span>actionString<span class="br0">&#41;</span>
	<span class="kw3">end</span> <span class="kw3">if</span>
	<span class="kw1">open</span> location <span class="st0">&quot;x-launchbar:hide&quot;</span>
<span class="kw3">end</span> handle_string
&nbsp;
<span class="kw3">on</span> runRubyScript<span class="br0">&#40;</span>action<span class="br0">&#41;</span>
	<span class="kw3">set</span> res <span class="kw3">to</span> <span class="kw1">do shell script</span> <span class="st0">&quot;$HOME/scripts/otask.rb -g <span class="es0">\&quot;</span>&quot;</span> <span class="sy0">&amp;</span> action <span class="sy0">&amp;</span> <span class="st0">&quot;<span class="es0">\&quot;</span>&quot;</span>
<span class="kw3">end</span> runRubyScript</pre></div></div>


<h4>Download</h4>

<p><a href="http://ttscoff.github.com/OTask/">http://ttscoff.github.com/OTask/</a></p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/things-tasks-from-the-command-line/' rel='bookmark' title='Things tasks from the command line'>Things tasks from the command line</a></li>
<li><a href='http://brettterpstra.com/moving-day/' rel='bookmark' title='Moving day'>Moving day</a></li>
<li><a href='http://brettterpstra.com/omnifocus-and-markdown-oh-my/' rel='bookmark' title='OmniFocus and Markdown, oh my'>OmniFocus and Markdown, oh my</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/otask-cli-for-omnifocus/">OTask: CLI for OmniFocus</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/otask-cli-for-omnifocus/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Watch for file changes and refresh your browser automatically</title>
		<link>http://brettterpstra.com/watch-for-file-changes-and-refresh-your-browser-automatically/</link>
		<comments>http://brettterpstra.com/watch-for-file-changes-and-refresh-your-browser-automatically/#comments</comments>
		<pubDate>Mon, 07 Mar 2011 08:33:34 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[automator]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[service]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=2056</guid>
		<description><![CDATA[<p>After my epic three-part post on Saturday, I spent the rest of the weekend doing more “useful” things. Now it’s Sunday night (Monday morning, I think), and I’ve got some kind of minor food poisoning which is currently keeping me awake. Thus a “hey, cool trick” post. I actually already own an app which can do this to some extent,&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/watch-for-file-changes-and-refresh-your-browser-automatically/">Watch for file changes and refresh your browser automatically</a></p>]]></description>
			<content:encoded><![CDATA[<p><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/03/magcodepic.jpg?9d7bd4" alt="Post image for Watcher Service" title="Code Watcher" width="288" height="263" class="alignright size-full wp-image-2062" />After my epic three-part post on Saturday, I spent the rest of the weekend doing more “useful” things. Now it’s Sunday night (Monday morning, I think), and I’ve got some kind of minor food poisoning which is currently keeping me awake. Thus a “hey, cool trick” post.</p>

<p>I actually already own an app which can do this to some extent, and I know there are more available. I like to do things the hard way once in a while. What I wanted was a basic script which could execute arbitrary code whenever a file of a certain type changed within a directory. The use case is web development: whenever I change a site-related file (html, php, css, less, rb, erb, etc.), I want Safari to refresh the related page.</p>

<p>Folder Actions don’t work well on my system (do they work for anyone?). Hazel would work, but I needed something more immediate. I had a version that used <code>launchd</code>, but it was difficult to consistently start and stop from a script. Here’s the final solution I came up with.</p>

<p><span id="more-2056"></span></p>

<p>At the top, let me say that the heavy lifting in my script was taken from a SASS file-watching script by <a href="https://github.com/carlo/haml-sass-file-watcher">Carlo Zottman</a>. It uses Ruby to poll a file collection for modification date variations, and keeps a pretty low profile. I wanted to avoid compiled code for this, as I eventually do want to go to bed tonight.</p>

<p>The basic goals:</p>

<ul>
<li>Watch only files of a specific type</li>
<li>Refresh my primary browser when a change occurs</li>
<li>Refresh across windows and tabs, but limit by user-specified keyword in URL</li>
</ul>

<p>I built both a command line script and a System Service to do this, and both work as standalone solutions. The Automator action makes it possible to right click a folder in Finder and choose “Watcher” to start watching it, and it asks you for the tab keyword in a nice popup dialog. Beyond that, it really just wraps the command line script. You can modify either with the following instructions.</p>

<p>Here’s the command line version:</p>


<div class="wp_syntax"><div class="code"><pre class="ruby"><span class="co1">#!/usr/bin/env ruby</span>
<span class="co1"># watch.rb by Brett Terpstra, 2011 &lt;http://brettterpstra.com&gt;</span>
<span class="co1"># with credit to Carlo Zottmann &lt;https://github.com/carlo/haml-sass-file-watcher&gt;</span>
&nbsp;
<span class="kw3">trap</span><span class="br0">&#40;</span><span class="st0">&quot;SIGINT&quot;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> <span class="kw3">exit</span> <span class="br0">&#125;</span>
&nbsp;
<span class="kw1">if</span> ARGV.<span class="me1">length</span> <span class="sy0">&lt;</span> <span class="nu0">2</span>
  <span class="kw3">puts</span> <span class="st0">&quot;Usage: #{$0} watch_folder keyword&quot;</span>
  <span class="kw3">puts</span> <span class="st0">&quot;Example: #{$0} . mywebproject&quot;</span>
  <span class="kw3">exit</span>
<span class="kw1">end</span>
&nbsp;
dev_extension = <span class="st0">'dev'</span>
filetypes = <span class="br0">&#91;</span><span class="st0">'css'</span>,<span class="st0">'html'</span>,<span class="st0">'htm'</span>,<span class="st0">'php'</span>,<span class="st0">'rb'</span>,<span class="st0">'erb'</span>,<span class="st0">'less'</span>,<span class="st0">'js'</span><span class="br0">&#93;</span>
watch_folder = ARGV<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>
keyword = ARGV<span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span>
<span class="kw3">puts</span> <span class="st0">&quot;Watching #{watch_folder} and subfolders for changes in project files...&quot;</span>
&nbsp;
<span class="kw1">while</span> <span class="kw2">true</span> <span class="kw1">do</span>
  files = <span class="br0">&#91;</span><span class="br0">&#93;</span>
  filetypes.<span class="me1">each</span> <span class="br0">&#123;</span><span class="sy0">|</span>type<span class="sy0">|</span>
    files <span class="sy0">+</span>= <span class="kw4">Dir</span>.<span class="me1">glob</span><span class="br0">&#40;</span> <span class="kw4">File</span>.<span class="me1">join</span><span class="br0">&#40;</span> watch_folder, <span class="st0">&quot;**&quot;</span>, <span class="st0">&quot;*.#{type}&quot;</span> <span class="br0">&#41;</span> <span class="br0">&#41;</span>
  <span class="br0">&#125;</span>
  new_hash = files.<span class="me1">collect</span> <span class="br0">&#123;</span><span class="sy0">|</span>f<span class="sy0">|</span> <span class="br0">&#91;</span> f, <span class="kw4">File</span>.<span class="me1">stat</span><span class="br0">&#40;</span>f<span class="br0">&#41;</span>.<span class="me1">mtime</span>.<span class="me1">to_i</span> <span class="br0">&#93;</span> <span class="br0">&#125;</span>
  hash <span class="sy0">||</span>= new_hash
  diff_hash = new_hash <span class="sy0">-</span> hash
&nbsp;
  <span class="kw1">unless</span> diff_hash.<span class="me1">empty</span>?
    hash = new_hash
&nbsp;
    diff_hash.<span class="me1">each</span> <span class="kw1">do</span> <span class="sy0">|</span>df<span class="sy0">|</span>
      <span class="kw3">puts</span> <span class="st0">&quot;Detected change in #{df[0]}, refreshing&quot;</span>
      <span class="sy0">%</span>x<span class="br0">&#123;</span>osascript<span class="co4">&lt;&lt;ENDGAME
        	tell application &quot;Safari&quot;
          	set windowList to every window
          	repeat with aWindow in windowList
          		set tabList to every tab of aWindow
          		repeat with atab in tabList
          			if (URL of atab contains &quot;#{keyword}&quot;) then
          			  tell atab to do javascript &quot;window.location.reload()&quot;
          			end if
          		end repeat
          	end repeat
        	end tell
ENDGAME</span>
<span class="br0">&#125;</span>
    <span class="kw1">end</span>
  <span class="kw1">end</span>
&nbsp;
  <span class="kw3">sleep</span> <span class="nu0">1</span>
<span class="kw1">end</span></pre></div></div>


<h2>Installing</h2>

<p>If you want to use the above script from Terminal, just put it in a directory in your path and run <code>chmod a+x watch.rb</code> on it. Then you can call it with <code>watch.rb folder/to/watch keyword</code>. The keyword you pass will determine which tabs will refresh in your browser. For example, if I’m working on dev.heckyesmarkdown.com (my local development version), I would use “dev.heckyes” to limit the refresh to only associated tabs. Once the script is running, you can stop it any time by typing Control-C in that Terminal. If you’ve run it in the background, you’ll either need to foreground it or kill it manually.</p>

<p>To install the System Service, <a href="#download">download the workflow</a>, unzip it and place it in <code>~/Library/Services</code> (where ‘~’ is your home folder). It will now show up when you right click on one or more selected folders in the Finder. Choose “Watcher,” enter a URL-matching keyword and let it go. You should see the spinning gear icon in your menubar. When you want to stop watching the folder, click that icon and choose “Stop Watcher”.</p>

<h2>Customizing</h2>

<p>In the standalone Ruby script (above), you can easily modify the watched filetypes in line 14, and you can replace the AppleScript with your own starting on line 34. I’ll offer some examples for other browsers below.</p>

<p>The Service contains pretty much the same script, but modified to work with an Automator workflow. If you open it in Automator and skip to the last action, you’ll see the script and you can make your filetype and AppleScript modifications in there.</p>

<h3>Using other browsers</h3>

<p>Chrome has decent AppleScript support these days, so changing this script to work with it is trivial. Just replace the AppleScript portion (beginning with “tell application…”) with the following:</p>


<div class="wp_syntax"><div class="code"><pre class="applescript"><span class="kw3">tell</span> <span class="kw1">application</span> <span class="st0">&quot;Google Chrome&quot;</span>
	<span class="kw3">set</span> windowList <span class="kw3">to</span> <span class="kw2">every</span> <span class="kw1">window</span>
	<span class="kw3">repeat</span> <span class="kw3">with</span> aWindow <span class="kw3">in</span> windowList
		<span class="kw3">set</span> tabList <span class="kw3">to</span> <span class="kw2">every</span> <span class="kw1">tab</span> <span class="kw3">of</span> aWindow
		<span class="kw3">repeat</span> <span class="kw3">with</span> atab <span class="kw3">in</span> tabList
			<span class="kw3">if</span> <span class="br0">&#40;</span>URL <span class="kw3">of</span> atab <span class="kw2">contains</span> <span class="st0">&quot;#{keyword}&quot;</span><span class="br0">&#41;</span> <span class="kw3">then</span>
				<span class="kw3">tell</span> atab <span class="kw3">to</span> reload
			<span class="kw3">end</span> <span class="kw3">if</span>
		<span class="kw3">end</span> <span class="kw3">repeat</span>
	<span class="kw3">end</span> <span class="kw3">repeat</span>
<span class="kw3">end</span> <span class="kw3">tell</span></pre></div></div>


<p>Firefox is a little less elegant, as far as I know, and requires System Events scripting to refresh a page. If you know a better way, I’d love to hear it, but here’s a basic script for reloading the front page. You might as well delete the keyword portions of the script/workflow if you go this route, they won’t be applicable:</p>


<div class="wp_syntax"><div class="code"><pre class="applescript"> <span class="kw3">tell</span> app <span class="st0">&quot;Firefox&quot;</span> <span class="kw3">to</span> <span class="kw1">activate</span>
 <span class="kw3">tell</span> app <span class="st0">&quot;System Events&quot;</span>
   keystroke <span class="st0">&quot;r&quot;</span> using command down
 <span class="kw3">end</span> <span class="kw3">tell</span></pre></div></div>


<p>I won’t detail any other browsers; if you’re doing web development in something more exotic, I’ll assume you know how to script it.</p>

<h3>Executing arbitrary code</h3>

<p>You don’t have to refresh browsers with this. You don’t even have to use it for web development. Have it watch text files for changes and run an iCal script when one is modified. It’s basically a hyperactive Folder Action.</p>

<p>If you want to have the script do something new every time you use it, you might want to externalize the action code. Just modify the <code>diff_hash.each do |df|</code> block to run an outside script. If it’s shell code, just make it executable and call it with <code>%x{path/to/script}</code>, and if it’s AppleScript, call it with <code>%x{/usr/bin/osascript path/to/script}</code>. Then you can modify the action script each time without changing the watch.rb file.</p>

<p>If you want to get really crazy, you could pass the script to execute as a command line parameter, or request it in the Automator workflow and pass it in. I don’t have a need for that right now, but if you build it, let me know!</p>

<p>I hope folks find this useful. Now, excuse me while I go retch for a little bit.</p>

<h2 id="download">Download</h2>

<div class="download_desc"><p class="download-icon"><a href="http://brettterpstra.com/downloads/Watcher.zip?9d7bd4" title="Download Watcher Service (227)"><img src="http://brettterpstra.com/wp-content/images/serviceicon.jpg?9d7bd4" alt="download image for Watcher Service" width="64" /></a><br /><a href="http://brettterpstra.com/downloads/Watcher.zip?9d7bd4" title="Download Watcher Service (227)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/Watcher.zip?9d7bd4" title="Download Watcher Service (227)">Watcher Service</a> — A Snow Leopard System Service that runs on folders in Finder. By default, it watches for changes to web dev files and refreshes Safari, but it can be customized extensively. See the “more info” link for customization instructions. <a href="http://brettterpstra.com/?p=2056">More Info</a></p></div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/os-x-service-for-natural-language-dates/' rel='bookmark' title='OS X Service for natural language dates'>OS X Service for natural language dates</a></li>
<li><a href='http://brettterpstra.com/a-few-scripts-for-taskpaper-users/' rel='bookmark' title='A few scripts for TaskPaper users'>A few scripts for TaskPaper users</a></li>
<li><a href='http://brettterpstra.com/a-bash-function-for-markdown-bloggers/' rel='bookmark' title='A Bash function for Markdown bloggers'>A Bash function for Markdown bloggers</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/watch-for-file-changes-and-refresh-your-browser-automatically/">Watch for file changes and refresh your browser automatically</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/watch-for-file-changes-and-refresh-your-browser-automatically/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Natural language date conversion for TextMate</title>
		<link>http://brettterpstra.com/natural-language-date-conversion-for-textmate/</link>
		<comments>http://brettterpstra.com/natural-language-date-conversion-for-textmate/#comments</comments>
		<pubDate>Sun, 20 Feb 2011 22:31:20 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[experiments]]></category>
		<category><![CDATA[natural language]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[textmate]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=1962</guid>
		<description><![CDATA[<p>I’ve been flirting with a plain-text to-do system again. The biggest problem for me is that a plain text system opens up so many possibilities for fiddling and scripting. I always end up fiddling more than working when I try it. I don’t see any reason this time would be different, but it’s Sunday and I have some extra time&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/natural-language-date-conversion-for-textmate/">Natural language date conversion for TextMate</a></p>]]></description>
			<content:encoded><![CDATA[<p><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/02/natural_language_examples.jpg?9d7bd4" class="alignright shadow" />I’ve been flirting with a plain-text to-do system again. The biggest problem for me is that a plain text system opens up so many possibilities for fiddling and scripting. I always end up fiddling more than working when I try it. I don’t see any reason this time would be different, but it’s Sunday and I have some extra time on my hands after being snowed in today. So I’m fiddling.</p>

<p>In the process, I wrote a quick <a href="http://macromates.com/">TextMate</a> command which lets me enter a date in natural language and have it converted to whatever format I need for my system. It has variables at the top of the command for defining the date format and output string template. I packaged it up in its own little bundle for people to play with. The command is similar to what I built as a <a href="http://brettterpstra.com/a-few-scripts-for-taskpaper-users/" title="A few scripts for TaskPaper users - Brett Terpstra">TaskPaper script</a> a while back. Just <a href="#download">download</a> at the end of the post, unzip and double-click to install.</p>

<p><span id="more-1962"></span></p>

<h2>Customizing</h2>

<p>The output format of the command defaults to a full date in the system’s preferred format. This makes it easy to see the effect of various strings. For most purposes, you’ll want something more along the lines of “2011–02-22″ instead. To change the format, you just need to edit the <code>dateformat</code> variable at the top of the command’s script.</p>

<p>To edit the command, just go to <strong>Bundles &gt; Bundle Editor &gt; Show Bundle Editor</strong> and find the Date Entry bundle. Select the <strong>Natural Date Entry</strong> command and edit it in the field on the right.</p>

<h3>Date Format</h3>

<p>There’s a commented-out <code>dateformat</code> replacement above the current variable, and you can modify it to fit whatever you need. Don’t forget to comment out or remove the current variable if you uncomment the one above it. The % format elements use Ruby’s <a href="http://www.ruby-doc.org/core/classes/Time.src/M000392.html" title="strftime (Time) [ruby-doc.org]">strftime format</a>. I built the command to work with <a href="http://skiadas.dcostanet.net/afterthought/2006/06/25/details-on-the-gtdalt-bundle/">GTDAlt</a> and <a href="http://www.hogbaysoftware.com/products/taskpaper">TaskPaper</a>, and on my system both use the YYYY-MM-DD format, so that’s what the secondary option is set to.</p>

<h3>Output Template</h3>

<p>To be practical, you probably also need to output some delimiters with the date. In GTDAlt, it would look like “due:[2011–02-22]”, and in TaskPaper it would look like “@due(2010–02-22)”. You can set this up using the <code>outputtemplate</code> variable below <code>dateformat</code>. It uses <code>%date%</code> as a placeholder which will be replaced with the date in the format you specified above. As an example, if you wanted to use the command with TaskPaper files, you would set it to <code>'@due(%date%)'</code>.</p>

<h3>Usage</h3>

<p>The command uses Command-Shift-D by default, but you can change that to whatever’s clever when you’re in the Bundle Editor. When run, it pops up an input dialog courtesy of TextMate’s built-in dialog system. You can enter text in an intuitive natural language format and it will output the date based on the variables you’ve defined. Here are some example strings:</p>

<ul>
<li>tomorrow</li>
<li>tomorrow 3pm</li>
<li>wednesday</li>
<li>wednesday morning</li>
<li>wed</li>
<li>next thursday</li>
<li>in one week</li>
<li>in three days</li>
<li>in 3 days</li>
</ul>

<p>You get the idea. I also set it up to take “+” modifiers. You can add any number of days at the end of a string, or just use the plus modifier to add days to today. For example:</p>

<ul>
<li>thu+7</li>
<li>+2</li>
<li>tomorrow + 3</li>
</ul>

<p>It will roll with any of those punches. Abbreviated days have to be three characters, i.e. mon, tue, wed, thu, fri, sat, or sun.</p>

<h2>Moving the command</h2>

<p>This command has to be distributed in a bundle because it requires the Ruby library <a href="http://rubyforge.org/projects/chronic/" title="RubyForge: Chronic: Project Info">“Chronic”</a> for the date conversion. The library is included in the Support/lib folder of the bundle. If you happen to have RubyGems installed, you can install the Chronic gem (<code>sudo gem install chronic</code>) and then modify the command to allow you to put it wherever you like without needing the support folder at all. To make the command use the gem instead of the bundled library, change the require statements in the command to look like this:</p>


<div class="wp_syntax"><div class="code"><pre class="ruby">SUPPORT = ENV<span class="br0">&#91;</span><span class="st0">'TM_SUPPORT_PATH'</span><span class="br0">&#93;</span>
BSUPPORT = ENV<span class="br0">&#91;</span><span class="st0">'TM_BUNDLE_SUPPORT'</span><span class="br0">&#93;</span>
DIALOG = ENV<span class="br0">&#91;</span><span class="st0">'DIALOG'</span><span class="br0">&#93;</span>
<span class="kw3">require</span> SUPPORT <span class="sy0">+</span> <span class="st0">'/lib/ui'</span>
<span class="kw3">require</span> SUPPORT <span class="sy0">+</span> <span class="st0">'/lib/exit_codes'</span>
<span class="kw3">require</span> <span class="st0">'rubygems'</span>
<span class="kw3">require</span> <span class="st0">'chronic'</span></pre></div></div>


<p>It would also be pretty easy to port to any other system, including an OS X system service. You’d just need to make sure you include the Chronic library, either bundled or install the gem, and find a way to get the text input without using TextMate’s dialog. <a href="http://cocoadialog.sourceforge.net/">CocoaDialog</a> or a service that acts on selected text would fit the bill.</p>

<p>I think this will be handy for more than just to-do lists. Let me know if you find a use for it elsewhere!</p>

<p><em>Man, it took me twice as long to write that post as it did to put together the command and the bundle. Like I said: Sunday.</em></p>

<h2>Download</h2>

<div class="download_desc"><p class="download-icon"><a href="http://brettterpstra.com/downloads/DateEntryBundle.zip?9d7bd4" title="Download Date Conversion for TextMate (87)"><img src="http://cdn2.brettterpstra.com/wp-content/uploads/downloads/thumbnails/2010/06/TextMateIcon.png?9d7bd4" alt="download image for Date Conversion for TextMate" width="64" /></a><br /><a href="http://brettterpstra.com/downloads/DateEntryBundle.zip?9d7bd4" title="Download Date Conversion for TextMate (87)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/DateEntryBundle.zip?9d7bd4" title="Download Date Conversion for TextMate (87)">Date Conversion for TextMate</a> — A TextMate bundle which contains one command: Natural Date Entry. When triggered, it requests a natural-language date string (e.g. “tomorrow 3pm”), converts it to a configurable date format, and outputs it with a customizable template. <a href="">More Info</a></p></div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/natural-language-date-service-update/' rel='bookmark' title='Natural Language Date Service update'>Natural Language Date Service update</a></li>
<li><a href='http://brettterpstra.com/os-x-service-for-natural-language-dates/' rel='bookmark' title='OS X Service for natural language dates'>OS X Service for natural language dates</a></li>
<li><a href='http://brettterpstra.com/natural-language-dates-for-textexpander/' rel='bookmark' title='Natural language dates for TextExpander'>Natural language dates for TextExpander</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/natural-language-date-conversion-for-textmate/">Natural language date conversion for TextMate</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/natural-language-date-conversion-for-textmate/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Dammit. Again with the Lipsum.</title>
		<link>http://brettterpstra.com/dammit-again-with-the-lipsum/</link>
		<comments>http://brettterpstra.com/dammit-again-with-the-lipsum/#comments</comments>
		<pubDate>Sun, 06 Feb 2011 16:51:56 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[experiments]]></category>
		<category><![CDATA[lorem ipsum]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[textexpander]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=1809</guid>
		<description><![CDATA[<p>I know, I said I was done with the Lorem Ipsum generators. Then Dr. Drang responded with a brilliant solution which doesn’t require Internet access to generate some beautiful dummy text. I set it up and ran it myself, and loved the results. Then I found myself wanting to expand it to do more, such as multiple paragraphs, list items&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/dammit-again-with-the-lipsum/">Dammit. Again with the Lipsum.</a></p>]]></description>
			<content:encoded><![CDATA[<p>I know, I <a href="http://brettterpstra.com/textexpander-lipsum-with-kwisatz-haderach/">said I was done</a> with the Lorem Ipsum generators. Then <a href="http://www.leancrew.com/all-this/2011/02/dissociated-darwin/">Dr. Drang responded</a> with a brilliant solution which doesn’t require Internet access to generate some beautiful dummy text. I set it up and ran it myself, and loved the results. Then I found myself wanting to expand it to do more, such as multiple paragraphs, list items and other things I use regularly when making dummy layouts. The problem is that I’m only good for one-liners in Perl, and didn’t want to take take the time to learn what I needed to in order to make the modifications. So I redid it in Ruby.<sup id="fnref:fn1"><a href="#fn:fn1" rel="footnote">1</a></sup></p>

<p>I used a Gem called <a href="https://github.com/postmodern/raingrams">Raingrams</a>, along with <a href="https://github.com/nex3/maruku">Maruku</a> for processing Markdown to HTML. See, if I had it output Markdown to begin with, I had the flexibility to do both easily. I’m posting the main script, which you can modify to output various elements and amounts of text. Even if you don’t know ruby, you can probably find your way around with a little guessing. In the section after the function definitions you can take any of the lines and output what you want by mixing or removing those elements.</p>

<p><span id="more-1809"></span>
This script requires Rubygems, and the Raingrams and Maruku gems. If you have Rubygems installed, you can just type <code>sudo gem install raingrams</code> and <code>sudo gem install maruku</code> at the command line to quickly get both on your system. If you don’t have Rubygems, you might want to skip this one, as you’re in for more trouble than this will pay off…</p>

<p>You’ll also need a text file to pull from. I’m using a chopped up version of 1984, with just enough paragraphs to get some good randomness. You can <a href="http://brettterpstra.com/share/1984trunc.txt">find it here</a>, if you want it. Otherwise, choose your favorite text file and make it long enough to be random and short enough to be fast. That might take some experimentation. Put the text in a file called <code>inputtext.txt</code> in a directory called <code>words</code> in the root of your home folder. The final path should look like <code>/Users/[YOUR USER NAME]/words/inputtext.txt</code>.</p>

<h3>The big script</h3>

<p>The script should have enough comments to make it relatively easy to dissect and rebuild as a TextExpander snippet for any individual part of it.</p>


<div class="wp_syntax"><div class="code"><pre class="ruby"><span class="co1">#!/usr/bin/env ruby -rjcode -Ku</span>
&nbsp;
<span class="kw3">require</span> <span class="st0">'rubygems'</span>
<span class="kw3">require</span> <span class="st0">'raingrams'</span>
<span class="kw3">require</span> <span class="st0">'maruku'</span>
&nbsp;
<span class="kw1">include</span> Raingrams
&nbsp;
<span class="co1"># All (min,max) pairs need max &gt; min</span>
&nbsp;
<span class="re1">@model</span> = BigramModel.<span class="me1">build</span> <span class="kw1">do</span> <span class="sy0">|</span>model<span class="sy0">|</span>
  model.<span class="me1">train_with_text</span> <span class="kw4">File</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="kw4">File</span>.<span class="me1">expand_path</span><span class="br0">&#40;</span><span class="st0">'~/words/inputtext.txt'</span><span class="br0">&#41;</span>,<span class="st0">'r'</span><span class="br0">&#41;</span>.<span class="me1">read</span>
<span class="kw1">end</span>
&nbsp;
<span class="re1">@model</span>.<span class="me1">refresh</span>
&nbsp;
<span class="co1"># generates a paragraph with random sentences</span>
<span class="co1"># min = minimum sentences</span>
<span class="co1"># max = maximum sentences</span>
<span class="kw1">def</span> graf<span class="br0">&#40;</span>min,max<span class="br0">&#41;</span> 
  <span class="co1"># grab the paragraph and split it into words</span>
  para = <span class="re1">@model</span>.<span class="me1">random_paragraph</span><span class="br0">&#40;</span><span class="br0">&#123;</span>:min_sentences <span class="sy0">=&gt;</span> min,:max_sentences <span class="sy0">=&gt;</span> max <span class="br0">&#125;</span><span class="br0">&#41;</span>.<span class="kw3">split</span><span class="br0">&#40;</span><span class="st0">' '</span><span class="br0">&#41;</span>
  <span class="co1"># add a random italics element</span>
  em = <span class="br0">&#40;</span><span class="kw3">rand</span><span class="br0">&#40;</span>para.<span class="me1">count</span> <span class="sy0">-</span> <span class="nu0">10</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="nu0">10</span><span class="br0">&#41;</span>
  para<span class="br0">&#91;</span>em<span class="br0">&#93;</span> = <span class="st0">&quot;*#{para[em]}*&quot;</span>
  <span class="co1"># add a random bold element</span>
  strong = <span class="br0">&#40;</span><span class="kw3">rand</span><span class="br0">&#40;</span>para.<span class="me1">count</span> <span class="sy0">-</span> <span class="nu0">10</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="nu0">10</span><span class="br0">&#41;</span>
  <span class="co1"># make sure they don't overlap</span>
  strong = strong <span class="sy0">-</span> <span class="nu0">2</span> <span class="kw1">if</span> strong == em
  para<span class="br0">&#91;</span>strong<span class="br0">&#93;</span> = <span class="st0">&quot;**#{para[strong]}**&quot;</span>
  <span class="co1"># add a multi-word link</span>
  link = <span class="br0">&#40;</span><span class="kw3">rand</span><span class="br0">&#40;</span>para.<span class="me1">count</span> <span class="sy0">-</span> <span class="nu0">10</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="nu0">10</span><span class="br0">&#41;</span>
  linkend = link <span class="sy0">+</span> <span class="br0">&#40;</span><span class="kw3">rand</span><span class="br0">&#40;</span><span class="nu0">6</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="nu0">2</span><span class="br0">&#41;</span>
  para<span class="br0">&#91;</span>link<span class="br0">&#93;</span> = <span class="st0">&quot;[#{para[link]}&quot;</span>
  para<span class="br0">&#91;</span>linkend<span class="br0">&#93;</span> = <span class="st0">&quot;#{para[linkend]}](http://dummy.com)&quot;</span>
  <span class="kw2">return</span> para.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">' '</span><span class="br0">&#41;</span>
<span class="kw1">end</span>
&nbsp;
<span class="co1"># returns a random sentence, used in headlines</span>
<span class="co1"># min = minumum words, max = max words</span>
<span class="kw1">def</span> sentence<span class="br0">&#40;</span>min,max<span class="br0">&#41;</span> 
  <span class="kw2">return</span> <span class="re1">@model</span>.<span class="me1">random_sentence</span>.<span class="kw3">split</span><span class="br0">&#40;</span><span class="st0">' '</span><span class="br0">&#41;</span><span class="br0">&#91;</span><span class="nu0">0</span>..<span class="br0">&#40;</span><span class="kw3">rand</span><span class="br0">&#40;</span>max <span class="sy0">-</span> min<span class="br0">&#41;</span><span class="sy0">+</span>min<span class="br0">&#41;</span><span class="br0">&#93;</span>.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">' '</span><span class="br0">&#41;</span>
<span class="kw1">end</span>
&nbsp;
<span class="co1"># returns a random list</span>
<span class="co1"># type = ul or ol</span>
<span class="co1"># min = minimum number of list items</span>
<span class="co1"># max = maximum number of list items</span>
<span class="kw1">def</span> list<span class="br0">&#40;</span>type,min,max<span class="br0">&#41;</span>
  list = <span class="st0">''</span>;
  prefix = type == <span class="st0">&quot;ol&quot;</span> ? <span class="st0">&quot; 1. &quot;</span> : <span class="st0">&quot; * &quot;</span>
  <span class="br0">&#40;</span><span class="kw3">rand</span><span class="br0">&#40;</span>max <span class="sy0">-</span> min<span class="br0">&#41;</span> <span class="sy0">+</span> min<span class="br0">&#41;</span>.<span class="me1">times</span> <span class="kw1">do</span>
    list <span class="sy0">+</span>= prefix <span class="sy0">+</span> <span class="re1">@model</span>.<span class="me1">random_gram</span>.<span class="me1">to_s</span> <span class="sy0">+</span> <span class="st0">&quot;<span class="es0">\n</span>&quot;</span>
  <span class="kw1">end</span>
  list <span class="sy0">+</span>= <span class="st0">&quot;<span class="es0">\n</span><span class="es0">\n</span>&quot;</span>
  <span class="kw2">return</span> list
<span class="kw1">end</span>
&nbsp;
<span class="co1"># Sequentially builds an output variable (o)</span>
<span class="co1"># Chop this apart to make snippets as needed</span>
&nbsp;
<span class="co1"># Level 1 headline</span>
o = <span class="st0">&quot;# &quot;</span> <span class="sy0">+</span> sentence<span class="br0">&#40;</span><span class="nu0">2</span>,<span class="nu0">5</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="st0">&quot;<span class="es0">\n</span><span class="es0">\n</span>&quot;</span>
<span class="co1"># 2 medium paragraphs</span>
<span class="nu0">2</span>.<span class="me1">times</span> <span class="kw1">do</span>
  o <span class="sy0">+</span>= <span class="st0">&quot;#{graf(4,6)}<span class="es0">\n</span><span class="es0">\n</span>&quot;</span>
<span class="kw1">end</span>
<span class="co1"># Level 2 headline</span>
o <span class="sy0">+</span>= <span class="st0">&quot;## &quot;</span> <span class="sy0">+</span> sentence<span class="br0">&#40;</span><span class="nu0">4</span>,<span class="nu0">7</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="st0">&quot;<span class="es0">\n</span><span class="es0">\n</span>&quot;</span>
<span class="co1"># 1 short paragraph</span>
o <span class="sy0">+</span>= graf<span class="br0">&#40;</span><span class="nu0">2</span>,<span class="nu0">4</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="st0">&quot;<span class="es0">\n</span><span class="es0">\n</span>&quot;</span>
<span class="co1"># an unordered list</span>
o <span class="sy0">+</span>= list<span class="br0">&#40;</span><span class="st0">'ul'</span>,<span class="nu0">5</span>,<span class="nu0">8</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="st0">&quot;<span class="es0">\n</span><span class="es0">\n</span>&quot;</span>
<span class="co1"># 1 more long paragraph</span>
o <span class="sy0">+</span>= graf<span class="br0">&#40;</span><span class="nu0">6</span>,<span class="nu0">8</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="st0">&quot;<span class="es0">\n</span><span class="es0">\n</span>&quot;</span>
<span class="co1"># Level 3 header</span>
o <span class="sy0">+</span>= <span class="st0">&quot;### &quot;</span> <span class="sy0">+</span> sentence<span class="br0">&#40;</span><span class="nu0">5</span>,<span class="nu0">9</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="st0">&quot;<span class="es0">\n</span><span class="es0">\n</span>&quot;</span>
<span class="co1"># medium paragraph</span>
o <span class="sy0">+</span>= graf<span class="br0">&#40;</span><span class="nu0">4</span>,<span class="nu0">6</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="st0">&quot;<span class="es0">\n</span><span class="es0">\n</span>&quot;</span>
<span class="co1"># ordered list</span>
o <span class="sy0">+</span>= list<span class="br0">&#40;</span><span class="st0">'ol'</span>,<span class="nu0">5</span>,<span class="nu0">8</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="st0">&quot;<span class="es0">\n</span><span class="es0">\n</span>&quot;</span>
&nbsp;
<span class="co1"># Process Markdown to HTML</span>
&nbsp;
<span class="co1"># if you want just the Markdown</span>
<span class="co1"># delete the two lines below</span>
<span class="co1"># and replace with 'puts o'</span>
doc = Maruku.<span class="me1">new</span><span class="br0">&#40;</span>o<span class="br0">&#41;</span>
<span class="kw3">puts</span> doc.<span class="me1">to_html</span>.<span class="kw3">gsub</span><span class="br0">&#40;</span><span class="sy0">/</span>\<span class="sy0">/</span>li<span class="sy0">&gt;</span>\n<span class="sy0">/</span>,<span class="st0">'/li&gt;'</span><span class="br0">&#41;</span>.<span class="kw3">gsub</span><span class="br0">&#40;</span><span class="sy0">/</span>\<span class="sy0">/</span>li<span class="sy0">&gt;&lt;</span>\<span class="sy0">/</span><span class="br0">&#40;</span><span class="br0">&#91;</span>uo<span class="br0">&#93;</span><span class="br0">&#41;</span>l<span class="sy0">/</span>,<span class="st0">&quot;/li&gt;<span class="es0">\n</span>&lt;/<span class="es0">\\</span>1l&quot;</span><span class="br0">&#41;</span>
<span class="co1"># the gsub is just to clean up maruku's double-spaced list output</span></pre></div></div>


<p><a href="http://peg.gd/17Q">Here’s some sample output</a>, generated in <a href="http://brettterpstra.com/code/notational-velocity-alt/">nvALT</a>, using <a href="http://smilesoftware.com/TextExpander/">TextExpander</a> with this script as a shell Snippet, and posted to Peggd. As you can see, it needs some fine tuning. The list items, strong and em could be multi-word, the links could check to see if there was a period-space-capital and prevent overlapping sentences, etc. It’s enough for my purposes, though, and between the script and this post, I’ve lost over an hour of what was supposed to be a productive weekend. I’m out!</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:fn1">
<p>This isn’t a one-up on Dr. Drang; it’s a rough script, and I’m just posting what I wasted half an hour on last night. <a href="#fnref:fn1" rev="footnote">↩</a></p>
</li>

</ol>
</div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/the-quickcal-winners/' rel='bookmark' title='The QuickCal winners'>The QuickCal winners</a></li>
<li><a href='http://brettterpstra.com/random-lipsum-for-textexpander/' rel='bookmark' title='Random Lipsum for TextExpander'>Random Lipsum for TextExpander</a></li>
<li><a href='http://brettterpstra.com/grabbing-a-mac-apps-icon-advanced-bash-usage-2/' rel='bookmark' title='Grabbing a Mac app’s icon: advanced Bash usage'>Grabbing a Mac app’s icon: advanced Bash usage</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/dammit-again-with-the-lipsum/">Dammit. Again with the Lipsum.</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/dammit-again-with-the-lipsum/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>SMS from the command line with Google Voice</title>
		<link>http://brettterpstra.com/sms-from-the-command-line-with-google-voice/</link>
		<comments>http://brettterpstra.com/sms-from-the-command-line-with-google-voice/#comments</comments>
		<pubDate>Sat, 20 Nov 2010 05:54:41 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[google voice]]></category>
		<category><![CDATA[hacks]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[terminal]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=1344</guid>
		<description><![CDATA[<p>I needed a script send an SMS today, and I found a very handy post at sudocode to send one via Google Voice, using PHP. I wanted to make it a little more command-line-friendly, so I rewrote it (ham-handedly) in Ruby and added some options parsing to it. It’s designed for—and only tested on—OS X, but may work fine elsewhere.&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/sms-from-the-command-line-with-google-voice/">SMS from the command line with Google Voice</a></p>]]></description>
			<content:encoded><![CDATA[<p>I needed a script send an SMS today, and I found a very <a href="http://sudocode.net/article/66/sending-a-google-voice-sms-using-php/">handy post at sudocode</a> to send one via <a href="https://www.google.com/voice/">Google Voice</a>, using PHP. I wanted to make it a little more command-line-friendly, so I rewrote it (ham-handedly) in Ruby and added some options parsing to it. It’s designed for—and only tested on—OS X, but may work fine elsewhere.</p>

<p><span id="more-1344"></span></p>

<h3>The code</h3>

<p>To use it, copy the code below into a text file, save it as voicesms.rb (or download it directly using the link at the top of the code), and run <code>chmod a+x voicesms.rb</code> from the command line to make it executable.</p>

<div class="collapse">


<div class="wp_syntax"><div class="code"><pre class="ruby"><span class="co1">#!/usr/bin/env ruby -rjcode -Ku</span>
&nbsp;
<span class="kw3">require</span> <span class="st0">'net/http'</span>
<span class="kw3">require</span> <span class="st0">'net/https'</span>
<span class="kw3">require</span> <span class="st0">'open-uri'</span>
<span class="kw3">require</span> <span class="st0">'cgi'</span>
<span class="kw3">require</span> <span class="st0">'optparse'</span>
&nbsp;
ACCOUNT = <span class="st0">''</span> <span class="co1"># Set to Google Voice account email for default account</span>
PASSWORD = <span class="st0">''</span> <span class="co1"># Set to Google Voice account password for default account</span>
NUMBERS = <span class="br0">&#91;</span><span class="st0">'+1555444333'</span>,<span class="st0">'+1555444222'</span><span class="br0">&#93;</span> <span class="co1"># Set one or more numbers for default destination(s)</span>
&nbsp;
options = <span class="br0">&#123;</span><span class="br0">&#125;</span>
optparse = OptionParser.<span class="me1">new</span> <span class="kw1">do</span><span class="sy0">|</span>opts<span class="sy0">|</span>
  opts.<span class="me1">banner</span> = <span class="st0">&quot;Usage: voicesms.rb -n +15554443333[,+15554442222] -m <span class="es0">\&quot;</span>Message to send<span class="es0">\&quot;</span> [-u Username:Password]&quot;</span>
&nbsp;
  options<span class="br0">&#91;</span><span class="re3">:numbers</span><span class="br0">&#93;</span> = NUMBERS
  opts.<span class="me1">on</span><span class="br0">&#40;</span> <span class="st0">'-n'</span>, <span class="st0">'--numbers NUM[,NUM]'</span>, <span class="st0">'Phone numbers to SMS (separate multiples with comma)'</span> <span class="br0">&#41;</span> <span class="kw1">do</span><span class="sy0">|</span>numbers<span class="sy0">|</span>
    options<span class="br0">&#91;</span><span class="re3">:numbers</span><span class="br0">&#93;</span> = numbers.<span class="kw3">split</span><span class="br0">&#40;</span><span class="st0">','</span><span class="br0">&#41;</span>
  <span class="kw1">end</span>
&nbsp;
  options<span class="br0">&#91;</span><span class="re3">:message</span><span class="br0">&#93;</span> = <span class="kw2">false</span>
  opts.<span class="me1">on</span><span class="br0">&#40;</span> <span class="st0">'-m'</span>, <span class="st0">'--message MESSAGE'</span>, <span class="st0">'Message to send'</span> <span class="br0">&#41;</span> <span class="kw1">do</span><span class="sy0">|</span>msg<span class="sy0">|</span>
    options<span class="br0">&#91;</span><span class="re3">:message</span><span class="br0">&#93;</span> = msg
  <span class="kw1">end</span>
&nbsp;
   options<span class="br0">&#91;</span><span class="re3">:username</span><span class="br0">&#93;</span> = ACCOUNT
   options<span class="br0">&#91;</span><span class="re3">:password</span><span class="br0">&#93;</span> = PASSWORD
   opts.<span class="me1">on</span><span class="br0">&#40;</span> <span class="st0">'-u'</span>, <span class="st0">'--user USERNAME:PASSWORD'</span>, <span class="st0">'Google Voice username and password'</span> <span class="br0">&#41;</span> <span class="kw1">do</span><span class="sy0">|</span>creds<span class="sy0">|</span>
     parts = creds.<span class="kw3">split</span><span class="br0">&#40;</span><span class="st0">':'</span><span class="br0">&#41;</span>
     options<span class="br0">&#91;</span><span class="re3">:username</span><span class="br0">&#93;</span> = parts<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>
     options<span class="br0">&#91;</span><span class="re3">:password</span><span class="br0">&#93;</span> = parts<span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span>
   <span class="kw1">end</span>
&nbsp;
  opts.<span class="me1">on</span><span class="br0">&#40;</span> <span class="st0">'-h'</span>, <span class="st0">'--help'</span>, <span class="st0">'Display this screen'</span> <span class="br0">&#41;</span> <span class="kw1">do</span>
    <span class="kw3">puts</span> opts
    <span class="kw3">exit</span>
  <span class="kw1">end</span>
<span class="kw1">end</span>
&nbsp;
optparse.<span class="me1">parse</span>!
&nbsp;
<span class="kw1">unless</span> options<span class="br0">&#91;</span><span class="re3">:message</span><span class="br0">&#93;</span>
  <span class="kw3">puts</span> <span class="st0">&quot;Message required. Use -m <span class="es0">\&quot;</span>MESSAGE<span class="es0">\&quot;</span>.&quot;</span>
  <span class="kw3">puts</span> <span class="st0">&quot;Enter `voicesms.rb -h` for help.&quot;</span>
  <span class="kw3">exit</span>
<span class="kw1">end</span>
&nbsp;
<span class="kw1">def</span> postit<span class="br0">&#40;</span>uri_str, data, header = <span class="kw2">nil</span>, limit = <span class="nu0">3</span><span class="br0">&#41;</span>
    <span class="kw3">raise</span> <span class="kw4">ArgumentError</span>, <span class="st0">'HTTP redirect too deep'</span> <span class="kw1">if</span> limit == <span class="nu0">0</span>
    url = <span class="kw4">URI</span>.<span class="me1">parse</span><span class="br0">&#40;</span>uri_str<span class="br0">&#41;</span>
    http = <span class="re2">Net::HTTP</span>.<span class="me1">new</span><span class="br0">&#40;</span>url.<span class="me1">host</span>,<span class="nu0">443</span><span class="br0">&#41;</span>
    http.<span class="me1">use_ssl</span> = <span class="kw2">true</span>
    http.<span class="me1">verify_mode</span> = <span class="re2">OpenSSL::SSL::VERIFY_NONE</span>
    response,content = http.<span class="me1">post</span><span class="br0">&#40;</span>url.<span class="me1">path</span>,data,header<span class="br0">&#41;</span>
    <span class="kw1">case</span> response
      <span class="kw1">when</span> <span class="re2">Net::HTTPSuccess</span>     <span class="kw1">then</span> content
      <span class="kw1">when</span> <span class="re2">Net::HTTPRedirection</span> <span class="kw1">then</span> postit<span class="br0">&#40;</span>response<span class="br0">&#91;</span><span class="st0">'location'</span><span class="br0">&#93;</span>,data,header, limit <span class="sy0">-</span> <span class="nu0">1</span><span class="br0">&#41;</span>
      <span class="kw1">else</span>
        <span class="kw3">puts</span> response.<span class="me1">inspect</span>
        response.<span class="me1">error</span>!
    <span class="kw1">end</span>
<span class="kw1">end</span>
&nbsp;
<span class="kw1">def</span> getit<span class="br0">&#40;</span>uri_str, header, limit = <span class="nu0">3</span><span class="br0">&#41;</span>
    <span class="kw3">raise</span> <span class="kw4">ArgumentError</span>, <span class="st0">'HTTP redirect too deep'</span> <span class="kw1">if</span> limit == <span class="nu0">0</span>
    url = <span class="kw4">URI</span>.<span class="me1">parse</span><span class="br0">&#40;</span>uri_str<span class="br0">&#41;</span>
    http = <span class="re2">Net::HTTP</span>.<span class="me1">new</span><span class="br0">&#40;</span>url.<span class="me1">host</span>,url.<span class="me1">port</span><span class="br0">&#41;</span>
    http.<span class="me1">use_ssl</span> = <span class="kw2">true</span>
    http.<span class="me1">verify_mode</span> = <span class="re2">OpenSSL::SSL::VERIFY_NONE</span>
    response,content = http.<span class="me1">get</span><span class="br0">&#40;</span>url.<span class="me1">path</span>,header<span class="br0">&#41;</span>
    <span class="kw1">case</span> response
      <span class="kw1">when</span> <span class="re2">Net::HTTPSuccess</span>     <span class="kw1">then</span> content
      <span class="kw1">when</span> <span class="re2">Net::HTTPRedirection</span> <span class="kw1">then</span> getit<span class="br0">&#40;</span>response<span class="br0">&#91;</span><span class="st0">'location'</span><span class="br0">&#93;</span>,header, limit <span class="sy0">-</span> <span class="nu0">1</span><span class="br0">&#41;</span>
      <span class="kw1">else</span>
        response.<span class="me1">error</span>!
    <span class="kw1">end</span>
<span class="kw1">end</span>
&nbsp;
data = <span class="st0">&quot;accountType=GOOGLE&amp;Email=#{options[:username]}&amp;Passwd=#{options[:password]}&amp;service=grandcentral&amp;source=brettterpstra-CLISMS-2.0&quot;</span>
res = postit<span class="br0">&#40;</span><span class="st0">'https://www.google.com/accounts/ClientLogin'</span>,data<span class="br0">&#41;</span>
<span class="kw1">if</span> res
  authcode = res.<span class="me1">match</span><span class="br0">&#40;</span><span class="sy0">/</span>Auth=<span class="br0">&#40;</span>.<span class="sy0">+</span><span class="br0">&#41;</span><span class="sy0">/</span><span class="br0">&#41;</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span>
  header = <span class="br0">&#123;</span><span class="st0">'Authorization'</span> <span class="sy0">=&gt;</span> <span class="st0">&quot;GoogleLogin auth=#{authcode.strip}&quot;</span>,<span class="st0">'Content-Length'</span> <span class="sy0">=&gt;</span> <span class="st0">'0'</span><span class="br0">&#125;</span>
  newres = getit<span class="br0">&#40;</span><span class="st0">'https://www.google.com/voice'</span>,header<span class="br0">&#41;</span>
  <span class="kw1">if</span> newres
    rnrse = newres.<span class="me1">match</span><span class="br0">&#40;</span><span class="sy0">/</span><span class="st0">'_rnr_se'</span>: <span class="st0">'([^'</span><span class="br0">&#93;</span><span class="sy0">+</span><span class="br0">&#41;</span><span class="st0">'/)[1]
    options[:numbers].each {|num|
      data = &quot;_rnr_se=#{rnrse}&amp;phoneNumber=#{num.strip}&amp;text=#{CGI.escape(options[:message])}&amp;id=&quot;
      finalres = postit('</span>https:<span class="sy0">//</span>www.<span class="me1">google</span>.<span class="me1">com</span><span class="sy0">/</span>voice<span class="sy0">/</span>sms<span class="sy0">/</span>send<span class="sy0">/</span><span class="st0">',data,header)
      if finalres[&quot;ok&quot;]
        puts &quot;Message sent to #{num}&quot;
      else
        puts &quot;Error sending to #{num}&quot;
      end
    }
  else
    newres.error!
  end
else
  res.error!
end</span></pre></div></div>



</div>

<h3>Usage</h3>

<p>You can set default account name, password and send-to numbers at the top of the script. These can be overridden by command line options at runtime. The standard syntax is:</p>

<pre><code>voicesms.rb -n +15552525543,+15554342221 -m "The message to send" -u GVoiceUsername:GVoicePassword
</code></pre>

<p>Parameters can be in any order. The –n parameter takes one or more phone numbers in international format (+XX country code at the beginning, +1 for US numbers), separated by commas (no spaces).</p>

<p>The –m option (message) is required and can’t be set by default in the script. Just use –m “and a quoted message” and it will handle the rest.</p>

<p>–u defines a username/password combination for Google Voice. It’s probably most convenient to set these at the top of the script and ignore this parameter, but the option is there.</p>

<p>–h will provide the help information.</p>

<h4>Aliasing</h4>

<p>The point of the script is to let me automate SMS messages from my system, so its primary invocation will be from other scripts. However, it will function just fine as a command line texting utility, in which case you’d probably want to alias the core functions in your <code>.bash_profile</code>. Set your default username and password in the script, and maybe a default destination number. If you want to send to a different numbers, you might want to make several aliases which include different <code>-n #</code> parameters, one for each destination. The alias will look something like:</p>

<pre><code>alias sms="~/scripts/voicesms.rb -m"
</code></pre>

<p>Or, maybe:</p>

<pre><code>alias smsjohn="~/scripts/voicesms.rb -n +15554443333 -m"
</code></pre>

<p>Get it? Then you can just type <code>smsjohn "And your message"</code> to send the message straight to John. Whoever that is.</p>

<h3>What it does</h3>

<p>It’s pretty simple, just a series of POST and GET requests to the Google API. It uses Google’s client authentication to get an initial auth code. Then it uses the auth code to get an auth token. Then, it posts your information to the API with the proper headers (the auth token) to complete the call. All of your data is sent securely over SSL (https) connections.</p>

<p>Have fun!</p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/single-keystroke-instapaper-in-google-reader/' rel='bookmark' title='Single-keystroke Instapaper in Google Reader'>Single-keystroke Instapaper in Google Reader</a></li>
<li><a href='http://brettterpstra.com/gvoice-command-line-sms-revisited/' rel='bookmark' title='GVoice command line SMS revisited'>GVoice command line SMS revisited</a></li>
<li><a href='http://brettterpstra.com/greader-instapaper-0-2/' rel='bookmark' title='GReader Instapaper 0.2'>GReader Instapaper 0.2</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/sms-from-the-command-line-with-google-voice/">SMS from the command line with Google Voice</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/sms-from-the-command-line-with-google-voice/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>TextExpander: bit.ly with authentication</title>
		<link>http://brettterpstra.com/textexpander-bit-ly-with-authentication/</link>
		<comments>http://brettterpstra.com/textexpander-bit-ly-with-authentication/#comments</comments>
		<pubDate>Mon, 18 Oct 2010 23:56:44 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[bit.ly]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[textexpander]]></category>
		<category><![CDATA[url shortener]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=1114</guid>
		<description><![CDATA[<p>A bit.ly shortening script for TextExpander which allows the use of the authenticated v3 API for click tracking and analytics.</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/textexpander-bit-ly-with-authentication/">TextExpander: bit.ly with authentication</a></p>]]></description>
			<content:encoded><![CDATA[<p>A quick script in response to <a href="http://brettterpstra.com/textexpander-experiments/#comments">Donald’s query</a> on my TextExpander Experiments post. He wanted to know if I could make the bit.ly script—which expands to a bit.ly-shortened version of a url in the clipboard—work with an authenticated account, presumably for click tracking. Well, sure! Just adjust the script below to contain your bit.ly username and your <a href="http://bit.ly/a/your_api_key">API key</a> (in lines 6 &amp; 7), and replace the existing script in the <a href="http://brettterpstra.com/code/?did=6" title="Download the TextExpander Experiments bundle">TextExpander experiments bundle</a> with this one (or create a new snippet with the script).</p>


<div class="wp_syntax"><div class="code"><pre class="ruby"><span class="co1">#!/usr/bin/env ruby -wKU</span>
&nbsp;
<span class="kw3">require</span> <span class="st0">'open-uri'</span>
<span class="kw3">require</span> <span class="st0">'cgi'</span>
&nbsp;
USER_NAME = <span class="st0">'username'</span> <span class="co1"># Your login name</span>
API_KEY = <span class="st0">'yourlongapikey'</span> <span class="co1"># http://bit.ly/a/your_api_key</span>
&nbsp;
<span class="kw1">def</span> entity_escape<span class="br0">&#40;</span>text<span class="br0">&#41;</span>
  text.<span class="kw3">gsub</span><span class="br0">&#40;</span><span class="sy0">/&amp;</span><span class="br0">&#40;</span>?!<span class="br0">&#40;</span><span class="br0">&#91;</span>a<span class="sy0">-</span>zA<span class="sy0">-</span>Z0<span class="sy0">-</span><span class="nu0">9</span><span class="br0">&#93;</span><span class="sy0">+|</span><span class="co1">#[0-9]+|#x[0-9a-fA-F]+);)/, '&amp;amp;')</span>
<span class="kw1">end</span>
&nbsp;
<span class="kw1">def</span> make_link<span class="br0">&#40;</span>text<span class="br0">&#41;</span>
  <span class="kw1">case</span> text
  <span class="kw1">when</span> <span class="sy0">%</span>r<span class="br0">&#123;</span>\Ahttps?:<span class="sy0">//</span>.<span class="sy0">*</span>?\.\w<span class="br0">&#123;</span><span class="nu0">2</span>,<span class="nu0">4</span><span class="br0">&#125;</span>.<span class="sy0">*</span>?\z<span class="br0">&#125;</span>:
    entity_escape<span class="br0">&#40;</span>text<span class="br0">&#41;</span>
  <span class="kw1">when</span> <span class="sy0">%</span>r<span class="br0">&#123;</span>\A<span class="br0">&#40;</span>www\..<span class="sy0">*|</span>.<span class="sy0">*</span>\.\w<span class="br0">&#123;</span><span class="nu0">2</span>,<span class="nu0">4</span><span class="br0">&#125;</span><span class="br0">&#41;</span>\z<span class="br0">&#125;</span>:
    <span class="st0">&quot;http://#{entity_escape text}&quot;</span>
  <span class="kw1">when</span> <span class="sy0">%</span>r<span class="br0">&#123;</span>\A.<span class="sy0">*</span>?\.\w<span class="br0">&#123;</span><span class="nu0">2</span>,<span class="nu0">4</span><span class="br0">&#125;</span>\<span class="sy0">/</span>?.<span class="sy0">*</span>\z<span class="br0">&#125;</span>:
    <span class="st0">&quot;http://#{entity_escape text}&quot;</span>
  <span class="kw1">else</span>
    <span class="kw2">nil</span>
  <span class="kw1">end</span>
<span class="kw1">end</span>
&nbsp;
url = make_link <span class="sy0">%</span>x<span class="br0">&#123;</span>__CF_USER_TEXT_ENCODING=$UID:0x8000100:0x8000100 pbpaste<span class="br0">&#125;</span>.<span class="me1">strip</span>
res = <span class="kw3">open</span><span class="br0">&#40;</span><span class="st0">&quot;http://api.bit.ly/v3/shorten?login=#{USER_NAME}&amp;apiKey=#{API_KEY}&amp;longUrl=#{CGI.escape(url)}&amp;format=txt&quot;</span><span class="br0">&#41;</span>.<span class="me1">read</span> <span class="kw1">unless</span> url.<span class="kw2">nil</span>?
&nbsp;
<span class="kw1">begin</span>
  <span class="kw3">print</span> res <span class="kw1">unless</span> res.<span class="kw2">nil</span>?
<span class="kw1">rescue</span>
  <span class="kw3">exit</span>
<span class="kw1">end</span></pre></div></div>


<p>The API can output JSON and XML, which would be more appropriate on most occasions, but the “txt” format (which <em>only</em> includes the shortened link) is perfect for this situation. Enjoy!</p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/textexpander-experiments/' rel='bookmark' title='TextExpander experiments'>TextExpander experiments</a></li>
<li><a href='http://brettterpstra.com/sms-from-the-command-line-with-google-voice/' rel='bookmark' title='SMS from the command line with Google Voice'>SMS from the command line with Google Voice</a></li>
<li><a href='http://brettterpstra.com/a-system-service-for-to-url-shortening/' rel='bookmark' title='A System Service for to. url shortening'>A System Service for to. url shortening</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/textexpander-bit-ly-with-authentication/">TextExpander: bit.ly with authentication</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/textexpander-bit-ly-with-authentication/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Updated: Songza Lucky Link Service</title>
		<link>http://brettterpstra.com/updated-songza-lucky-link-service/</link>
		<comments>http://brettterpstra.com/updated-songza-lucky-link-service/#comments</comments>
		<pubDate>Sun, 21 Mar 2010 03:25:25 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[service]]></category>
		<category><![CDATA[snow leopard]]></category>
		<category><![CDATA[songza]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=338</guid>
		<description><![CDATA[<p>Songza.fm has moved to Songza.org, so I’ve rewritten the old Songza Lucky Link Service for Snow Leopard to match. This new version of the service runs without dependencies, so it should work for any Snow Leopard setup right out of the box. It’s a bit silly, but what it does is take your selected text and run it as a&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/updated-songza-lucky-link-service/">Updated: Songza Lucky Link Service</a></p>]]></description>
			<content:encoded><![CDATA[<p>Songza.fm has moved to Songza.org, so I’ve rewritten the <a href="http://brettterpstra.com/2009/11/12/songza-lucky-link-service/">old Songza Lucky Link Service</a> for Snow Leopard to match. This new version of the service runs without dependencies, so it should work for any Snow Leopard setup right out of the box. It’s a bit silly, but what it does is take your selected text and run it as a query on Songza.org, parse for the first result (if there are any), shorten that link and insert it after your selected text. A fast, easy way to punctuate your obscure music references in emails and on the web. Have fun, and join me in hoping for the Songza API to blossom into something we can really sink our scripts into!</p>

<p>To install, just download the file below, unzip it (double click), and move it into <code>[your username]/Library/Services</code>. To use it, select text in any Cocoa app (Safari, Mail, and most Mac apps) and look under the application menu in the menubar for the Services submenu. Trigger it from there, or just right click on your selected text and you should get the same services menu as a contextual menu item. If you’re feeling clever, go into the Keyboard preferences pane, choose Keyboard Shortcuts -&gt; Services and double click the blank space to the right of the service to assign a hotkey. As long as that hotkey isn’t already assigned by the application your using, you can trigger the service with it any time you have text selected.</p>

<div class="download_desc"><p class="download-icon"><a href="http://brettterpstra.com/downloads/SongzaLuckyLinkSLService.zip?9d7bd4" title="Download Songza Lucky Link Service (83)"><img src="http://brettterpstra.com/wp-content/images/serviceicon.jpg?9d7bd4" alt="download image for Songza Lucky Link Service" width="64" /></a><br /><a href="http://brettterpstra.com/downloads/SongzaLuckyLinkSLService.zip?9d7bd4" title="Download Songza Lucky Link Service (83)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/SongzaLuckyLinkSLService.zip?9d7bd4" title="Download Songza Lucky Link Service (83)">Songza Lucky Link Service</a> — A Snow Leopard Service to generate a shortened url to the first result of a Songza.org search for the selected text. The text will remain, but it will have the result in parenthesis after it. Uses a built-in html parser to scrape the results, at least until Songza provides an API. <a href="http://brettterpstra.com/2010/03/20/updated-songza-lucky-link-service/">More Info</a></p></div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/songza-lucky-link-service/' rel='bookmark' title='Songza Lucky Link Service'>Songza Lucky Link Service</a></li>
<li><a href='http://brettterpstra.com/songza-is-back/' rel='bookmark' title='Songza is back!'>Songza is back!</a></li>
<li><a href='http://brettterpstra.com/auto-link-text-service-updated/' rel='bookmark' title='Auto-link text service updated'>Auto-link text service updated</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/updated-songza-lucky-link-service/">Updated: Songza Lucky Link Service</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/updated-songza-lucky-link-service/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk: basic (Feed is rejected)
Page Caching using disk: enhanced
Database Caching 34/180 queries in 0.712 seconds using xcache
Object Caching 4335/4531 objects using xcache
Content Delivery Network via cdn2.brettterpstra.com

Served from: brettterpstra.com @ 2012-05-23 04:09:02 -->
