<?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 - 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>Fri, 03 Feb 2012 03:11:46 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<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 colorbox-3212" />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 (40)"><img class="colorbox-3212"  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 (40)" 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 (40)">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/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>
<li><a href='http://brettterpstra.com/is-your-url-too-short-try-our-system-free/' rel='bookmark' title='Is your URL too short? Try our system, free!'>Is your URL too short? Try our system, free!</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 colorbox-3177" /></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 colorbox-3157" />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 (140)"><img class="colorbox-3157"  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 (140)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/mm2md" title="Download mindmeister2md (140)">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 colorbox-2879" />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 colorbox-2733"></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>13</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="https://github.com/ttscoff/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="https://github.com/ttscoff/OTask">https://github.com/ttscoff/OTask</a></p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/moving-day/' rel='bookmark' title='Moving day'>Moving day</a></li>
<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/totally-taskpaper/' rel='bookmark' title='Totally TaskPaper'>Totally TaskPaper</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 colorbox-2056" />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 (170)"><img class="colorbox-2056"  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 (170)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/Watcher.zip?9d7bd4" title="Download Watcher Service (170)">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>5</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 colorbox-1962" />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 (75)"><img class="colorbox-1962"  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 (75)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/DateEntryBundle.zip?9d7bd4" title="Download Date Conversion for TextMate (75)">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/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>
<li><a href='http://brettterpstra.com/a-couple-of-textmate-macros-for-markdown-lists/' rel='bookmark' title='A couple of TextMate Macros for Markdown lists'>A couple of TextMate Macros for Markdown lists</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 (80)"><img class="colorbox-338"  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 (80)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/SongzaLuckyLinkSLService.zip?9d7bd4" title="Download Songza Lucky Link Service (80)">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>
		<item>
		<title>A better System Service for Evernote clipping — with MultiMarkdown</title>
		<link>http://brettterpstra.com/a-better-os-x-system-service-for-evernote-notes-with-multimarkdown/</link>
		<comments>http://brettterpstra.com/a-better-os-x-system-service-for-evernote-notes-with-multimarkdown/#comments</comments>
		<pubDate>Sun, 07 Mar 2010 00:02:09 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[evernote]]></category>
		<category><![CDATA[markdown]]></category>
		<category><![CDATA[multimarkdown]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[service]]></category>
		<category><![CDATA[snow leopard]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=302</guid>
		<description><![CDATA[<p>Update: If you’re looking for a Markdown -&#62; Evernote, check this out. Another post, quickly and with less explanation… The fact that Evernote processes HTML so much better than it does plain or rich text got me thinking and tinkering. I use Markdown (actually, MultiMarkdown) constantly, and it does a great job of turning plain text into valid markup. With&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/a-better-os-x-system-service-for-evernote-notes-with-multimarkdown/">A better System Service for Evernote clipping — with MultiMarkdown</a></p>]]></description>
			<content:encoded><![CDATA[<p><strong>Update:</strong> If you’re looking for a Markdown -&gt; Evernote, <a href="http://brettterpstra.com/taking-the-markdown-to-evernote-service-further/">check this out</a>.</p>

<p>Another post, quickly and with less explanation…</p>

<p><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2010/03/EvernoteIcon-300x300.png?9d7bd4" alt="Evernote Icon" title="EvernoteIcon" width="300" height="300" class="alignright size-medium wp-image-305 colorbox-302" />The fact that Evernote processes HTML so much better than it does plain or rich text got me thinking and tinkering. I use <a href="http://daringfireball.net/projects/markdown/">Markdown</a> (actually, <a href="http://fletcherpenney.net/multimarkdown/">MultiMarkdown</a>) constantly, and it does a great job of turning plain text into valid markup. With (Multi)Markdown, even plain text becomes HTML that–when imported into Evernote–retains most of its formatting. To answer your question, no, I’m not obsessed with Evernote, I’m obsessed with problems I think I could solve. It’s unhealthy.</p>

<p><strong><em>Please note</em></strong>, this requires that you have <a href="http://fletcherpenney.net/">Fletcher Penney’s</a> MultiMarkdown installed in <code>~/Library/Application Support/MultiMarkdown</code>, and that the Perl files (MultiMarkdown.pl and SmartyPants.pl) are located in a ‘bin’ subdirectory (which is the default install). If you don’t have MultiMarkdown, you should get it anyway (all the cool kids have it), so head over to the <a href="http://fletcher.github.com/MultiMarkdown/">download page</a> and grab a copy. Now, on with the show.</p>

<p>I set this up originally as a <a href="http://macromates.com/">TextMate</a> command, intending just to be able to clip code snippets and free-form text to Evernote without thinking too much about it. That worked well, so I modified it to work as a System Service. Specifically, a Snow Leopard service, but I’m providing the Ruby script here and it can be modified for any Mac setup you want.</p>

<p>While it will work just fine on plain text with no markup, it does have a couple of “special” features. If you start a line with a <code>#</code> and a space (e.g.: # This is my header), which is a <a href="http://daringfireball.net/projects/markdown/syntax#header">Markdown convention</a> for a first-level heading, it will use that as the title for the note and strip it out of the text in processing. It only uses the first one it finds, but it will strip out any first-level headers in the selection. I’ll probably modify that later, or just have it leave them in. Also, a line that begins with “tags:” followed by a space and a comma-separated list of words will be split up and used to tag the new note. This is also stripped before processing. It handles spaces in multi-word tags, and odd marks at the beginning or end of a tag, <em>but only one punctuation character, and only at the beginning or end of a tag</em>. The code follows…</p>

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

<p>Here’s the Ruby code, messy as it may be:</p>

<div markdown=0>
<pre><code>
    #!/usr/bin/env ruby -rjcode -Ku
    # requires that MultiMarkdown be installed in ~/Library/Application Support/MultiMarkdown
    # That, or edit the script to point to yours :)

    ARGF.each do |f|
    input = f
    contents = &#x27;&#x27;
    tags = &#x27;&#x27;
    title = nil

    def e_as(str)
        str.to_s.gsub(/(?=[&quot;\\])/, &#x27;\\&#x27;)
    end
    input.each_line { |line| 
      if line =~ /^# (.*?)/
        title = line[2..-1]
        break
      end
    }
    title = %x{date &#x27;+Clipped note: %A, %B %d, %Y at %l:%M %p&#x27;|tr -s &quot; &quot;} if title.nil?

    input.each_line { |line| 
      if line =~ /^[Tt]ags: /
        tags = line[5..-1].split(&#x27;,&#x27;).map {|tag| tag = tag.strip.gsub(/^(.)?\b|\b(.)?$/,&quot;\\2\&quot;\\1&quot;) }
        break
      end
    }

    IO.popen(&#x27;&quot;$HOME/Library/Application Support/MultiMarkdown/bin/MultiMarkdown.pl&quot;|&quot;$HOME/Library/Application Support/MultiMarkdown/bin/SmartyPants.pl&quot;&#x27;, &quot;r+&quot;) do |io|
     input.each_line { |line| 
        io &lt;&lt; line unless line =~ /^# |[Tt]ags\: /
     }; io.close_write
     io.each_line do |line|
       contents &lt;&lt; line
     end
    end
    tags = &quot; tags {#{tags.join(&quot;,&quot;)}}&quot; unless tags.empty?
    %x{osascript -e &#x27;tell application &quot;Evernote&quot; to create note with html &quot;#{e_as contents}&quot; title &quot;#{title}&quot; notebook &quot;Unfiled&quot;#{tags}&#x27;}
    end
</code></pre>
</div>

<p>You can create a System Service in Automator with it, set up a command in TextMate, or do whatever else you can think of. If you just want to download the service and try it out, I’ve made it <a href="http://brettterpstra.com/downloads/MarkdownToEvernote.zip?9d7bd4">available here</a>. Unzip and copy it to ~/Library/Services (in your home folder). If you set it up as a System Service, assign a shortcut key in the Keyboard pane of System Preferences.</p>

<p>It does choke once in a while, apparently on Markdown-generated code snippets, but I haven’t quite narrowed down why, yet. I’ll update the code if I figure that one out. Overall, though, it makes pretty clippings and allows you to use some Markdown syntax to spice up your text without having to touch the (regrettably abominable) Evernote editor.</p>

<hr />

<div class="download_desc"><p class="download-icon"><a href="http://brettterpstra.com/downloads/MarkdownToEvernote.zip?9d7bd4" title="Download Markdown To Evernote Service (390)"><img class="colorbox-302"  src="http://brettterpstra.com/wp-content/images/serviceicon.jpg?9d7bd4" alt="download image for Markdown To Evernote Service" width="64" /></a><br /><a href="http://brettterpstra.com/downloads/MarkdownToEvernote.zip?9d7bd4" title="Download Markdown To Evernote Service (390)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/MarkdownToEvernote.zip?9d7bd4" title="Download Markdown To Evernote Service (390)">Markdown To Evernote Service</a> — A Snow Leopard System Service that grabs selected text, processes it with MultiMarkdown and clips the resulting HTML to Evernote, creating a nicely formatted note. Uses the first (and hopefully only) Markdown first-level heading (# My headline) as the title for the note, and will look for a line starting with “tags: ” followed by a comma-separated list of tags as well.

Requires that MultiMarkdown be installed in ~/Library/Application Support/MultiMarkdown. <a href="http://brettterpstra.com/2010/03/06/a-better-os-x-system-service-for-evernote-notes-with-multimarkdown/">More Info</a></p></div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/taking-the-markdown-to-evernote-service-further/' rel='bookmark' title='Taking the Markdown to Evernote service further'>Taking the Markdown to Evernote service further</a></li>
<li><a href='http://brettterpstra.com/evernote-site-memory-tagger-for-wordpress/' rel='bookmark' title='Evernote Site Memory tagger for WordPress'>Evernote Site Memory tagger for WordPress</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>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/a-better-os-x-system-service-for-evernote-notes-with-multimarkdown/">A better System Service for Evernote clipping — with MultiMarkdown</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/a-better-os-x-system-service-for-evernote-notes-with-multimarkdown/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>A (fairly) simple equation evaluation service for Snow Leopard</title>
		<link>http://brettterpstra.com/equation-evaluation-service-for-snow-leopard/</link>
		<comments>http://brettterpstra.com/equation-evaluation-service-for-snow-leopard/#comments</comments>
		<pubDate>Thu, 31 Dec 2009 14:33:05 +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>

		<guid isPermaLink="false">http://brettterpstra.com/?p=215</guid>
		<description><![CDATA[<p>Download the Evaluate Expression Snow Leopard service: EvaluateExpressionService.zip This is a stripped down version of a command I have in the TextMate bundle we use at TUAW. It allows you to select any basic numeric equation and evaluate it, replacing the selected text with the results. It will ignore your text if it contains anything but numbers and basic mathematical&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/equation-evaluation-service-for-snow-leopard/">A (fairly) simple equation evaluation service for Snow Leopard</a></p>]]></description>
			<content:encoded><![CDATA[<p><img style=' float: left; padding: 4px; margin: 0 7px 2px 0; display:none'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2009/12/equationevalheader.jpg?9d7bd4" alt="Expressionevalheader" height="187" width="650" class="alignleft headerimg colorbox-215" />Download the Evaluate Expression Snow Leopard service: <a href="http://brettterpstra.com/share/EvaluateExpressionService.zip?9d7bd4">EvaluateExpressionService.zip</a></p>

<p>This is a stripped down version of a command I have in the <a href="http://macromates.com/">TextMate</a> bundle we use at <a href="http://www.tuaw.com/">TUAW</a>. It allows you to select any basic numeric equation and evaluate it, replacing the selected text with the results. It will ignore your text if it contains anything but numbers and basic mathematical symbols. Sure, there are plenty of ways to do calculations in OS X (<a href="http://www.mactropolis.com/how-tos/leopard-tip-spotlight-calculator/">Spotlight</a>, <a href="http://www.obdev.at/products/launchbar/index.html">Launchbar</a>, <a href="http://www.blacktree.com/">Quicksilver</a>), but I’ve had more and more incidents lately where I just wanted to do quick calculations inline, so I whipped this up. A little explanation…</p>

<p><span id="more-215"></span>
The service is built for Snow Leopard only, but I’m including the code here because it could be wrapped up in <a href="http://wafflesoftware.net/thisservice/">ThisService</a> or <a href="http://www.xendai.com/">Bellhop</a> pretty quickly for Leopard. It’s built in Ruby and uses the eval function to process text such as <code>20*(3.5/2)</code> and return the result. It will add commas to numbers longer than 3 digits, but I stripped out the part of the original command that trimmed decimal places; I figured the accuracy might be important and the results are easy to edit manually in this case. I also added a silly feature that I actually find quite useful: it evaluates +/- percentage strings, such as <code>23.99-15%</code>, which I find most useful for calculating savings during sales or quickly handling markup or tax on services and goods. It can’t get too complex, but for most everyday purposes, it does a good job.</p>

<h3>The code:</h3>

<div markdown="0">
<pre><code>
def ts( st )
  # Adds commas to longer numbers
  # Removes .00 left over from % calculations
  mynum = st.to_s.reverse.scan(/(?:\d*\.)?\d{1,3}-?/).join(',').reverse
  dec = mynum.split('.')
  return dec[0] if dec[1].to_i == 0
  mynum
end

ARGF.each do |f|
    # check if the text passed is actually an equation
    if f.match(/^[\%\$\d\*\/\+\-,\. \(\)]+$/)
        # add a preceding 0 to decimals passed for floating point calculation
        num = f.gsub(/(^|[^\d])(\.\d+)/,'\10\2')
        # convert basic percentage equations for eval
        num = num.gsub(/([\d\.]+)(?:\s+?)?([+-])(?:\s+?)?([\d\.]+)%([^\d]|$)/,'\1\2(\1*(\3.to_f * 0.01))\4')
        # process the result to add commas, trim unnecessary decimal places
        print ts(eval(num).to_s)
    # if it's not an equation, return the input (no effect)
    else
        print f
    end
end
</code></pre>
</div>

<p>You can <a href="http://brettterpstra.com/downloads/EvaluateExpressionService.zip?9d7bd4">download a ready-to-go Snow Leopard service</a>, or build your own with the code above. Don’t forget to use the Keyboard preference panel to assign a keyboard shortcut to it (I’m using Control-Shift-=). I’d love to hear back if you find it useful!</p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/clippable-to-evernote-snow-leopard-service/' rel='bookmark' title='Clippable to Evernote Snow Leopard Service'>Clippable to Evernote Snow Leopard Service</a></li>
<li><a href='http://brettterpstra.com/updated-songza-lucky-link-service/' rel='bookmark' title='Updated: Songza Lucky Link Service'>Updated: Songza Lucky Link Service</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>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/equation-evaluation-service-for-snow-leopard/">A (fairly) simple equation evaluation service for Snow Leopard</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/equation-evaluation-service-for-snow-leopard/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 32/171 queries in 0.804 seconds using xcache
Object Caching 4153/4340 objects using xcache
Content Delivery Network via cdn2.brettterpstra.com

Served from: brettterpstra.com @ 2012-02-04 09:13:22 -->
