<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Jerod Santo</title>
    <link>https://jerodsanto.net/</link>
    <description>Buy the truth, sell it not</description>
    <generator>Hugo</generator>
    <language>en</language>
    
    <lastBuildDate>Tue, 03 Mar 2026 16:00:17 -0600</lastBuildDate>

    
    <item>
      <title>So long, and thanks for all the logs</title>
      <link>https://jerodsanto.net/2026/03/so-long-changelog/</link>
      <pubDate>Tue, 03 Mar 2026 19:09:33 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2026/03/so-long-changelog/</guid>
      
      
      <description><![CDATA[<p>After 13 <a href="https://jerodsanto.net/2013/04/changeloggin/">years</a>, 1042 <a href="https://changelog.com/person/jerodsanto">podcasts</a>, 452 newsletters, and countless friends made along the way&hellip; it&rsquo;s time to say goodbye to The Changelog. I shipped my <a href="https://changelog.com/news/182">final News</a> last Monday and Adam shipped our <a href="https://changelog.com/friends/129">Friends finale</a> yesterday.</p>
<p>For reasons I cannot explain, during &ldquo;big change&rdquo; moments like this my brain shoots clichés at me like Nitro fired tennis balls at approaching American Gladiator contenders.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<blockquote>
<p>&ldquo;Parting is such sweet sorrow&rdquo;</p>
</blockquote>
<blockquote>
<p>&ldquo;All good things must come to an end&rdquo;</p>]]></description>
      
      <content:encoded><![CDATA[<p>After 13 <a href="https://jerodsanto.net/2013/04/changeloggin/">years</a>, 1042 <a href="https://changelog.com/person/jerodsanto">podcasts</a>, 452 newsletters, and countless friends made along the way&hellip; it&rsquo;s time to say goodbye to The Changelog. I shipped my <a href="https://changelog.com/news/182">final News</a> last Monday and Adam shipped our <a href="https://changelog.com/friends/129">Friends finale</a> yesterday.</p>
<p>For reasons I cannot explain, during &ldquo;big change&rdquo; moments like this my brain shoots clichés at me like Nitro fired tennis balls at approaching American Gladiator contenders.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<blockquote>
<p>&ldquo;Parting is such sweet sorrow&rdquo;</p>
</blockquote>
<blockquote>
<p>&ldquo;All good things must come to an end&rdquo;</p>
</blockquote>
<blockquote>
<p>&ldquo;Time to turn the page&rdquo;</p>
</blockquote>
<p>Shut up, brain! I&rsquo;m not going to say any of that cheesy stuff&hellip; I do, however, want to share a few of my favorite things, talk about what&rsquo;s next for me, and thank some folks who touched my life<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> along the way.</p>
<h2 id="these-are-a-few-of-my-favorite-things">These are a few of my favorite things</h2>
<p>If you&rsquo;ve listened to any of our 8 <a href="https://changelog.com/topic/sotl">state of the &ldquo;log&rdquo;</a> episodes, you know how hard it is for us to pick favorites. I&rsquo;m going to do my best, but with the caveat that 13 years of making things means E_TOO_MANY_THINGS and there&rsquo;s a bunch of people, episodes, and conversations that I adore, but there&rsquo;s only so many hours in the day.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<h3 id="changelog-specials">Changelog Specials</h3>
<p>The episode I&rsquo;m most proud of is probably <a href="https://changelog.com/podcast/450">Why we 💚 Vim</a>, in which I went full &ldquo;NPR style&rdquo; and created a highly edited narrative intertwining multiple interviews, all of which were great on their own. It turned out better than I expected and people really enjoyed it (even my nerdy joke in the opener.) Win!</p>
<p><a href="https://changelog.com/podcast/450"><img src="why-we-love-vim.png" alt="Why we 💚 Vim hero shot"></a></p>
<p>Next up are the two &ldquo;Song Encoder&rdquo; specials<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> where I profiled people who create at the intersection of software and music. I made one on <a href="https://changelog.com/podcast/466">$STDOUT the rapper</a> and one on <a href="https://changelog.com/podcast/477">Forrest Brazeal</a>. I thought they were pretty great, but they didn&rsquo;t resonate with our audience like the Vim episode. That deflated me a bit, since I&rsquo;m being honest, and I gave up on future Specials.</p>
<p>I still love them, though, and I listen to the $STDOUT episode periodically just because I enjoy the guy&rsquo;s bars.</p>
<h3 id="dev-game-shows">Dev game shows</h3>
<p>One day Emma Bostian pitched me a <em>JavaScript Jeopardy</em> idea for an <a href="https://changelog.com/jsparty/112">episode of JS Party</a> and we had so much fun doing it that I spun off a bunch of different game show variants.</p>
<p>I didn&rsquo;t want Merv Griffin suing us, so I renamed it to <em>JS Danger</em>. Our <em>Go Time</em> friends wanted to play too, so I came up with <em>Go Panic!</em> for them.</p>
<p>While pitting people against each other in obscure technical trivia was fun, I wanted something more team-oriented where participants could get mad at our audience instead of me. 😏</p>
<p>That desire produced the award<strike>-winning</strike>-worthy <em>Frontend Feud</em> series, which we played online and IRL at various conferences. I rebranded that one to <em>Gophers Say</em> and Mat Ryer hosted it (with me running the game board remotely) on-stage at multiple GopherCons.</p>
<p><img src="gophers-say-on-stage.jpg" alt="Mat Ryer hosts Gophers Say live on stage at GopherCon Europe 2024"></p>
<p>Then came my pièce de résistance! <a href="https://changelog.com/friends/15">#define: a game of fake definitions</a><sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
<p>We played #define seven times and it&rsquo;s some of the most fun I&rsquo;ve had in all my years. What an amazing privilege it was to actually make a living playing silly games like these!</p>
<p>By the end, I shipped <a href="https://changelog.com/topic/games">27 dev game shows</a> and I&rsquo;d be willing to wager you could go hit &ldquo;play&rdquo; on any of those episodes and still enjoy yourself thoroughly.</p>
<h3 id="kaizen-all-the-way">Kaizen all the way</h3>
<p>The friendship (and collaborative story arc) that grew from me deciding to email <a href="https://gerhard.io">Gerhard Lazu</a> one day and ask him to help deploy <a href="https://github.com/thechangelog/changelog.com/discussions">this new Elixir app</a> I&rsquo;d built is the stuff of providence.</p>
<p><img src="jerod-and-gerhard.jpg" alt="Me visiting Gerhard in Pivotal&rsquo;s London office (2016)"></p>
<p>That was over a decade ago. We&rsquo;ve been working to continuously improve things together ever since.</p>
<p><a href="https://changelog.com/topic/kaizen">Our Kaizen</a> series is where we capture, discuss, and analyze that progress. These episodes are truly special to me, and I&rsquo;ll miss them a lot. So many laughs. So many vulnerable moments shared with complete strangers all around the world.</p>
<p>I still proudly rock my <a href="https://merch.changelog.com/products/kaizen">kaizen shirt</a> and can&rsquo;t wait to hang out with Gerhard again soon.</p>
<h3 id="time-for-changelog--friends">Time for Changelog &amp; Friends</h3>
<p>For many years, The Changelog was a weekly interview show. For the last few years, it&rsquo;s been so much more. Adam and I <a href="https://changelog.com/friends/1">introduced</a> our Friday talk show in Spring of 2023 and the ensuing 129 episodes are, for my money, our best work together.</p>
<p>Some context: Adam lives in Texas and I live in Nebraska. Partnering and co-hosting from afar is challenging. Our &ldquo;easy button&rdquo; opportunity to get together IRL was conferences. We love the hallway track: hang out, talk tech, make new friends, see old friends.</p>
<p><img src="ato-hallway-track.jpg" alt="Adam, Jerod, and some other rando (Paloma Oliveira)"></p>
<p>But we only got to do that a few times a year. Changelog &amp; Friends was our attempt to have that hallway track vibe on a weekly basis. I think we drilled it. It is, after all, 🎵 <a href="https://gist.github.com/jerodsanto/9a21add5f51741eeb128255f9b931f4d">your favorite ever show</a> 🎵</p>
<h2 id="whats-next-for-me">What&rsquo;s next for me</h2>
<p>The truth is I don&rsquo;t have a plan for what&rsquo;s next.</p>
<p>Part of me wants to go get a software engineering job<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>. Part of me wants to keep creating content.<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup> Part of me wants to start a new software business. Then there&rsquo;s a part of me that wants to just open a food truck and sell meatball sandwiches for a living!<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup></p>
<p>I&rsquo;m not going to rush in to anything.</p>
<p>But, <strong>I am open to ideas</strong>, collabs, consulting, or building cool stuff with you and/or your employer. Want to work with me? I&rsquo;m easy to <a href="mailto:jerod.santo@gmail.com">contact</a>.</p>
<p><em>(I&rsquo;ll continue writing here on this site, as my brain requires. I&rsquo;ve been publishing on the internet my entire adult life. I doubt I can stop now.)</em></p>
<h2 id="special-thanks">Special thanks</h2>
<p>Ok this section could get really long and you only care about it if you&rsquo;re listed (right?), so I&rsquo;m not going to add a lot of context around each thanks.</p>
<p>Except for this first one: <strong>thank you to Adam Stacoviak for everything you&rsquo;ve done for me and for everything we&rsquo;ve done together. It&rsquo;s been the pleasure of a life time</strong>.</p>
<p><img src="adam-and-jerod.jpg" alt="I believe this was OSCON 2019 in Portland&hellip;"></p>
<p>Thank you also, to: Heather Stacoviak, Gerhard Lazu, Breakmaster Cylinder, Alexandru Mair, Jason Backens, Bryan Lozano, Mat Ryer, Nick Nisi, Amal Hussein, Kevin Ball, Chris Hiller, Feross Aboukhadijeh, Emma Bostian, Suz Hinton, Mikeal Rogers, Johnny Boursiquot, Kris Brandow, Erik St. Martin, Brian Ketelsen, Carlisia Campos, Natalie Pistunovich, Angelica Hill, Jon Calhoun, Daniel Whitenack, Chris Benson, Matthew Sanabria, Brett Cannon, Lars Wikman, Cody Peterson, John Henry Muller, Aaron Dowd, and so many others I can&rsquo;t think straight anymore&hellip;</p>
<p>Oh, and thank you to <strong>my favorite person in the whole wide world</strong>: my wife, Rachel. Words fail to describe how much you mean to me.</p>
<p>Finally, I&rsquo;d like to thank everyone who&rsquo;s listened, read, commented, slack&rsquo;d, zulip&rsquo;d, or emailed me along the way. Connecting with your life in an authentic way, no matter how small or short that connection, means the world to me. Please do reach out and let&rsquo;s stay connected! I&rsquo;ll look forward to it. But for now&hellip;</p>
<p>So long, and thanks for all the logs! 💚💚💚</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>iykyk. If you don&rsquo;t know&hellip; <a href="https://www.youtube.com/watch?v=7iaEgcIDr4Q">thank me later</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>In a positive way. Not some creepy, &ldquo;Uhm, why are you touching me?&rdquo; kind of thing&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Sorry, I let my guard down and my brain hit me with a cliché&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Inspired by the <a href="https://songexploder.net">Song Exploder</a> podcast, which I still listen to regularly&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Pronounced &ldquo;pound define&rdquo; (or &ldquo;hash define&rdquo; if you&rsquo;re a loon)&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Do those still exist?&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>If I do this, it will not be dev focused. I&rsquo;m ready for a change of scenery&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p>Everyone should experience the secret Santo sauce and balls (<a href="https://www.youtube.com/watch?v=bPpcfH_HHH8">whoops</a>)&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Agentic anxiety</title>
      <link>https://jerodsanto.net/2026/02/agentic-anxiety/</link>
      <pubDate>Mon, 16 Feb 2026 16:00:55 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2026/02/agentic-anxiety/</guid>
      
      
      <description><![CDATA[<p>One fascinating part of our conversation<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> with Steve Ruiz from <a href="https://tldraw.dev">tldraw</a> started when he confessed that he feels bad going to bed without his Claudes working on <em>something</em>. I share the feeling. But why?</p>
<p>We analyze it on the pod, but I wanted to jot down some thoughts here as well, if for no other reason but to get it out of my head.</p>
<h2 id="just-one-more-level">Just one more level&hellip;</h2>
<p>After talking with tldraw Steve, I read a different Steve&rsquo;s <del>confession</del> post, <a href="https://steve-yegge.medium.com/the-ai-vampire-eda6e4f07163">AI Vampire</a>, in which he points to one aspect of what&rsquo;s going on: good ol&rsquo; fashion <strong>addiction</strong></p>]]></description>
      
      <content:encoded><![CDATA[<p>One fascinating part of our conversation<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> with Steve Ruiz from <a href="https://tldraw.dev">tldraw</a> started when he confessed that he feels bad going to bed without his Claudes working on <em>something</em>. I share the feeling. But why?</p>
<p>We analyze it on the pod, but I wanted to jot down some thoughts here as well, if for no other reason but to get it out of my head.</p>
<h2 id="just-one-more-level">Just one more level&hellip;</h2>
<p>After talking with tldraw Steve, I read a different Steve&rsquo;s <del>confession</del> post, <a href="https://steve-yegge.medium.com/the-ai-vampire-eda6e4f07163">AI Vampire</a>, in which he points to one aspect of what&rsquo;s going on: good ol&rsquo; fashion <strong>addiction</strong></p>
<blockquote>
<p>Agentic software building is genuinely addictive. The better you get at it, the more you want to use it. It’s simultaneously satisfying, frustrating, and exhilarating. It doles out dopamine and adrenaline shots like they’re on a fire sale.</p>
<p>Many have likened it to a slot machine. You pull a lever with each prompt, and get random rewards and sometimes amazing “payouts.” No wonder it’s addictive.</p>
</blockquote>
<p>I first heard the slot machine analogy <a href="https://changelog.com/podcast/676">from Paul Dix</a>. It certainly resonates. But it&rsquo;s even worse than that. Agentic software is like pulling a slot machine that, with each tooling improvement and model iteration, hands out an increasing number of jackpots. Who wouldn&rsquo;t pull that lever ad infinitum?!</p>
<p>Yes, addiction is a factor. Putting Claude Code to work feels like a great video game that provokes me to grind a bit longer, beat the next level, and achieve <em>one more thing</em> before joining my family for dinner. But I think there&rsquo;s something deeper going on here than just excitement / dopamine hits.</p>
<h2 id="fomo-more-like-foblb">FOMO? More like FOBLB&hellip;</h2>
<p>Fear of missing out is certainly playing a role in our collective propensity to <em>just keep prompting</em>, but it&rsquo;s not merely <em>missing out</em> that I&rsquo;m afraid of.</p>
<p>I&rsquo;ve been playing the software game since &lsquo;04, so I&rsquo;m no stranger to rule changers and game changers. In fact, my direct path to <a href="https://changelog.com">The Changelog</a> was an effort to keep up with the constant change.</p>
<p>Something&rsquo;s different this time, and I can say confidently this is <em>the most unsure</em> I&rsquo;ve ever been about software&rsquo;s future.</p>
<p>Am I excited? Yes! Am I also fearful? Yes (no ! this time)</p>
<p><em>FOMO</em>, while accurate, more appropriately describes missing a great concert or a chance to make some cash. This feeling I&rsquo;m feeling<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> bears with it <em>existential dread</em> that <em>FOMO</em> can&rsquo;t quite represent.</p>
<p>My fear is not missing out. My fear<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> is <strong>being left behind</strong>. There are many external factors piling on to invoke this feeling. I could list them here, but I&rsquo;m not going to, lol. Founded or not, time will tell. But I&rsquo;m certainly feeling it.</p>
<h2 id="whats-a-dev-to-do">What&rsquo;s a dev to do?</h2>
<p>(<em>Just keep prompting&hellip; Just keep prompting&hellip;</em>)</p>
<p>I&rsquo;m approaching agentic software the same way I&rsquo;ve approached every other change in the industry over the last two decades:</p>
<p>I dig in.</p>
<p>I build stuff.</p>
<p>I figure it out.</p>
<p>I move up the value chain.</p>
<p>There are moments of excitement. There are moments of existential dread. But my agentic anxiety is mitigated when I&rsquo;m building stuff. I don&rsquo;t know if that&rsquo;s healthy or not, but I do know it&rsquo;s true.</p>
<p>Much like Steve, I sleep better at night knowing my Claudes aren&rsquo;t.</p>
<hr>
<p>Also I&rsquo;m starting a small tree farm this Spring. Failing to plan is planning to fail. 😜</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The episode ships Wednesday. Here&rsquo;s <a href="https://changelog.com/podcast/677">the link</a> (will 404 until then)&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>I sure hope I&rsquo;m not the only one. Holla if you feel me!&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>I&rsquo;d prefer the spirit of power, love, and a sound mind&hellip;&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>7 phrases that need to die before I do</title>
      <link>https://jerodsanto.net/2026/02/normalize-not-saying-this-stuff/</link>
      <pubDate>Fri, 06 Feb 2026 16:52:09 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2026/02/normalize-not-saying-this-stuff/</guid>
      
      
      <description><![CDATA[<p>God, grant me the serenity to accept the things I cannot change, the courage to change the things I can, and the wisdom to make people stop typing this drivel into <code>textarea</code>s near me.</p>
<ol>
<li>Tell me you&rsquo;re $X without telling me you&rsquo;re $X<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
<li>You 👏 don&rsquo;t 👏 have 👏 to 👏 clap 👏 between 👏 words<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></li>
<li>I was today years old when&hellip;<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></li>
<li>The. One. Where. Every. Word. Is. A. Sentence.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></li>
<li>I don&rsquo;t know who needs to hear this&hellip;<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></li>
<li>I&rsquo;m not crying, you&rsquo;re crying.<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></li>
<li>Six Seven<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup></li>
</ol>
<p>That&rsquo;s all for now. I&rsquo;m sure I&rsquo;ll despise future phrases in the future. Before I go, here&rsquo;s one more from the past: <a href="https://www.youtube.com/watch?v=9qvVK8U4TiA">&ldquo;automagically&rdquo;</a> is a dumb word (but makes for a <a href="https://www.youtube.com/watch?v=YbWX15jASEw">pretty good song</a>)</p>]]></description>
      
      <content:encoded><![CDATA[<p>God, grant me the serenity to accept the things I cannot change, the courage to change the things I can, and the wisdom to make people stop typing this drivel into <code>textarea</code>s near me.</p>
<ol>
<li>Tell me you&rsquo;re $X without telling me you&rsquo;re $X<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
<li>You 👏 don&rsquo;t 👏 have 👏 to 👏 clap 👏 between 👏 words<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></li>
<li>I was today years old when&hellip;<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></li>
<li>The. One. Where. Every. Word. Is. A. Sentence.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></li>
<li>I don&rsquo;t know who needs to hear this&hellip;<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></li>
<li>I&rsquo;m not crying, you&rsquo;re crying.<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></li>
<li>Six Seven<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup></li>
</ol>
<p>That&rsquo;s all for now. I&rsquo;m sure I&rsquo;ll despise future phrases in the future. Before I go, here&rsquo;s one more from the past: <a href="https://www.youtube.com/watch?v=9qvVK8U4TiA">&ldquo;automagically&rdquo;</a> is a dumb word (but makes for a <a href="https://www.youtube.com/watch?v=YbWX15jASEw">pretty good song</a>)</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Where $X = &lsquo;so annoying&rsquo;&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Tell me you&rsquo;re desperate for attention without telling me desperate for attention&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>It pains me to report I&rsquo;ve used this one in earnest&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>I treat this one like celebs with Epstein: claim I never used it, hope nobody digs up the past&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Nobody. The answer is always nobody needed to hear it&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Sir, this is a Wendy&rsquo;s&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>I spent far too long deciding if this should be #6 or #7&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>The shovelware cometh</title>
      <link>https://jerodsanto.net/2026/02/the-shovelware-cometh/</link>
      <pubDate>Mon, 02 Feb 2026 15:05:15 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2026/02/the-shovelware-cometh/</guid>
      
      
      <description><![CDATA[<p>In September of last year, I <a href="https://changelog.com/news/160">covered</a> a post by Mike Judge arguing that <a href="https://mikelovesrobots.substack.com/p/wheres-the-shovelware-why-ai-coding">AI coding claims don&rsquo;t add up</a>, in which he asked this question:</p>
<blockquote>
<p>If so many developers are so extraordinarily productive using these tools, <strong>where is the flood of shovelware</strong>? We should be seeing apps of all shapes and sizes, video games, new websites, mobile apps, software-as-a-service apps — we should be drowning in choice. We should be in the middle of an indie software revolution. We should be seeing 10,000 Tetris clones on Steam.</p>]]></description>
      
      <content:encoded><![CDATA[<p>In September of last year, I <a href="https://changelog.com/news/160">covered</a> a post by Mike Judge arguing that <a href="https://mikelovesrobots.substack.com/p/wheres-the-shovelware-why-ai-coding">AI coding claims don&rsquo;t add up</a>, in which he asked this question:</p>
<blockquote>
<p>If so many developers are so extraordinarily productive using these tools, <strong>where is the flood of shovelware</strong>? We should be seeing apps of all shapes and sizes, video games, new websites, mobile apps, software-as-a-service apps — we should be drowning in choice. We should be in the middle of an indie software revolution. We should be seeing 10,000 Tetris clones on Steam.</p>
</blockquote>
<p>At the time, I found Mike&rsquo;s rhetorical question compelling. Not necessarily convincing, but compelling. We really weren&rsquo;t seeing a flood of new wares yet. Some of us had our <a href="https://justin.searls.co/links/2025-09-08-i-ve-got-your-shovelware-right-here/">shovels out</a>, but not in the numbers you&rsquo;d expect for a truly revolutionary new technology<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>Fast forward to last week, when Pete Goldsmith discovered that <a href="https://petegoldsmith.com/2026/01/26/2026-01-26-show-hn-trends/">Show HN posts per month more than doubled in the last year</a>:</p>
<blockquote>
<p>After building some things recently using LLMs and agent workflows, I was noticing more ‘Show HN’ posts. Assuming there had been a rise in folks building things, but wanting to rule out confirmation bias, I looked at the numbers.</p>
</blockquote>
<p>Turns out, the numbers<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> resemble HN&rsquo;s favorite shape: 🏒</p>
<p><img src="show-hn-ratio.webp" alt="Show HN as % of All Story Submissions (2021-2026) goes hockey stick style"></p>
<p>This, to me, looks like the canary in the coal mine; the bellwether leading the flock; the first swallow of summer; the&hellip; you get the idea.</p>
<p>We may soon be overwhelmed by <a href="https://rselbach.com/your-sub-is-now-my-weekend-project">weekend projects</a>, <a href="https://worksonmymachine.ai/p/the-great-flood-of-adequate-software">adequate software</a>, and <a href="https://www.robinsloan.com/notes/home-cooked-app/">home-cooked meals</a>. The shovelware cometh. And it might change everything.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I think this can be explained by a few factors. a) the tools needed to improve, b) we suck at shipping, and c) we were just starting to cook.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>This is likely conservative, too, because many people promote their new projects without doing a proper &ldquo;Show HN&rdquo; post.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Thinking about custom software in a new way</title>
      <link>https://jerodsanto.net/2026/01/thinking-about-custom-software-in-a-new-way/</link>
      <pubDate>Wed, 28 Jan 2026 15:56:14 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2026/01/thinking-about-custom-software-in-a-new-way/</guid>
      
      
      <description><![CDATA[<p>At this point, anybody saying things about AI&rsquo;s usefulness in software development like <a href="https://x.com/notch/status/2015801398768681075">notch did on X the other day</a> is either a) burying their head in the sand, b) an old man yelling at clouds, or c) seriously entrenched for some other reason.</p>
<p>Here&rsquo;s notch&rsquo;s take, so you don&rsquo;t have to bounce:</p>
<blockquote>
<p>Reminder that using AI to write code is an incredibly bad idea still, and anyone advocating for it is either incompetent or evil.</p>]]></description>
      
      <content:encoded><![CDATA[<p>At this point, anybody saying things about AI&rsquo;s usefulness in software development like <a href="https://x.com/notch/status/2015801398768681075">notch did on X the other day</a> is either a) burying their head in the sand, b) an old man yelling at clouds, or c) seriously entrenched for some other reason.</p>
<p>Here&rsquo;s notch&rsquo;s take, so you don&rsquo;t have to bounce:</p>
<blockquote>
<p>Reminder that using AI to write code is an incredibly bad idea still, and anyone advocating for it is either incompetent or evil.</p>
<p>It&rsquo;s just as dumb as letting AI write the laws. It&rsquo;s about logic, not about typing.</p>
</blockquote>
<p>Well, call me incompetent. Or evil. Call me whatever makes you feel good about yourself. But I believe you should be using AI to write code today. And tomorrow. And for the foreseeable future.</p>
<p>Two things can be true at the same time:</p>
<ol>
<li>The AI hype is (still) off the charts</li>
<li>Claude Code has changed how I think about custom software</li>
</ol>
<p>After many years of software development, both professionally and personally, my default refrain when presented with the prospect of throwing custom software at a problem was: <em>perish the thought!</em></p>
<p>In other words: <strong>No, let&rsquo;s not. Unless it is absolutely necessary and obviously worth the (total) cost (of ownership).</strong></p>
<p>That&rsquo;s because I was intimately familiar with exactly how expensive a proposition custom software is, so I avoided it if at all possible.</p>
<p>Now I&rsquo;m thinking about custom software in a new way.</p>
<p>The price to prove a concept, to spike out a prototype, to build a thing and see how it works, to &ldquo;go West, young man&rdquo; has dropped precipitously thanks to these new tools. The new price has moved me.</p>
<p>From <em>&ldquo;perish the thought&rdquo;</em> to <em>&ldquo;entertain the thought&rdquo;</em></p>
<p>From &ldquo;<em>but why?</em>&rdquo; to &ldquo;<em>why not?</em>&rdquo;</p>
<p>In other words: <strong>Yes, let&rsquo;s. Unless it is absolutely unnecessary and obviously not worth the (total) cost (of ownership).</strong></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Hugo bloggin&#39; with Obsidian like a boss</title>
      <link>https://jerodsanto.net/2026/01/hugo-boss-obsidian-plugin/</link>
      <pubDate>Fri, 23 Jan 2026 16:32:27 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2026/01/hugo-boss-obsidian-plugin/</guid>
      
      
      <description><![CDATA[<p>One of my goals when I <a href="https://jerodsanto.net/2025/12/is-this-thing-still-on/">refreshed my blog</a> was to publish with <a href="https://gohugo.io">Hugo</a>, but write with <a href="https://obsidian.md">Obsidian</a>. Turns out that was as simple as:</p>
<pre tabindex="0"><code>ln -s ~/src/jerodsanto/net/content/posts ~/Dropbox/obsidian/Jerod/blog
</code></pre><p>That&rsquo;s a good start, but once I&rsquo;m writing in Obsidian&hellip; I don&rsquo;t exactly want to leave. There&rsquo;s friction when managing various Hugo tasks from a separate terminal session. So, I had Claude Code write me an Obsidian plugin to add those features.</p>
<p>It&rsquo;s called <a href="https://github.com/jerodsanto/hugo-boss">Hugo Boss</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and I submitted it to the Obsidian community plugins list, so hopefully you&rsquo;ll be able to install it from there soon<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. The plugin adds a button that has four features today:</p>]]></description>
      
      <content:encoded><![CDATA[<p>One of my goals when I <a href="https://jerodsanto.net/2025/12/is-this-thing-still-on/">refreshed my blog</a> was to publish with <a href="https://gohugo.io">Hugo</a>, but write with <a href="https://obsidian.md">Obsidian</a>. Turns out that was as simple as:</p>
<pre tabindex="0"><code>ln -s ~/src/jerodsanto/net/content/posts ~/Dropbox/obsidian/Jerod/blog
</code></pre><p>That&rsquo;s a good start, but once I&rsquo;m writing in Obsidian&hellip; I don&rsquo;t exactly want to leave. There&rsquo;s friction when managing various Hugo tasks from a separate terminal session. So, I had Claude Code write me an Obsidian plugin to add those features.</p>
<p>It&rsquo;s called <a href="https://github.com/jerodsanto/hugo-boss">Hugo Boss</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and I submitted it to the Obsidian community plugins list, so hopefully you&rsquo;ll be able to install it from there soon<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. The plugin adds a button that has four features today:</p>
<ol>
<li><strong>New Post</strong> – starts a doc with required front matter (or selected template)</li>
<li><strong>Preview Site</strong> – starts Hugo&rsquo;s server and opens your draft in a webview</li>
<li><strong>Publish Post</strong> – applies all necessary changes to publish a post</li>
<li><strong>Deploy Site</strong> – builds Hugo site and runs configured deploy command</li>
</ol>
<p>Here&rsquo;s what it looks like in use. Blog post on the left. Preview on the right.</p>
<p><img src="hugo-boss-in-action.png" alt="Hugo Boss in action. Blog post on the left. Preview on the right."></p>
<p>So far these four features are all I&rsquo;ve needed to feel productive with this setup. But, if there are other ideas that would make this even more <em>boss</em>, <a href="https://github.com/jerodsanto/hugo-boss/pulls">PRs welcome</a>!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>&ldquo;Remember when people used to say <em>boss</em>, when they were describing something that was really cool?&rdquo;&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>However, there are currently <a href="https://github.com/obsidianmd/obsidian-releases/pulls">957 open PRs</a> to that repo, so it may be awhile&hellip;&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Wow, curl is even used heavily in the NFL playoffs</title>
      <link>https://jerodsanto.net/2026/01/all-the-places-curl-will-go/</link>
      <pubDate>Wed, 21 Jan 2026 17:25:26 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2026/01/all-the-places-curl-will-go/</guid>
      
      
      <description><![CDATA[<p>I remember once telling <a href="https://curl.se/">curl</a> creator, Daniel Stenberg, he&rsquo;d <a href="https://changelog.com/friends/49#transcript-182">achieved total world domination</a>. Turns out, he was just getting started.</p>
<p>Fast forward to last weekend when football fans around the world <a href="https://www.youtube.com/watch?v=UwIOv1OcwnM">witnessed</a> curl propelling the LA Rams to the NFC Championship game (and on to the Super Bowl?)</p>
<p><img src="curl.gif" alt="Rams DB, Kam Curl, makes a game-ending interception agains the Bears"></p>]]></description>
      
      <content:encoded><![CDATA[<p>I remember once telling <a href="https://curl.se/">curl</a> creator, Daniel Stenberg, he&rsquo;d <a href="https://changelog.com/friends/49#transcript-182">achieved total world domination</a>. Turns out, he was just getting started.</p>
<p>Fast forward to last weekend when football fans around the world <a href="https://www.youtube.com/watch?v=UwIOv1OcwnM">witnessed</a> curl propelling the LA Rams to the NFC Championship game (and on to the Super Bowl?)</p>
<p><img src="curl.gif" alt="Rams DB, Kam Curl, makes a game-ending interception agains the Bears"></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Buckets for Bucks</title>
      <link>https://jerodsanto.net/2026/01/please-support-our-youth-basketball-program/</link>
      <pubDate>Mon, 19 Jan 2026 16:38:55 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2026/01/please-support-our-youth-basketball-program/</guid>
      
      
      <description><![CDATA[<p>The homeschool <a href="https://www.omahalightningbasketball.com">basketball program</a> that all three of my boys play in (and I have coached in a volunteer capacity for ~10 years) is doing a little fundraiser to help keep registration fees low.</p>
<p>Please <a href="https://app.99pledges.com/fund/omahalight1/levi-santo">check it out</a> and consider donating to Levi&rsquo;s campaign. He&rsquo;s <a href="https://x.com/jerodsanto/status/2012199844920590593">working hard</a> to bring in as much support as he can!</p>]]></description>
      
      <content:encoded><![CDATA[<p>The homeschool <a href="https://www.omahalightningbasketball.com">basketball program</a> that all three of my boys play in (and I have coached in a volunteer capacity for ~10 years) is doing a little fundraiser to help keep registration fees low.</p>
<p>Please <a href="https://app.99pledges.com/fund/omahalight1/levi-santo">check it out</a> and consider donating to Levi&rsquo;s campaign. He&rsquo;s <a href="https://x.com/jerodsanto/status/2012199844920590593">working hard</a> to bring in as much support as he can!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>I joined the POSSE Party</title>
      <link>https://jerodsanto.net/2026/01/i-joined-the-posse-party/</link>
      <pubDate>Mon, 19 Jan 2026 14:31:22 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2026/01/i-joined-the-posse-party/</guid>
      
      
      <description><![CDATA[<p><a href="https://justin.searls.co">Justin Searls</a> quit social media by posting more. How, exactly, did he do that? By writing way too much <a href="https://github.com/searlsco/posse_party">Ruby code</a> to cross-post his blog to all the social networks using an atom feed of his design.</p>
<p><a href="https://indieweb.org/POSSE">POSSE</a> is an old idea: publish on your own site, syndicate elsewhere. That&rsquo;s desirable, for sure, but not always easy to accomplish.</p>
<p><a href="https://posseparty.com">POSSE Party</a> is the new app Justin released so others can accomplish the same without all the work he went through.</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="https://justin.searls.co">Justin Searls</a> quit social media by posting more. How, exactly, did he do that? By writing way too much <a href="https://github.com/searlsco/posse_party">Ruby code</a> to cross-post his blog to all the social networks using an atom feed of his design.</p>
<p><a href="https://indieweb.org/POSSE">POSSE</a> is an old idea: publish on your own site, syndicate elsewhere. That&rsquo;s desirable, for sure, but not always easy to accomplish.</p>
<p><a href="https://posseparty.com">POSSE Party</a> is the new app Justin released so others can accomplish the same without all the work he went through.</p>
<p>I&rsquo;m super lazy. So, last week I sat down, watched Justin&rsquo;s <a href="https://www.youtube.com/watch?v=DQbmSDzSoKM">Set up POSSE Party in 20 minutes</a>, and followed all the steps. A few days later (lol, email delivery woes), I had <a href="https://posse.jerodsanto.net">my own instance</a> up and running.</p>
<p>For now it&rsquo;s just posting to <a href="https://changelog.social/@jerod">Mastodon</a> and <a href="https://x.com/jerodsanto">X</a> with super minimal settings, but I plan on adding more networks and customizing everything as time allows.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>The Temporal API finally lands in Chrome 144</title>
      <link>https://jerodsanto.net/2026/01/the-temporal-api-finally-lands-in-chrome-144/</link>
      <pubDate>Wed, 14 Jan 2026 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2026/01/the-temporal-api-finally-lands-in-chrome-144/</guid>
      
      
      <description><![CDATA[<p><a href="https://developer.chrome.com/blog/new-in-chrome-144#temporal">It&rsquo;s about <em>time</em></a>! They&rsquo;ve been working on this since <em>time</em> immemorial. Next question: <em>when</em> will it land in Safari on iOS? Not any <em>time</em> soon, it seems. <em>Until</em> that fateful day&hellip; polyfills, y&rsquo;all. <a href="https://github.com/tc39/proposal-temporal/tree/main/#polyfills">Polyfills</a>.</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="https://developer.chrome.com/blog/new-in-chrome-144#temporal">It&rsquo;s about <em>time</em></a>! They&rsquo;ve been working on this since <em>time</em> immemorial. Next question: <em>when</em> will it land in Safari on iOS? Not any <em>time</em> soon, it seems. <em>Until</em> that fateful day&hellip; polyfills, y&rsquo;all. <a href="https://github.com/tc39/proposal-temporal/tree/main/#polyfills">Polyfills</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Jerod&#39;s Chip Dip Depletion Principle</title>
      <link>https://jerodsanto.net/2026/01/jerods-chip-dip-depletion-principle/</link>
      <pubDate>Mon, 05 Jan 2026 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2026/01/jerods-chip-dip-depletion-principle/</guid>
      
      
      <description><![CDATA[<p>I have discovered a natural law of the Universe.</p>
<p>I will call it <strong>&ldquo;The Chip Dip Depletion Principle&rdquo;</strong>, which states:</p>
<blockquote>
<p>Given sufficient snacking time, for any suitable chip/dip combination, the chip to dip ratio remains imbalanced until one of the two supplies are depleted.</p>
</blockquote>
<p><strong>&ldquo;Jerod&rsquo;s Chip Dip Depletion Principle&rdquo;</strong>, a practical example:</p>
<p><em>&ldquo;Yay, chips and dip! I&rsquo;m going to eat some.&rdquo;</em></p>
<p>/me: Snacks on chips and dip</p>
<p><em>&ldquo;Dratz! I am all out of chips, but still have dip. I need more chips.&rdquo;</em></p>]]></description>
      
      <content:encoded><![CDATA[<p>I have discovered a natural law of the Universe.</p>
<p>I will call it <strong>&ldquo;The Chip Dip Depletion Principle&rdquo;</strong>, which states:</p>
<blockquote>
<p>Given sufficient snacking time, for any suitable chip/dip combination, the chip to dip ratio remains imbalanced until one of the two supplies are depleted.</p>
</blockquote>
<p><strong>&ldquo;Jerod&rsquo;s Chip Dip Depletion Principle&rdquo;</strong>, a practical example:</p>
<p><em>&ldquo;Yay, chips and dip! I&rsquo;m going to eat some.&rdquo;</em></p>
<p>/me: Snacks on chips and dip</p>
<p><em>&ldquo;Dratz! I am all out of chips, but still have dip. I need more chips.&rdquo;</em></p>
<p>/me: Reloads chips</p>
<p>/me: Snacks on chips and dip</p>
<p><em>&ldquo;Dratz! I am all out of dip, but still have chips. I need more dip.&rdquo;</em></p>
<p>/me: Reloads dip</p>
<p>/me: Snacks on chips and dip</p>
<p><em>&ldquo;Dratz! I am again all out of chips, but still have dip. I need more chips.&rdquo;</em></p>
<p>/me: Repeats until one of the supplies is exhausted</p>
<p>/me: Fat and happy that <strong>&ldquo;Jerod&rsquo;s Chip Dip Depletion Principle&rdquo;</strong> holds true</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Changelog community book recommendations</title>
      <link>https://jerodsanto.net/2025/12/changelog-community-book-recommendations/</link>
      <pubDate>Wed, 31 Dec 2025 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2025/12/changelog-community-book-recommendations/</guid>
      
      
      <description><![CDATA[<p>Here&rsquo;s me, writing <a href="https://changelog.com/news/165">in Changelog News</a> last October:</p>
<blockquote>
<p>I&rsquo;m getting back in to reading. Not Audible. Not e-ink. I&rsquo;m talking physical books held in my physical hands scanned by my physical eye balls.</p>
<p>I need recommends! Please reply with <strong>the best book</strong> you&rsquo;ve read in the last 10 years. Fiction. Non-fiction. Whatever!</p>
</blockquote>
<p>I was overwhelmed by the response! There were too many thoughtful recommendations to keep them all to myself, so I jotted down each one, grabbed the synopses, and linked them up for everyone to peruse.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Here&rsquo;s me, writing <a href="https://changelog.com/news/165">in Changelog News</a> last October:</p>
<blockquote>
<p>I&rsquo;m getting back in to reading. Not Audible. Not e-ink. I&rsquo;m talking physical books held in my physical hands scanned by my physical eye balls.</p>
<p>I need recommends! Please reply with <strong>the best book</strong> you&rsquo;ve read in the last 10 years. Fiction. Non-fiction. Whatever!</p>
</blockquote>
<p>I was overwhelmed by the response! There were too many thoughtful recommendations to keep them all to myself, so I jotted down each one, grabbed the synopses, and linked them up for everyone to peruse.</p>
<p><em>(Here they are, roughly in the order I received them.)</em> 👇</p>
<hr>
<p><a href="https://app.thestorygraph.com/books/2566c745-421f-451f-a79b-f9cff1cdb247">Lilith</a> by George MacDonald</p>
<p><em>Lilith is a story concerning the nature of life, death, and salvation.After he followed the old man through the mirror, nothing in his life was ever right again. It was a special mirror and the man he followed was a special man &ndash; a man who led hi&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/cd626fd8-11de-458a-a71a-4b1c64c86ff1">James</a> by Percial Everett</p>
<p><em>A brilliant, action-packed reimagining of The Adventures of Huckleberry Finn, both harrowing and ferociously funny, told from the enslaved Jim’s point of view.When the enslaved Jim overhears that he is about to be sold to a man in New Orleans, sep&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/83dababf-03fe-4910-a32f-6295f6934f00">The Royal Game</a> by Stefan Zweig</p>
<p><em>Chess Story, also known as The Royal Game, is the Austrian master Stefan Zweig&rsquo;s final achievement, which was completed in Brazilian exile and sent off to his American publisher only a matter of days before his suicide in 1942. It is the only stor&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/b6ee5fb3-46eb-472d-ab77-32efef35734e">Lonesome Dove</a> by Larry McMurtry</p>
<p><em>A love story, an adventure, and an epic of the frontier, Larry McMurtry&rsquo;s Pulitzer Prize- winning classic, Lonesome Dove, the third book in the Lonesome Dove tetralogy, is the grandest novel ever written about the last defiant wilderness of Americ&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/1b6ca291-cdef-4706-a7f8-39c6360380b2">Endurance</a> by Alfred Lansing.</p>
<p><em>This fabulous account of Sir Ernest Shackleton&rsquo;s epic adventure recreates one of the most astonishing feats of exploration and human courage ever recorded. In August 1914, the Endurance set sail for the South Atlantic. The object of the expedition&hellip;</em></p>
<p>Also: you should listen to the Books in the Box episodes from <a href="https://oxide-and-friends.transistor.fm">Oxide and Friends</a>, if you haven’t. Lots of great ones there.</p>
<p><a href="https://app.thestorygraph.com/books/c8ea0f1c-a7b5-4236-b51a-4573dc846025">Idoru</a> by William Gibson</p>
<p><em>Now in trade paperback from the author of Neuromancer comes a story that takes readers to 21st century Tokyo after the millennial quake, where something violently new is about to erupt.</em></p>
<p><a href="https://app.thestorygraph.com/books/717510ba-579d-4455-97d4-b8945ae2c82b">Good Strategy, Bad Strategy</a> by Richard P. Rumelt</p>
<p><em>Good Strategy/Bad Strategy clarifies the muddled thinking underlying too many strategies and provides a clear way to create and implement a powerful action-oriented strategy for the real world. Developing and implementing a strategy is the central&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/a62792d0-7cbc-449e-a56c-5adaafee499e">Lessons of History</a> by Will and Ariel Durant</p>
<p><em>In this illuminating and thoughtful book, Will and Ariel Durant have succeeded in distilling for the reader the accumulated store of knowledge and experience from their five decades of work on the eleven monumental volumes of The Story of Civiliza&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/cf3e5868-a15f-4b21-ba08-e8779bddfc88">Diaspora</a> by Greg Egan</p>
<p><em>In the twenty-first century, humanity drastically reconfigured itself. Many became polis citizens, joining vast computer networks to live as conscious software. Some of the citizens moved into gleisners: disposable, renewable robotic bodies acting&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/b8d91e0d-bd3f-4126-ad30-b58e1c4a1e18">The Will of the Many</a> by James Islington</p>
<p><em>AUDI. VIDE. TACE.The Catenan Republic—the Hierarchy—may rule the world now, but they do not know everything.I tell them my name is Vis Telimus. I tell them I was orphaned after a tragic accident three years ago, and that good fortune alone has led&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/c84bba55-3c4c-4351-b445-3f6f780156c4">Ready Player One</a> (book was way better than the movie)</p>
<p><em>IN THE YEAR 2044, reality is an ugly place. The only time teenage Wade Watts really feels alive is when he&rsquo;s jacked into the virtual utopia known as the OASIS. Wade&rsquo;s devoted his life to studying the puzzles hidden within this world&rsquo;s digital conf&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/78e94aba-f608-4ba8-b9f5-762f5becf4d3">Ender&rsquo;s Game</a> (book better than movie as well)</p>
<p><em>From New York Times bestselling author Orson Scott Card, Ender&rsquo;s Game is the classic Hugo and Nebula award-winning science fiction novel of a young boy&rsquo;s recruitment into the midst of an interstellar war. In order to develop a secure defense agai&hellip;</em></p>
<p>Also: <a href="https://app.thestorygraph.com/authors/3a093349-c689-4097-a424-e52007c21d1c">Dan Brown</a> novels</p>
<p><a href="https://app.thestorygraph.com/books/c3ab97d5-39d4-4d41-94ef-ca2f7e008c6e">The Last Traverse</a> by Ty Gagne</p>
<p><em>On a mountain somewhere above treeline, in some of the coldest and worst winter conditions imaginable, two men lie unconscious in the snow as explosive winds batter the nearby summits. In The Last Traverse: Tragedy and Resilience in the Winter Whi&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/f7e3106d-7f4f-4c91-9bf3-e894d9028986">Careless People</a> by Sarah Wynn-Williams</p>
<p><em>An explosive insider account charting one woman&rsquo;s career at the heart of one of the most influential companies on the planet, Careless People gives you a front-row seat to Facebook, the decisions that have shaped world events in recent decades, an&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/ecf1cb5f-c60c-466a-9f08-834c8c1cb2da">Address Unknown</a> by Kathrine Taylor</p>
<p><em>A rediscovered classic, originally published in 1938 and now an international bestseller.When it first appeared in Story magazine in 1938, Address Unknown became an immediate social phenomenon and literary sensation. Published in book form a year &hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/60e78619-a7c2-436f-a9fd-638e62b74c13">Akata Witch</a> by Nnedi Okorafor</p>
<p><em>Akata Witch weaves together a heart-pounding tale of magic, mystery, and finding one&rsquo;s place in the world.Twelve-year-old Sunny lives in Nigeria, but she was born American. Her features are African, but she&rsquo;s albino. She&rsquo;s a terrific athlete, but &hellip;</em></p>
<p>Also: Anything by <a href="https://app.thestorygraph.com/authors/009e05c0-ca93-4527-87f5-d9660fe7dac0">Octavia Butler</a></p>
<p><a href="https://app.thestorygraph.com/books/f37d8be9-fa2b-4c5f-a350-ed290fc4f6e4">Musicophilia: Tales of Music and the Brain</a> by Oliver Sacks</p>
<p><em>Revised and ExpandedWith the same trademark compassion and erudition he brought to The Man Who Mistook His Wife for a Hat, Oliver Sacks explores the place music occupies in the brain and how it affects the human condition. In Musicophilia, he show&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/110b5fa0-b91e-479b-8007-9b52c9f5875e">When Among Crows</a> by Veronica Roth</p>
<p><em>When Among Crows is swift and striking, drawing from the deep well of Slavic folklore and asking if redemption and atonement can be found in embracing what we most fear.We bear the sword, and we bear the pain of the sword.Pain is Dymitr’s calling&hellip;.</em></p>
<p><a href="https://app.thestorygraph.com/books/b4228f63-3f46-4788-b1a0-bf4a02e60cea">The Most Important Thing</a> by Howard Marks</p>
<p><em>This is that rarity, a useful book.&quot;&ndash;Warren Buffett Howard Marks, the chairman and cofounder of Oaktree Capital Management, is renowned for his insightful assessments of market opportunity and risk. After four decades spent ascending to the top &hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/21866866-64b0-46b0-bb59-2c513bf55f21">Clear Thinking</a> from Shane Parrish</p>
<p><em>A roadmap for thinking clearly in any situation.We all aspire to see the world clearly. And yet all too often, when the pressure is on, we give in to our most irrational impulses - making intuitive decisions that take us ever-further from our goal&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/ef788e65-0d87-4945-940e-b4a7c78677d2">Writing to Learn</a> by William Zinsser</p>
<p><em>From the author of On Writing Well comes a book on how to write clearly about any subject&ndash;and how to use writing as a means of learning.</em></p>
<p><a href="https://app.thestorygraph.com/books/ac3ea915-993d-4f30-8632-0f91e4ad0704">Project Hail Mary</a> by Andy Weir</p>
<p><em>Ryland Grace is the sole survivor on a desperate, last-chance mission—and if he fails, humanity and the earth itself will perish.Except that right now, he doesn’t know that. He can’t even remember his own name, let alone the nature of his assignme&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/2dc432fb-c009-4190-aaae-15c6468d666d">The Phoenix Project</a> by Gene Kim, others</p>
<p><em>Bill is an IT manager at Parts Unlimited. It&rsquo;s Tuesday morning and on his drive into the office, Bill gets a call from the CEO. The company&rsquo;s new IT initiative, code named Phoenix Project, is critical to the future of Parts Unlimited, but the proj&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/0baa7f2a-3f3f-4752-9d81-0434117d0648">Accelerate</a> by Jez Humble, others</p>
<p><em>Accelerate your organization to win in the marketplace. How can we apply technology to drive business value? For years, we&rsquo;ve been told that the performance of software delivery teams doesn&rsquo;t matter―that it can&rsquo;t provide a competitive advantage to&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/674f14a4-8dc3-4c99-915d-e7147a475790">What You Do Is Who You Are</a> by Ben Horowitz</p>
<p><em>Ben Horowitz, a leading venture capitalist, modern management expert, and New York Times bestselling author, combines lessons both from history and from modern organizational practice with practical and often surprising advice to help executives b&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/6217b452-8686-48a9-a128-11181521da4e">The Dip</a> by Seth Godin</p>
<p><em>A New York Times, USA Today, and Wall Street Journal bestseller In this iconic bestseller, popular business blogger and bestselling author Seth Godin proves that winners are really just the best quitters. Godin shows that winners quit fast, quit&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/c4c8b5ac-1332-463c-ab43-0f636f1e86b9">Thinking, Fast and Slow</a> by Daniel Kahneman</p>
<p><em>In his mega bestseller, Thinking, Fast and Slow, Daniel Kahneman, world-famous psychologist and winner of the Nobel Prize in Economics, takes us on a groundbreaking tour of the mind and explains the two systems that drive the way we think.System 1&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/e585eba0-5d91-4c0a-8194-c3d53b049711">Qualityland</a> by Marc-Uwe Kling</p>
<p><em>In the near future sci-fi world of Qualityland, algorithms help create an idyllic life for its citizens, but what if the perfect world wasn&rsquo;t built for you?Welcome to QualityLand, the best country on Earth. Here, a universal ranking system determi&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/d06ba1d7-ab53-4d16-975b-eb1c84318688">Elantris</a> by Brandon Sanderson</p>
<p><em>Elantris was the capital of Arelon: gigantic, beautiful, literally radiant, filled with benevolent beings who used their powerful magical abilities for the benefit of all. Yet each of these demigods was once an ordinary person until touched by the&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/866837ca-6ff7-4b54-8e4e-9cae7735eff2">Lucky 666</a> by Bob Drury</p>
<p><em>From the authors of the New York Times bestselling The Heart of Everything That Is and Halsey s Typhoon comes the dramatic, untold story of a daredevil bomber pilot and his misfit crew who fly their lone B-17 into the teeth of the Japanese Empire &hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/a9cf1698-9140-43c6-9070-97b9236bb29e">Dies the Fire</a> by S.M. Stirling</p>
<p><em>S. M. Stirling presents his first Novel of the Change, the start of the New York Times bestselling postapocalyptic saga set in a world where all technology has been rendered useless.The Change occurred when an electrical storm centered over the is&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/aee831e6-4d79-48c3-8ac1-9e43bb35bda1">The Master and his Emissary</a> by Ian Gilchrist</p>
<p><em>Why is the brain divided? The difference between right &amp; left hemispheres has been puzzled over for centuries. In a book of unprecedented scope, McGilchrist draws on a vast body of recent brain research, illustrated with case histories, to rev&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/cd4708ea-c758-4f5c-80ad-f1529af714bd">The Plight of Man and the Power of God</a> by Martyn Lloyd-Jones</p>
<p><em>Martyn Lloyd-Jones&rsquo; preaching always had an emphasis on the desperate plight of man and the power of God to save. His preaching was crystal clear on the sovereignty of God in the salvation of sinners, a concept that does not sit comfortably in our&hellip;</em></p>
<p><a href="https://app.thestorygraph.com/books/b5a93403-41c4-4b93-8e5f-b442185461c1">Lost City of the Monkey God</a> by Douglas Preston</p>
<p><em>The #1 New York Times and Wall Street Journal bestseller, named one of the best books of the year by The Boston Globe and National Geographic: acclaimed journalist Douglas Preston takes readers on a true adventure deep into the Honduran rainforest&hellip;</em></p>
<hr>
<p>I surely won&rsquo;t get around to reading all these, but there&rsquo;s a few that I&rsquo;m definitely planning to check out. I already bought and am half way through <em>Project Hail Mary</em>. I was swayed by the upcoming <a href="https://www.imdb.com/title/tt12042730">major motion picture</a>. It&rsquo;s been too long since I&rsquo;ve been able to say, &ldquo;the book was so much better!&rdquo; 🤣</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Is this thing still on?</title>
      <link>https://jerodsanto.net/2025/12/is-this-thing-still-on/</link>
      <pubDate>Mon, 22 Dec 2025 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2025/12/is-this-thing-still-on/</guid>
      
      
      <description><![CDATA[<p>I&rsquo;ve needed to refresh the ol&rsquo; blog for <em>years</em>, but I never have the time. Thankfully, Claude Code compresses time <em>just enough</em> for me to finally get it done! All I need now is an obligatory &ldquo;I refreshed my blog&rdquo; blog post&hellip;</p>
<p>The last time I did this was forever ago. So long, in fact, that I cannot remember exactly when. 2013 maybe? I don&rsquo;t know. I didn&rsquo;t even write about it then. Here were my goals this time around:</p>]]></description>
      
      <content:encoded><![CDATA[<p>I&rsquo;ve needed to refresh the ol&rsquo; blog for <em>years</em>, but I never have the time. Thankfully, Claude Code compresses time <em>just enough</em> for me to finally get it done! All I need now is an obligatory &ldquo;I refreshed my blog&rdquo; blog post&hellip;</p>
<p>The last time I did this was forever ago. So long, in fact, that I cannot remember exactly when. 2013 maybe? I don&rsquo;t know. I didn&rsquo;t even write about it then. Here were my goals this time around:</p>
<ul>
<li>Graph paper theme: I <a href="https://changelog.com/jsparty/275#transcript-22">love</a> graph paper!</li>
<li>No categories or tags: unnecessary friction</li>
<li>Blog from Obisidian: a symlink is all you need</li>
<li>No analytics: I want to write to think, not to be read</li>
<li>Switch from Jekyll to Hugo: faster builds, easier maintenance</li>
</ul>
<p>There&rsquo;s still some kinks to iron out, but so far so good. Does this mean I&rsquo;ll write more here than I used to? Who knows, but at the very least I&rsquo;ve removed all my excuses&hellip;</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Nuclear Privilege</title>
      <link>https://jerodsanto.net/2025/12/nuclear-privilege/</link>
      <pubDate>Sat, 20 Dec 2025 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2025/12/nuclear-privilege/</guid>
      
      
      <description><![CDATA[<p>Some may say my path was paved</p>
<p>My skin color made it so.</p>
<p>Others claim I walked the easy way</p>
<p>Thanks to my XY chromosomes.</p>
<p>Those things may be true, how can you actually know…</p>
<p>But they appear as molehills in a valley next to the mountainous</p>
<p>peak my perch was placed by my parents.</p>
<p>A perspective of true privilege. See</p>
<p>After fifty plus years, they still love each other</p>]]></description>
      
      <content:encoded><![CDATA[<p>Some may say my path was paved</p>
<p>My skin color made it so.</p>
<p>Others claim I walked the easy way</p>
<p>Thanks to my XY chromosomes.</p>
<p>Those things may be true, how can you actually know…</p>
<p>But they appear as molehills in a valley next to the mountainous</p>
<p>peak my perch was placed by my parents.</p>
<p>A perspective of true privilege. See</p>
<p>After fifty plus years, they still love each other</p>
<p>And I reap the benefit.</p>
<hr>
<p>Mom was like…</p>
<p>“Work before play” and</p>
<p>“Good things come to those who wait” and</p>
<p>“Nothing good ever happens after midnight”</p>
<p>And I was like…</p>
<p>“Please come sit down, Mom. We don’t want to eat dinner without you.”</p>
<hr>
<p>Dad was like…</p>
<p>“Hey, be cool” and</p>
<p>“You want five across your lips?!” and</p>
<p>“Thanks, kiddo!”</p>
<p>And I was like…</p>
<p>“My finger got lost in your belly button!”</p>
<hr>
<p>She showed me how to steal humility from vanity’s clutch</p>
<p>What loyalty actually looks like</p>
<p>And that it’s ok to cry when your heart is touched.</p>
<p>He showed me how to cheer your support</p>
<p>What family-first actually looks like</p>
<p>And that if you laugh hard enough at your own jokes</p>
<p>other people will laugh with you…</p>
<p>Or at you. But nobody’s keeping score.</p>
<hr>
<p>They stuck together, in better &amp; worse, sickness &amp; health</p>
<p>through the peaks &amp; valleys, the highs &amp; the lows, the rivers &amp; roads…</p>
<p>Shielding us kids from the 99 problems that every faithful marriage endures.</p>
<p>I never wondered whether or not they loved me, or each other</p>
<p>It wasn’t a question of my heart.</p>
<p>My childhood was built upon a rock.</p>
<hr>
<p>What I didn’t know then that I do know now…</p>
<p>Is just how rare that rock was.</p>
<p>We’re talkin:</p>
<p>Winning Powerball ticket, rare</p>
<p>Cubs take the pennant, rare</p>
<p>Mr. C’s prime rib just how you like it, rare.</p>
<p>What I took for granted growing up…</p>
<p>(That invisible base layer of unity)</p>
<p>(That starter for a great pot of sauce)</p>
<p>that should be the normal childhood experience.</p>
<p>But, tragically, it isn’t.</p>
<p>So, thanks Mom and Dad. For the privilege.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Full Stack Radio</title>
      <link>https://jerodsanto.net/2019/06/full-stack-radio/</link>
      <pubDate>Mon, 10 Jun 2019 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2019/06/full-stack-radio/</guid>
      
      
      <description><![CDATA[<p>Adam Wathan invited me on his excellent <a href="http://www.fullstackradio.com">Full Stack Radio</a> podcast to talk about building <a href="https://changelog.com">Changelog.com</a> with Elixir and Phoenix. Topics include:</p>
<ul>
<li>How pattern matching works in Elixir and why it&rsquo;s more powerful than method overloading in other languages</li>
<li>How Elixir&rsquo;s pipe operator makes the transition from OO to functional programming more natural</li>
<li>Why you don&rsquo;t need to be intimidated by unfamiliar features like GenServers to use Elixir for web app development</li>
<li>Noticeable differences between working with Rails and Phoenix and what it was like to transition</li>
<li>How the Phoenix ORM makes n+1 queries impossible</li>
<li>Why background tasks are a lot easier in Elixir than in an ecosystem like PHP</li>
<li>What other tools and technology power the Changelog platform</li>
<li>How the Changelog Phoenix app is deployed</li>
</ul>
<p><a href="http://www.fullstackradio.com/116">You can listen to the episode right here.</a></p>]]></description>
      
      <content:encoded><![CDATA[<p>Adam Wathan invited me on his excellent <a href="http://www.fullstackradio.com">Full Stack Radio</a> podcast to talk about building <a href="https://changelog.com">Changelog.com</a> with Elixir and Phoenix. Topics include:</p>
<ul>
<li>How pattern matching works in Elixir and why it&rsquo;s more powerful than method overloading in other languages</li>
<li>How Elixir&rsquo;s pipe operator makes the transition from OO to functional programming more natural</li>
<li>Why you don&rsquo;t need to be intimidated by unfamiliar features like GenServers to use Elixir for web app development</li>
<li>Noticeable differences between working with Rails and Phoenix and what it was like to transition</li>
<li>How the Phoenix ORM makes n+1 queries impossible</li>
<li>Why background tasks are a lot easier in Elixir than in an ecosystem like PHP</li>
<li>What other tools and technology power the Changelog platform</li>
<li>How the Changelog Phoenix app is deployed</li>
</ul>
<p><a href="http://www.fullstackradio.com/116">You can listen to the episode right here.</a></p>
<p>If you like listening to me blather on about software development, also take a listen to <a href="https://changelog.com/backstage/4">Backstage #4</a> where I do exactly that with fellow-developer, Nick Janetakis. And, since we built our own custom platform, I can even do a shiny embed of that episode for ya! 🙌</p>
<p><audio data-theme="day" data-src="https://changelog.com/backstage/4/embed" src="https://cdn.changelog.com/uploads/backstage/4/backstage-4.mp3" preload="none" class="changelog-episode" controls></audio><script async src="https://jerodsanto.net//cdn.changelog.com/embed.js"></script></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Listen to me on SendGrid&#39;s podcast</title>
      <link>https://jerodsanto.net/2019/02/listen-to-me-on-sendgrids-podcast/</link>
      <pubDate>Wed, 13 Feb 2019 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2019/02/listen-to-me-on-sendgrids-podcast/</guid>
      
      
      <description><![CDATA[<p>I was honored to be interviewed by <a href="https://mobile.twitter.com/thinkingserious">Elmer Thomas</a> for SendGrid&rsquo;s very cool <a href="https://soundcloud.com/somecodingrequired">Some Coding Required</a> podcast. We talked about my favorite open source project, how open source has impacted my life, and gave some advice for open source contributors and maintainers.</p>
<p>My segment <a href="https://soundcloud.com/somecodingrequired/podcastinit6-oscon-2018-recap#t=7:19">starts right here</a>. Or listen to the whole thing. 14 minutes short.</p>
<iframe width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/563488455&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"></iframe>]]></description>
      
      <content:encoded><![CDATA[<p>I was honored to be interviewed by <a href="https://mobile.twitter.com/thinkingserious">Elmer Thomas</a> for SendGrid&rsquo;s very cool <a href="https://soundcloud.com/somecodingrequired">Some Coding Required</a> podcast. We talked about my favorite open source project, how open source has impacted my life, and gave some advice for open source contributors and maintainers.</p>
<p>My segment <a href="https://soundcloud.com/somecodingrequired/podcastinit6-oscon-2018-recap#t=7:19">starts right here</a>. Or listen to the whole thing. 14 minutes short.</p>
<iframe width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/563488455&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"></iframe>
]]></content:encoded>
    </item>
    
    <item>
      <title>Our 2018 State of the &#34;log&#34; address</title>
      <link>https://jerodsanto.net/2018/12/our-2018-state-of-the-log-address/</link>
      <pubDate>Wed, 19 Dec 2018 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2018/12/our-2018-state-of-the-log-address/</guid>
      
      
      <description><![CDATA[<p>It&rsquo;s been a good year. Take <a href="https://changelog.com/podcast/328">a listen</a>:</p>
<blockquote>
<p>We’re going behind the scenes to look back at 2018 as we prepare for 2019 and onward. We talk through our most popular episodes, most controversial episodes, and even some of our personal favorites.</p>
</blockquote>
<p><audio data-theme="night" data-src="https://changelog.com/podcast/328/embed" src="https://cdn.changelog.com/uploads/podcast/328/the-changelog-328.mp3" preload="none" class="changelog-episode" controls></audio><script async src="https://jerodsanto.net//cdn.changelog.com/embed.js"></script></p>]]></description>
      
      <content:encoded><![CDATA[<p>It&rsquo;s been a good year. Take <a href="https://changelog.com/podcast/328">a listen</a>:</p>
<blockquote>
<p>We’re going behind the scenes to look back at 2018 as we prepare for 2019 and onward. We talk through our most popular episodes, most controversial episodes, and even some of our personal favorites.</p>
</blockquote>
<p><audio data-theme="night" data-src="https://changelog.com/podcast/328/embed" src="https://cdn.changelog.com/uploads/podcast/328/the-changelog-328.mp3" preload="none" class="changelog-episode" controls></audio><script async src="https://jerodsanto.net//cdn.changelog.com/embed.js"></script></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>My Favorite Changelog Episodes of 2016</title>
      <link>https://jerodsanto.net/2016/12/my-favorite-changelog-episodes-of-2016/</link>
      <pubDate>Sat, 31 Dec 2016 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2016/12/my-favorite-changelog-episodes-of-2016/</guid>
      
      
      <description><![CDATA[<p>We shipped 44 episodes of The Changelog in 2016. That&rsquo;s a lot of conversations!</p>
<p>All of our guests are interesting and insightful in their own ways, but there are a handful that continue to resonate with me in my work and life since we spoke with them. Here they are, in chronological order:</p>
<h2 id="nadia-eghbal">Nadia Eghbal</h2>
<p>Nadia&rsquo;s outsider (turned insider) take on open source and funding sparked debates and actions throughout the year. This topic is so near-and-dear to our hearts that we started an entirely new podcast with Nadia and co-host Mikeal Rogers: <a href="https://changelog.com/rfc">Request For Commits</a>.</p>]]></description>
      
      <content:encoded><![CDATA[<p>We shipped 44 episodes of The Changelog in 2016. That&rsquo;s a lot of conversations!</p>
<p>All of our guests are interesting and insightful in their own ways, but there are a handful that continue to resonate with me in my work and life since we spoke with them. Here they are, in chronological order:</p>
<h2 id="nadia-eghbal">Nadia Eghbal</h2>
<p>Nadia&rsquo;s outsider (turned insider) take on open source and funding sparked debates and actions throughout the year. This topic is so near-and-dear to our hearts that we started an entirely new podcast with Nadia and co-host Mikeal Rogers: <a href="https://changelog.com/rfc">Request For Commits</a>.</p>
<p><audio src="https://cdn.changelog.com/uploads/podcast/193/the-changelog-193.mp3" preload="none" class="changelog-episode" data-src="https://changelog.com/podcast/193/embed" data-theme="night" controls></audio></p>
<h2 id="matt-mullenweg">Matt Mullenweg</h2>
<p>Matt&rsquo;s reputation preceeded him, but I was surprised at how soft-spoken, kind, and full of insights he is. I could talk to him for hours on end.</p>
<p><audio src="https://cdn.changelog.com/uploads/podcast/197/the-changelog-197.mp3" preload="none" class="changelog-episode" data-src="https://changelog.com/podcast/197/embed" data-theme="night" controls></audio></p>
<h2 id="richard-hipp">Richard Hipp</h2>
<p>This might actually be my #1 favorite. Sqlite&rsquo;s story is inspiring and Richard Hipp is a saint.</p>
<p><audio src="https://cdn.changelog.com/uploads/podcast/201/the-changelog-201.mp3" preload="none" class="changelog-episode" data-src="https://changelog.com/podcast/201/embed" data-theme="night" controls></audio></p>
<h2 id="yukihiro-matsumoto-matz">Yukihiro Matsumoto (Matz)</h2>
<p>This was the show I was most nervous to record, and with good reason. It&rsquo;s Matz! He did not disappoint.</p>
<p><audio src="https://cdn.changelog.com/uploads/podcast/202/the-changelog-202.mp3" preload="none" class="changelog-episode" data-src="https://changelog.com/podcast/202/embed" data-theme="night" controls></audio></p>
<p>(For a behind-the-scenes look at just how nervous we were for this show, listen to <a href="https://changelog.com/spotlight/2">my conversation with Katrina Owen on Spotlight #2</a>)</p>
<h2 id="pieter-hintjens">Pieter Hintjens</h2>
<p>When Adam booked this show with Pieter to talk about his life and pending death, I thought he was nuts. Turned out he was genius, as its perhaps the show we&rsquo;re most proud of and beloved by tens of thousands. We miss you Pieter!</p>
<p><audio src="https://cdn.changelog.com/uploads/podcast/205/the-changelog-205.mp3" preload="none" class="changelog-episode" data-src="https://changelog.com/podcast/205/embed" data-theme="night" controls></audio></p>
<h2 id="cory-doctorow">Cory Doctorow</h2>
<p>Cory is one of those guys who you can just wind up and watch him go. This was a challenging conversation, and I mean that in the best sense of the word.</p>
<p><audio src="https://cdn.changelog.com/uploads/podcast/221/the-changelog-221.mp3" preload="none" class="changelog-episode" data-src="https://changelog.com/podcast/221/embed" data-theme="night" controls></audio></p>
<h2 id="sandi-metz">Sandi Metz</h2>
<p>Last, but certainly not least. Sandi is so smart, so nice, and manages to teach you things while you don&rsquo;t even realize it. She deserves every bit of credit the greater Ruby community has given her over the years.</p>
<p><audio src="https://cdn.changelog.com/uploads/podcast/225/the-changelog-225.mp3" preload="none" class="changelog-episode" data-src="https://changelog.com/podcast/225/embed" data-theme="night" controls></audio></p>
<h2 id="up-next">Up Next</h2>
<p>With guests like Linda Liukas, Steve Klabnik, Ashley Willams, and Nathan Sobo already booked, 2017 is shaping up to be another <em>excellent</em> time to be a <a href="https://changelog.com/subscribe">Changelog listener</a>.</p>
<script async src="https://jerodsanto.net//cdn.changelog.com/embed.js"></script>
]]></content:encoded>
    </item>
    
    <item>
      <title>5 Answers For Coder Catchup Episode 100</title>
      <link>https://jerodsanto.net/2016/12/5-answers-for-coder-catchup-episode-100/</link>
      <pubDate>Thu, 22 Dec 2016 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2016/12/5-answers-for-coder-catchup-episode-100/</guid>
      
      
      <description><![CDATA[<p>To celebrate episode 100 of Jaymie Jones&rsquo; <a href="http://codercatchup.com">Coder Catchup</a> podcast, Jaymie reached out to a handful of people and asked us 5 questions about web development, which he then read on the show.</p>
<p>I was honored to be on the show alongside such great web folks as Chris Coyier, Wes Bos, Adam Stacoviak, Sarah Allen, and more.</p>
<p>Since I wrote my answers to Jaymie in Markdown and shipped them off for him to read, I figured I&rsquo;d also post them here as well.</p>]]></description>
      
      <content:encoded><![CDATA[<p>To celebrate episode 100 of Jaymie Jones&rsquo; <a href="http://codercatchup.com">Coder Catchup</a> podcast, Jaymie reached out to a handful of people and asked us 5 questions about web development, which he then read on the show.</p>
<p>I was honored to be on the show alongside such great web folks as Chris Coyier, Wes Bos, Adam Stacoviak, Sarah Allen, and more.</p>
<p>Since I wrote my answers to Jaymie in Markdown and shipped them off for him to read, I figured I&rsquo;d also post them here as well.</p>
<p>But still, you should <a href="http://codercatchup.com/episodes/55598-episode-100-one-hundred">listen to his Episode 100</a> since there are many great answers to the questions below. Enjoy!</p>
<h3 id="1-if-you-were-starting-fresh-what-would-you-primarily-focus-on-eg-as-a-new-web-designerdeveloper">1. If you were starting fresh, what would you primarily focus on? (e.g. as a new web designer/developer)</h3>
<p>Beginners <em>shouldn&rsquo;t focus</em> much, I don&rsquo;t think. This is why many 101 level courses are surveys of an entire field. It&rsquo;s important to get the lay of the land before focusing in on one or a few things. How else can you get a sense of what&rsquo;s worth focusing on?</p>
<p>In web development, I think that means learning how the web works as a system. Clients, servers, DNS, HTTP(s), and HTML are the underpinnings of the web that should be understood first. Then, I&rsquo;d try to use that knowledge to publish a website, soup-to-nuts. Once you can do that on your own, the opportunities really open up for you.</p>
<h3 id="2-what-is-one-piece-of-advice-you-think-is-most-important-for-web-developersdesigners-going-forward">2. What is one piece of advice you think is most important for web developers/designers going forward?</h3>
<p>Generalize. Technologies come to fame then fall from favor faster than boy bands. If you gained expertise in Thing A by burying your head in it and ignoring Things B, C, and D, your skills may be quite marketable today and <em>completely irrelevant</em> tomorrow. On the other hand, if you keep your eyes on Things A through D while working with maybe B and C, you stand a much better chance of remaining valuable as things change.</p>
<p>If we look at our ongoing education in terms of breadth and depth, my advice (and practice) is to go for breadth habitually and go for depth when the need arises.</p>
<h3 id="3-what-would-you-do-to-get-out-of-a-slump-or-plateau-with-your-web-career">3. What would you do to get out of a slump or plateau with your web career?</h3>
<p>This depends on what you mean by slump, but I&rsquo;ll assume it means waning interest in the web and my work on it. In this case, I&rsquo;ve found that side projects are great slump busters. Working on a side project that I care about energizes me and helps me power through times of low motivation, especially if they&rsquo;re challenging and push me outside of my comfort zone. Frankly, that&rsquo;s one of my favorite things about working on Changelog. Producing awesome content for developers is an entirely different challenge from writing software itself. Plus, it usually means hacking on a few things along the way!</p>
<h3 id="4-what-is-1-thing-you-would-like-to-see-web-developersdesigners-doing-more-of">4. What is 1 thing you would like to see web developers/designers doing more of?</h3>
<p>I&rsquo;d love to see web developers and designers focus more on the fundamentals and less on bleeding edge features and new techniques. Get the basics right: quality content, ease of use, and speed of delivery. The rest is just icing on the cake.</p>
<h3 id="5-one-piece-of-advice-of-your-choosing">5. One piece of advice of your choosing</h3>
<p>Listen to <a href="https://changelog.com">more podcasts</a>. :)</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Safari does not trigger click events on anchors or buttons containing SVG elements</title>
      <link>https://jerodsanto.net/2015/08/safari-does-not-trigger-click-events-on-anchors-or-buttons-containing-svg-elements/</link>
      <pubDate>Tue, 18 Aug 2015 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2015/08/safari-does-not-trigger-click-events-on-anchors-or-buttons-containing-svg-elements/</guid>
      
      
      <description><![CDATA[<p>This fact will ruin your day if, for instance, you are expecting Rails&rsquo; jQuery UJS to Just Work when you add a <code>remote: true</code> or <code>method: :post</code> to that <code>link_to</code> helper. You know, something like this:</p>
<p>It&rsquo;ll work in Chrome. It&rsquo;ll work in Firefox. But Safari on the other hand&hellip;</p>
<p>Safari will not trigger a <code>click</code> event and whatever JavaScript you were expecting to fire will not. How to fix? In your CSS:</p>]]></description>
      
      <content:encoded><![CDATA[<p>This fact will ruin your day if, for instance, you are expecting Rails&rsquo; jQuery UJS to Just Work when you add a <code>remote: true</code> or <code>method: :post</code> to that <code>link_to</code> helper. You know, something like this:</p>
<p>It&rsquo;ll work in Chrome. It&rsquo;ll work in Firefox. But Safari on the other hand&hellip;</p>
<p>Safari will not trigger a <code>click</code> event and whatever JavaScript you were expecting to fire will not. How to fix? In your CSS:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="nt">svg</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">pointer-events</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This took me way too long to diagnose. Hopefully this post saves you some time/money!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>New Mac: Eventual Installs</title>
      <link>https://jerodsanto.net/2015/04/new-mac-eventual-installs/</link>
      <pubDate>Sat, 11 Apr 2015 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2015/04/new-mac-eventual-installs/</guid>
      
      
      <description><![CDATA[<p>It&rsquo;s been a few weeks since I started the process of setting up my new laptop. I <a href="https://jerodsanto.net/2015/03/new-mac-instant-installs/">wrote previously</a> about which apps and tweaks I made immediately after unboxing. Now I&rsquo;m back to share what I&rsquo;ve done since.</p>
<p>The most surprising thing to me is that I&rsquo;m <em>still</em> dual wielding.</p>
<img alt="The machines have at least switched sides. Progress!" src="http://jerodsanto.net/drop/macs-side-by-side.jpg" style="max-width: 400px">
<p>It&rsquo;s been pretty nice to have the new machine always plugged in and configured with multiple monitors and the old machine floating between the desk, upstairs, and my travel bag. It&rsquo;s <em>almost</em> as good as having <a href="http://www.thewire.com/technology/2013/03/dave-morin-path-interview/63613/">a day phone and a night phone</a>.</p>]]></description>
      
      <content:encoded><![CDATA[<p>It&rsquo;s been a few weeks since I started the process of setting up my new laptop. I <a href="https://jerodsanto.net/2015/03/new-mac-instant-installs/">wrote previously</a> about which apps and tweaks I made immediately after unboxing. Now I&rsquo;m back to share what I&rsquo;ve done since.</p>
<p>The most surprising thing to me is that I&rsquo;m <em>still</em> dual wielding.</p>
<img alt="The machines have at least switched sides. Progress!" src="http://jerodsanto.net/drop/macs-side-by-side.jpg" style="max-width: 400px">
<p>It&rsquo;s been pretty nice to have the new machine always plugged in and configured with multiple monitors and the old machine floating between the desk, upstairs, and my travel bag. It&rsquo;s <em>almost</em> as good as having <a href="http://www.thewire.com/technology/2013/03/dave-morin-path-interview/63613/">a day phone and a night phone</a>.</p>
<p>Let&rsquo;s see what&rsquo;s changed since last time.</p>
<h2 id="apps">Apps</h2>
<p>I&rsquo;m still only installing apps out of necessity, but it turns out I need a lot of &rsquo;em!</p>
<img alt="Page 2 of LaunchPad" src="http://jerodsanto.net/drop/eventual-installs.png" style="max-width: 512px;">
<ul>
<li>
<p><a href="http://tapbots.com/tweetbot/mac/">Tweetbot</a> — the best-in-breed Twitter client. It&rsquo;s getting a bit long in the tooth, but it&rsquo;s still indespensible. I will buy version 2 as soon as it hits the App Store.</p>
</li>
<li>
<p><a href="http://audacity.sourceforge.net">Audacity</a> — I was hoping to get by with <em>just</em> Audio Hijack, but it turns out Audacity is still better when recording many takes of the same content (for ad reads, intros, etc.). What&rsquo;s worse: it is brutally ugly on a retina display.</p>
</li>
<li>
<p><a href="http://www.rdio.com">Rdio</a> — I love the web, but for the web services I use all-day-every-day (Slack, Rdio, Harvest), I&rsquo;ll take the desktop app, please. Even if that app is just wrapping a web view, as is the case with Rdio.</p>
</li>
<li>
<p><a href="http://flexibits.com/Fantastical">Fantastical 2</a> — So much better than Apple&rsquo;s Calendar it isn&rsquo;t even funny.</p>
</li>
<li>
<p><a href="http://www.skype.com/">Skype</a> — Skype could&rsquo;ve gone away if Google hadn&rsquo;t attached Hangouts to a dead, rotting corpse.</p>
</li>
<li>
<p><a href="http://rowanj.github.io/gitx/">GitX</a> — I hop back and forth between Git&rsquo;s CLI and GitX. GitX is great for staging changes, especially when staging hunks. There are many GitX forks out there, but I&rsquo;ve found Rowan James&rsquo; to be the best one.</p>
</li>
<li>
<p><a href="http://glui.me">Glui</a> — For capturing, annotating, and sharing screenshots. It&rsquo;ll post to CloudApp (boo!) or Dropbox (yay!), but mostly I just drag images from Glui to whatever Slack room I&rsquo;m currently active in.</p>
</li>
<li>
<p><a href="https://eggerapps.at/postico/">Postico</a> — An awesome PostgreSQL client from a former Sequel Pro dev and current maintainer of <a href="http://postgresapp.com">Postgres.app</a>.</p>
</li>
<li>
<p><a href="http://notational.net">Notational Velocity</a> — there is <a href="http://brettterpstra.com/projects/nvalt/">a fork</a> that&rsquo;s quite popular as well, but I have been happy with NV for private note taking.</p>
</li>
<li>
<p><a href="https://panic.com/transmit/">Transmit</a> — the only thing surprising about seeing Panic&rsquo;s SFTP/S3 client in this list is that it didn&rsquo;t make the &ldquo;Instant Installs&rdquo; list. Super useful.</p>
</li>
</ul>
<h2 id="tweaks">Tweaks</h2>
<p>I can&rsquo;t recall <em>all</em> of the little tweaks to the stock OS that I&rsquo;ve made since my last post.</p>
<p>However, I did revisit my <code>osx</code> script in my dotfiles, which has tons of goodies in it. <a href="https://github.com/jerodsanto/dotfiles/blob/master/osx">Check that out</a> if you&rsquo;re interested in streamlining OS X for a hacker&rsquo;s workflow.</p>
<h2 id="next">Next</h2>
<p>So far I&rsquo;ve been able to go without Sparrow and iTerm, but it hasn&rsquo;t been easy. If I go the distance with Terminal.app and/or Mail.app I&rsquo;ll probably write up a little something about it.</p>
<p>Let me know if you&rsquo;re interested in that or not. I&rsquo;ve been running low on writing time lately!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>New Mac: Instant Installs</title>
      <link>https://jerodsanto.net/2015/03/new-mac-instant-installs/</link>
      <pubDate>Sat, 21 Mar 2015 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2015/03/new-mac-instant-installs/</guid>
      
      
      <description><![CDATA[<p>Spring is in the air and a shiny new 13&quot; MacBook Pro is in my office<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>It will replace the mid-2011 13&quot; MacBook Air that I&rsquo;m writing this post with. Why aren&rsquo;t I writing it on my new machine? Because for the first time ever I&rsquo;ve decided <em>not</em> to migrate. My current install has been through 6 major operating system upgrades, countless experiments, untold hacks, and more. It&rsquo;s time to start fresh.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Spring is in the air and a shiny new 13&quot; MacBook Pro is in my office<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>It will replace the mid-2011 13&quot; MacBook Air that I&rsquo;m writing this post with. Why aren&rsquo;t I writing it on my new machine? Because for the first time ever I&rsquo;ve decided <em>not</em> to migrate. My current install has been through 6 major operating system upgrades, countless experiments, untold hacks, and more. It&rsquo;s time to start fresh.</p>
<img alt="Dual Wielding" src="http://jerodsanto.net/drop/macs-side-by-side.jpg" style="max-width: 400px">
<p>I&rsquo;ll be running the two machines side-by-side for the next couple weeks, only loading things I need on to the new one. This means every app has to prove itself to me again. No exceptions.</p>
<h2 id="apps">Apps</h2>
<p>I plan to write up a few posts as I make the transition, documenting what makes the cut and what doesn&rsquo;t. This post covers the instant installs that I didn&rsquo;t hesitate to install on day one. Since this is a work machine, these apps are all Serious Business™. You won&rsquo;t find games or family tree software in this lot. These apps help me make a living.</p>
<img alt="This pic might be my first and last real use for LaunchPad" src="http://jerodsanto.net/drop/instant-installs.png" style="max-width: 512px;">
<ul>
<li>
<p><a href="http://dropbox.com">Dropbox</a> — For file sync and sharing, accept no substitutes. It&rsquo;s an amazing feeling to load up a new machine with Dropbox, wait a few minutes, and have all my most critical files downloaded and right where I left them.</p>
</li>
<li>
<p><a href="http://sublimetext.com/3">Sublime Text</a> — Speaking of dual wielding, I&rsquo;m one of those weirdos who uses two text editors: Vim and Sublime Text. ST 3 is an excellent editor, and I&rsquo;m excited to see its author back from hiatus and making progress once again.</p>
</li>
<li>
<p><a href="http://google.com/chrome">Chrome</a> — I fell out of love with Chrome as a day-to-day browser<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, but it still has best-in-class Dev Tools, which is the only reason it makes the cut.</p>
</li>
<li>
<p><a href="http://rogueamoeba.com/audiohijack/">Audio Hijack</a> — Because podcasting.</p>
</li>
<li>
<p><a href="http://kapeli.com/dash">Dash</a> — The best documentation browser I&rsquo;ve ever used. I almost forgot that it also makes docs available offline until a recent trip to the boondocks reminded me. So great.</p>
</li>
<li>
<p><a href="http://www.acqualia.com/soulver/">Soulver</a> — I always have one or two Soulver sheets open while working. It sits in the sweet spot between Spotlight math and a full-on spreadsheet.</p>
</li>
<li>
<p><a href="http://manytricks.com/moom/">Moom</a> — I wish I didn&rsquo;t need Moom. OS X should handle window management better<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, but it doesn&rsquo;t. Moom fixes it.</p>
</li>
<li>
<p><a href="http://slack.com">Slack</a> — Slack is team communication done right. Their Mac app, like everything else, is an absolute joy to use.</p>
</li>
</ul>
<h2 id="tweaks">Tweaks</h2>
<p>iCloud now brings many settings with it. I was pleasently surprised to see even my text shortcuts synced over automatically.</p>
<img alt="&shrug;" src="http://jerodsanto.net/drop/text-shortcuts.png" style="max-width: 400px;">
<p>Still, there are many tweaks that don&rsquo;t fall inside iCloud&rsquo;s purview. Here are a few I couldn&rsquo;t live without:</p>
<ul>
<li>
<p><a href="https://jerodsanto.net/2015/02/i-fought-the-rsi-and-the-rsi-almost-won/">Make Caps Lock key act as ⌃, disable ⌃ key altogether</a></p>
</li>
<li>
<p>Install <a href="http://font.ubuntu.com">Ubuntu Mono</a>, which is my favorite coding font</p>
</li>
<li>
<p>Hot Corners: upper left = Mission Control, lower right = Sleep Display</p>
</li>
</ul>
<h2 id="next">Next</h2>
<p>There are a few apps I&rsquo;m trying to live without<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> and whole boat load of things I haven&rsquo;t set up yet.</p>
<p>I&rsquo;ll write another post next week with more apps I end up installing and tweaks I bring over. Follow along on <a href="https://twitter.com/jerodsanto">Twitter</a> or via <a href="https://jerodsanto.net/feed.xml">RSS</a><sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> so you won&rsquo;t miss it!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>3.1 GHz i7, 16GB of RAM, 256GB SSD (for the curious)&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>I use Safari as my main browser. Having all my browsers synced between my devices via iCloud is its killer feature.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>This is the <em>one</em> thing Windows does better than OS X, in my experience.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Sparrow and iTerm, to name two.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>It lives!&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>What Continuity Means to Me</title>
      <link>https://jerodsanto.net/2015/03/what-continuity-means-to-me/</link>
      <pubDate>Fri, 13 Mar 2015 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2015/03/what-continuity-means-to-me/</guid>
      
      
      <description><![CDATA[<p>The features that had me the most excited with the advent of iOS 8 and Yosemite fall under Apple&rsquo;s <a href="https://www.apple.com/ios/whats-new/continuity/">Continuity</a> umbrella. From the marketing page:</p>
<blockquote>
<p>Apple products have always been designed to work together beautifully. But now they may really surprise you. With iOS 8 and OS X Yosemite, you’ll be able to do more wonderful things than ever before.</p>
</blockquote>
<p>Answer phone calls and send SMS messages from my laptop? Yes! AirDrop between my phone and my Mac? I&rsquo;ve been asking for that since 2013!</p>]]></description>
      
      <content:encoded><![CDATA[<p>The features that had me the most excited with the advent of iOS 8 and Yosemite fall under Apple&rsquo;s <a href="https://www.apple.com/ios/whats-new/continuity/">Continuity</a> umbrella. From the marketing page:</p>
<blockquote>
<p>Apple products have always been designed to work together beautifully. But now they may really surprise you. With iOS 8 and OS X Yosemite, you’ll be able to do more wonderful things than ever before.</p>
</blockquote>
<p>Answer phone calls and send SMS messages from my laptop? Yes! AirDrop between my phone and my Mac? I&rsquo;ve been asking for that since 2013!</p>
<blockquote class="twitter-tweet" lang="en"><p>Why, oh why, can’t I AirDrop from my Mac to my phone? Seems only logical. Mavericks?</p>&mdash; Jerod Santo (@jerodsanto) <a href="https://twitter.com/jerodsanto/status/382505294556721152">September 24, 2013</a></blockquote> <script async src="https://jerodsanto.net//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Unfortunately, sometimes the most-awaited features become the most disappointing.</p>
<blockquote class="twitter-tweet" lang="en"><p>The ONE feature I was looking forward to in iOS 8 / Yosemite – AirDrop between phone and mac – has never worked for me.</p>&mdash; Jerod Santo (@jerodsanto) <a href="https://twitter.com/jerodsanto/status/558302237034688512">January 22, 2015</a></blockquote> <script async src="https://jerodsanto.net//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>It turns out that AirDrop&rsquo;s problem is that my mid-2011 Macbook Air is too old to support the feature<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. OK. Apple gets a pass on that one. But what of the other promises? Can Continuity keep them? Not so much:</p>
<blockquote class="twitter-tweet" lang="en"><p>Continuity means never having to say “I missed your iMessage.”</p>&mdash; Jerod Santo (@jerodsanto) <a href="https://twitter.com/jerodsanto/status/557733484417273856">January 21, 2015</a></blockquote> <script async src="https://jerodsanto.net//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Now instead of just my phone buzzing incessantly when someone iMessages me, <em>all</em> of my devices beep and buzz. It&rsquo;s like an Apple-branded flash mob. That&rsquo;s not all.</p>
<blockquote class="twitter-tweet" lang="en"><p>Continuity means swiping up/away a notification on my phone only to have it appear as unread on my laptop, iPad, and —you guessed it— phone.</p>&mdash; Jerod Santo (@jerodsanto) <a href="https://twitter.com/jerodsanto/status/575471839889092608">March 11, 2015</a></blockquote> <script async src="https://jerodsanto.net//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Am I taking crazy pills or should swiping away a notification mark it as acknowledged? iOS doesn&rsquo;t think so. And thanks to Continuity, Yosemite doesn&rsquo;t think so either. &ldquo;Connected like never before&rdquo; means that both operating systems now propagate their annoying bugs to the other. And there&rsquo;s more.</p>
<blockquote class="twitter-tweet" lang="en"><p>Continuity means my Mac telling me I missed a call right after I hang up the call.</p>&mdash; Jerod Santo (@jerodsanto) <a href="https://twitter.com/jerodsanto/status/564078159369601024">February 7, 2015</a></blockquote> <script async src="https://jerodsanto.net//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Mac-based phone calls might be the most disappointing feature of the lot. The ring is delayed. The connection often fails. When I silence a call on my phone it rings on my Mac instead. It&rsquo;s a hot mess<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<hr>
<p>There&rsquo;s no doubt in my mind that these things are difficult to get right. It&rsquo;s a cocktail of software, hardware interfaces, networking protocols<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, context, presence, and more. But still, I think we&rsquo;d all be better off if Apple would&rsquo;ve held off on Continuity until they really had it figured out. Half-baked features are sometimes worse than no features at all.</p>
<p>Oh well, maybe they&rsquo;ll get it right <a href="http://www.extremetech.com/computing/199090-ios-9-rumored-to-focus-on-stability-performance-increases">in iOS 9</a>.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>We&rsquo;ll see if it works when my shiny new 13&quot; Retina Macbook Pro arrives next week!&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Ironically, as I was writing this a call came in from my phone, which was in the bedroom. I answered it from my Mac and it worked flawlessly. <a href="http://cl.ly/image/1Z190w1F1O0p">See what I mean</a>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>I <em>believe</em> the delay between the phone ringing on the phone and the Mac is Bluetooth-related.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Trailing Conditionals Considered Harmful Unless Used Sparingly</title>
      <link>https://jerodsanto.net/2015/03/trailing-conditionals-considered-harmful-unless-used-sparingly/</link>
      <pubDate>Sun, 08 Mar 2015 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2015/03/trailing-conditionals-considered-harmful-unless-used-sparingly/</guid>
      
      
      <description><![CDATA[<p>One Ruby feature that I fell in love with back in the day is the ability to tack conditionals on at the end of a line. For example, this bit of code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">some_condition?</span>
</span></span><span class="line"><span class="cl">  <span class="n">object</span><span class="o">.</span><span class="n">perform_some_action</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Can be expressed as a one-liner, like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">object</span><span class="o">.</span><span class="n">perform_some_action</span> <span class="k">if</span> <span class="n">some_condition?</span>
</span></span></code></pre></div><p>It&rsquo;s a small difference, but the latter form often maps more directly to how the author thinks about the problem. It feels even better when teamed with the <code>unless</code> keyword:</p>]]></description>
      
      <content:encoded><![CDATA[<p>One Ruby feature that I fell in love with back in the day is the ability to tack conditionals on at the end of a line. For example, this bit of code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">some_condition?</span>
</span></span><span class="line"><span class="cl">  <span class="n">object</span><span class="o">.</span><span class="n">perform_some_action</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Can be expressed as a one-liner, like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">object</span><span class="o">.</span><span class="n">perform_some_action</span> <span class="k">if</span> <span class="n">some_condition?</span>
</span></span></code></pre></div><p>It&rsquo;s a small difference, but the latter form often maps more directly to how the author thinks about the problem. It feels even better when teamed with the <code>unless</code> keyword:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">object</span><span class="o">.</span><span class="n">perform_some_action</span> <span class="k">unless</span> <span class="n">some_condition?</span>
</span></span></code></pre></div><p>After years of writing and reading code like this, I&rsquo;ve slowly grown cold on the style.</p>
<h1 id="why">Why</h1>
<p>The reason why I&rsquo;m bearish on trailing conditionals may best be expressed by a road sign I saw on a recent road trip down Interstate 80:</p>
<p><img src="http://jerodsanto.net/drop/i-80-closed.jpg" alt="Not the actual sign I saw, but close enough"></p>
<p>Notice the trailing conditional? A lot can go wrong with signs like these if the driver doesn&rsquo;t make it to the final line of the text. Why might that happen?</p>
<ul>
<li>The sign could be blocked by some obstruction until the last second</li>
<li>The driver could be distracted by kids, the radio, their phone, etc. until it&rsquo;s too late</li>
<li>A sign-related exit could be imminent with the driver in the wrong lane<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
</ul>
<p>In this case, <strong>WHEN FLASHING</strong> is the key indicator on the sign. Why is it the last thing mentioned? In the world of journalism they call this <a href="http://en.wiktionary.org/wiki/bury_the_lead">burying the lead</a>.</p>
<p>If the sign designer place <strong>WHEN FLASHING</strong> first, the driver could often skip the rest of the text altogether<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. This saves cognitive overhead that the driver can use elsewhere and avoids potential disasters that might occur if the conditional isn&rsquo;t understood in time.</p>
<p>In a slightly-tangential way, trailing conditionals violate the <a href="http://en.wikipedia.org/wiki/Principle_of_least_astonishment">Principle of Least Surprise</a>. This principle — as most important things in life — made its way in to a Mitch Hedberg joke, in which he picks a fight with the phrase &ldquo;Do Not Disturb&rdquo;:</p>
<iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/194783967&amp;color=ff5500&amp;auto_play=false&amp;hide_related=false&amp;show_comments=false&amp;show_user=false&amp;show_reposts=false"></iframe>
<p>The problem is exacerbated when driving 80 MPH on the interstate, but it exists in our code as well. The trailing conditional feels great when you&rsquo;re <em>writing</em> the code, but it often makes it harder to <em>read</em>. This is most obvious when the operation that precedes the conditional is verbose. Take this fake code, for instance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">call_this_really_long_method_that_is_probably_too_long_but_that_will_not_stop_us</span> <span class="k">unless</span> <span class="n">some_condition?</span>
</span></span></code></pre></div><p>What if you didn&rsquo;t scroll over to see the <code>unless</code> at the end? You wouldn&rsquo;t know what&rsquo;s going on at all. Admittedly, method names of this length are rare, but <em>it is</em> common to have trailing conditionals nested inside other control structures that have the same effect<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.</p>
<p><a href="http://blogs.msdn.com/b/oldnewthing/archive/2007/04/06/2036150.aspx">Code is read much more often than it is written</a>, so we need to optimize for readability over writeability<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>. Trailing conditionals tend to do the opposite.</p>
<h2 id="but">But</h2>
<p>As with most things in software (and writing), there are exceptions. Some uses of trailing conditionals improve readability. The best case for them in my experience is with <a href="http://en.wikipedia.org/wiki/Guard_(computer_science)">guard clauses</a>. Guard clauses have a few characteristics that make them quite readable with trailing conditionals:</p>
<ul>
<li>They occur at the top of a method, so they are rarely nested themselves</li>
<li>They often return or raise an error, which are brief statements</li>
<li>There are often a few guard clauses together, so vertical brevity aides reading</li>
</ul>
<p>Take a look at this method which returns a <code>price_range</code> string for a given object that responds to <code>price_minimum</code> and <code>price_maximum</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">price_range</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="vi">@price_range</span> <span class="k">if</span> <span class="n">defined?</span> <span class="vi">@price_range</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="s2">&#34;&#34;</span> <span class="k">unless</span> <span class="n">price_minimum</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="s2">&#34;&#34;</span> <span class="k">unless</span> <span class="n">price_maximum</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ... code to determine `minimum` and `maximum` ...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="vi">@price_range</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">minimum</span><span class="si">}</span><span class="s2">-</span><span class="si">#{</span><span class="n">maximum</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>The first line <a href="http://en.wikipedia.org/wiki/Memoization">memoizes</a> the <code>price_range</code>, since this is apparently an expensive computation. Lines 2 and 3 are guard clauses. What would this look like with traditional conditionals?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">price_range</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="n">defined?</span> <span class="vi">@price_range</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="vi">@price_range</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">unless</span> <span class="n">price_minimum</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">unless</span> <span class="n">price_maximum</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ... code to determine `minimum` and `maximum` ...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="vi">@price_range</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">minimum</span><span class="si">}</span><span class="s2">-</span><span class="si">#{</span><span class="n">maximum</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>This code requires more vertical work to parse. There&rsquo;s a 3rd form it could take, which is to put the conditionals first and still keep each one a one-liner:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">price_range</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="n">defined?</span> <span class="vi">@price_range</span> <span class="k">return</span> <span class="vi">@price_range</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="o">!</span><span class="n">price_minimum</span> <span class="k">return</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="o">!</span><span class="n">price_maximum</span> <span class="k">return</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ... code to determine `minimum` and `maximum` ...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="vi">@price_range</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">minimum</span><span class="si">}</span><span class="s2">-</span><span class="si">#{</span><span class="n">maximum</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>This works for me, but I prefer the return-first form because any time a method returns early we want to know about that <em>ASAP</em>.</p>
<h2 id="so">So</h2>
<p>Think twice before slinging around trailing conditionals. They put the cart before the horse and in extreme cases they cause the reader to miss the horse altogether. This makes them often less readable than the traditional form.</p>
<p>Or maybe Mitch was right and we all just need to read faster!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>This is actually what happened to me. I barely deciphered the correct meaning in time.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>The road being closed is the exception, not the common case. This means the lights will rarely flash and the sign is most often irrelevant.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>This is <a href="http://stackoverflow.com/a/578318">yet another reason</a> that I advocate for 80-characters or less per line.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>The two are often coupled, but are sometimes at odds.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>I Fought the RSI and the RSI (Almost) Won</title>
      <link>https://jerodsanto.net/2015/02/i-fought-the-rsi-and-the-rsi-almost-won/</link>
      <pubDate>Thu, 26 Feb 2015 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2015/02/i-fought-the-rsi-and-the-rsi-almost-won/</guid>
      
      
      <description><![CDATA[<p>I’ve been battling pain in my left hand<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and wrist for the past couple of years. There are but 3 things on my person that I need to bring to work each day:</p>
<ol>
<li>A Brain</li>
<li>Eyes</li>
<li>Hands</li>
</ol>
<p>Losing any of these faculties would send me down a new career path, so I’m quite protective of these particular body parts. This hand pain worried me.</p>
<p>It started out in my forearm and felt like fatigue more than anything else. Over time, the pain slid down my arm and in to my wrist. Finally, it rested on the left side of my hand and in my pinky. It wasn’t debilitating, but it was annoying. And getting worse.</p>]]></description>
      
      <content:encoded><![CDATA[<p>I’ve been battling pain in my left hand<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and wrist for the past couple of years. There are but 3 things on my person that I need to bring to work each day:</p>
<ol>
<li>A Brain</li>
<li>Eyes</li>
<li>Hands</li>
</ol>
<p>Losing any of these faculties would send me down a new career path, so I’m quite protective of these particular body parts. This hand pain worried me.</p>
<p>It started out in my forearm and felt like fatigue more than anything else. Over time, the pain slid down my arm and in to my wrist. Finally, it rested on the left side of my hand and in my pinky. It wasn’t debilitating, but it was annoying. And getting worse.</p>
<p>So I did what all Internet denizens do: <strong>I Googled the crap out of it.</strong></p>
<h2 id="the-diagnosis">The diagnosis</h2>
<p>I had been warned of <a href="http://en.wikipedia.org/wiki/Carpal_tunnel_syndrome">Carpal Tunnel Syndrome</a> (CTS) before, and it scared the dog doo out of me. I suspected that might be what it was, but my symptoms didn’t quite line up.</p>
<p>I <em>was</em> playing Racquetball once or twice a week when the pain began, so I thought it might be related. The pain started in my forearm, so I considered that it might be some form of <a href="http://en.wikipedia.org/wiki/Tennis_elbow">Tennis Elbow</a>.</p>
<p>Ruling out Tennis Elbow was easy: I quit playing Racquetball for awhile. The pain persisted.</p>
<p>I won’t share all the Internet avenues and back alleys I went down in search of diagnoses and solutions. I’ll just say that by the end of it, I was pretty sure I was experiencing some kind of <a href="http://en.wikipedia.org/wiki/Repetitive_strain_injury">Repetitive Strain Injury</a> (RSI).</p>
<p>What sucks about RSI is it starts out minor and slowly gets worse until you can’t handle the pain anymore. You can’t simply tough it out. You have to change your habits or give up the activity that’s causing strain altogether.</p>
<p><img src="http://jerodsanto.net/drop/rsi-gloves.jpg" alt="Not cool, dudes"></p>
<p>With visions of surgery and hand braces dancing in my head, I set out to find some solutions.</p>
<h2 id="the-non-solutions">The non-solutions</h2>
<p>It was a long and windy road of non-solutions. A lot of the advice out there says to strengthen and stretch your hands. To help in that effort, I purchased a set of <a href="http://www.amazon.com/exec/obidos/ASIN/B000P8Q03I/ref=nosim&amp;tag=jerodsanto-20">Chinese Stress Balls</a>. I took regular breaks from typing and used these stress balls instead.</p>
<p><a href="http://www.amazon.com/exec/obidos/ASIN/B000P8Q03I/ref=nosim&amp;tag=jerodsanto-20"><img src="http://jerodsanto.net/drop/chinese-stress-balls.jpg" alt="Shiny!"></a></p>
<p>Aside from being fun to play with — and an infinite source of juvenile jokes — these asian delights did bupkis to reduce my pain.</p>
<p>Next up on the hit list (and a more common purchase than the Stress Balls, for sure) was <a href="http://www.amazon.com/exec/obidos/ASIN/B00CYX54C0/ref=nosim&amp;tag=jerodsanto-20">an ergonomic keyboard</a>.</p>
<p><img src="http://jerodsanto.net/drop/microsoft-sculpt.jpg" alt="The mouse is heavy, but it grew on me"></p>
<p>Ergonomics are a huge part of curing (and preventing) RSI. Switching to an ergonomic keyboard was the #1 piece of advice that I came across on the web.</p>
<p>And when it comes to ergonomic keyboards, Microsoft’s is one of the best<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. <a href="http://www.amazon.com/exec/obidos/ASIN/B00CYX54C0/ref=nosim&amp;tag=jerodsanto-20">The Sculpt</a> had just been released when I was shopping, so I bought it instead of its bulkier predecessor, <a href="http://www.amazon.com/exec/obidos/ASIN/B000A6PPOK/ref=nosim&amp;tag=jerodsanto-20">the  Natural</a>.</p>
<p>I quickly fell in love with the Sculpt and use it to this day. The mouse: not so much, but it grew on me<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>. The only problem with it: <strong>my pain didn’t go away!</strong></p>
<p>At this point I was pretty upset and had almost resigned myself to a life of hand pain when I came across <a href="https://www.math.ucdavis.edu/~greg/pinky-rsi.html">the one person on the Internet who had my <em>exact</em> symptoms</a>!</p>
<h2 id="the-solution">The solution</h2>
<p>So much of what Greg was saying had me pegged:</p>
<blockquote>
<p>I would suppose that many people have had this problem without a complete diagnosis or an optimal treatment plan.</p>
</blockquote>
<p>Me.</p>
<blockquote>
<p>The right pinky is used for 16 keys, and the left pinky for 10 keys, while every other finger is used for at most 8 keys. This may not matter much for typing text in English. However, a lot of computer typing these days is nothing like English</p>
</blockquote>
<p>Me again.</p>
<blockquote>
<p>In my case, this led to extreme pinky overuse and, worse, typing with my hand twisted (ulnar deviation)</p>
</blockquote>
<p>ME ME ME. This image he included in his post perfectly mirrored what my left hand looks like when I type:</p>
<p><img src="http://jerodsanto.net/drop/ulnar-deviation.png" alt="It’s called “ulnar deviation” which is a fancy way of saying “OUCH”"></p>
<p>By this point in his article I couldn’t read the solution fast enough:</p>
<blockquote>
<p>I tried a variety of standard remedies that may or may not have helped (see below), but the most important long-term solution was a non-standard keyboard mapping.</p>
</blockquote>
<p>You can read the whole thing to learn about how Greg remapped certain keys to reduce usage of his right pinky, but I didn’t need to. With this knowledge in hand, I came to grips with they key which was really stressing my left pinky out: the <em>blasted</em> control (<strong>⌃</strong>) key.</p>
<p>Anyone who is a big user of keyboard shortcuts on OS X is a big user of <strong>⌃</strong>. I’m no exception. What’s worse is that this particular key (which I use all day every day) is in the lowest, leftest corner of the keyboard. The only key more stressful to reach is the <em>double blasted</em> <strong>fn</strong> key. Thankfully I rarely use <strong>fn</strong>.</p>
<p>My solution? I remapped the <strong>⌃</strong> key to take <strong>Caps Lock</strong> spot<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> and disabled the <strong>⌃</strong> key altogether to force myself not to use it. How to do?</p>
<p>On OS X, Go to <strong>&ldquo;System Preferences&rdquo; -&gt; &ldquo;Keyboard&rdquo; -&gt; &ldquo;Modifier Keys&rdquo;</strong>. You&rsquo;ll be presented with this dialog where you can set them as you please:</p>
<p><img src="http://jerodsanto.net/drop/osx-remap-keys.png" alt=" "></p>
<p>Why was this minor adjustment such a big deal for me? Because this motion, which causes the hand-twisting-pinky-stress:</p>
<img src="http://jerodsanto.net/drop/pinky-before.gif" alt="Before" style="max-width: 350px;">
<p>Became this motion, which is not stressful at all:</p>
<img src="http://jerodsanto.net/drop/pinky-after.gif" alt="After" style="max-width: 350px;">
<p>After this one change the pain slowly subsided. It took many months for it to disappear completely, but eventually it was just gone.</p>
<p>Nowadays it&rsquo;s barely an afterthought. I can still feel a <em>little</em> bit of exhaustion if I type furiously all day, but it&rsquo;s an order of magnitude less severe than before.</p>
<p>Next to <a href="https://jerodsanto.net/2013/05/tips-for-a-successful-switch-to-a-standing-desk/">switching to a standing desk</a>, remapping my <strong>⌃</strong> key to <strong>Caps Lock</strong> is the best thing I&rsquo;ve done to take care of my body while I work.</p>
<h2 id="ymmv">YMMV</h2>
<p>I’m putting this out there in hopes that somebody with similar symptoms will find some relief. I’m thankful that Greg documented his situation and a fix, and I hope to point others to it if possible.</p>
<p>That being said, each person is different and RSI comes in many shapes and forms. Your mileage may vary!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Why am I smiling? Because I know something you don’t know. <a href="http://www.imdb.com/title/tt0093779/quotes?item=qt0482731">I’m not <em>right</em> handed.</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Can you imagine?&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>You can now buy the Sculpt <a href="http://www.amazon.com/exec/obidos/ASIN/B00CYX26BC/ref=nosim&amp;tag=jerodsanto-20">sans-mouse</a>, but couldn’t back when I was on the market&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Who uses <strong>Caps Lock</strong>, honestly?&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Thoughtfulness is a Secret Weapon</title>
      <link>https://jerodsanto.net/2015/02/thoughtfulness-is-a-secret-weapon/</link>
      <pubDate>Sat, 21 Feb 2015 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2015/02/thoughtfulness-is-a-secret-weapon/</guid>
      
      
      <description><![CDATA[<p>One virtue shared by the people I admire most is this: <strong>they are deeply thoughtful</strong>.</p>
<p>We emulate those we admire. We model our lives after theirs. They’re called <em>role models</em> for a reason, after all. My desire to emulate my role models has made me strive for thoughtfulness. I don’t always make it. Rarely, perhaps. But I’ve been working at it. I continue to work at it.</p>
<p>Thoughtfulness is a verb<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. It’s something we do. We practice it. Or we don’t. It doesn&rsquo;t come easily to me. I doubt it comes easily to most of us.</p>]]></description>
      
      <content:encoded><![CDATA[<p>One virtue shared by the people I admire most is this: <strong>they are deeply thoughtful</strong>.</p>
<p>We emulate those we admire. We model our lives after theirs. They’re called <em>role models</em> for a reason, after all. My desire to emulate my role models has made me strive for thoughtfulness. I don’t always make it. Rarely, perhaps. But I’ve been working at it. I continue to work at it.</p>
<p>Thoughtfulness is a verb<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. It’s something we do. We practice it. Or we don’t. It doesn&rsquo;t come easily to me. I doubt it comes easily to most of us.</p>
<p>A realization that I’ve had lately is that when I place my thoughts – deliberately – on someone or something, I become more useful. I have better advice. My attitude improves. I am more willing to help. I am more <em>able</em> to help. When I <em>don’t</em> place my thoughts on that someone or that something, I am none of these things.</p>
<p>Our thoughts must be placed. Otherwise they’ll run free like a wild beast, pursuing its carnal desires. Many people think we are subject to our minds<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. I don’t believe that for a second. There’s a <em>you</em> in there. There’s a <em>me</em>. <em>I</em> get to decide where my mind wanders, and where it doesn’t. I can place my thoughts. So can you.</p>
<h2 id="place-your-thoughts-on-others">Place your thoughts on others</h2>
<p>This is the hardest thing to do. And the most rewarding. Our thought life is a zero-sum game. Every minute I spend thinking about someone else is a minute I can’t spend thinking about me. It’s hard not to think about me. Every time I turn around, there I am.</p>
<p>Thinking of others is a principal that weaves its way through the Scriptures. One of my favorites is <a href="https://bible.com/1/heb.10.24.kjv">Hebrews 10:24</a>, which states:</p>
<blockquote>
<p>And let us consider one another to provoke unto love and good works</p>
</blockquote>
<p>The end goal here is to provoke one another to (a) love and to (b) good works<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, but notice the prerequisite to doing so: <em>let us consider one another</em>.</p>
<p>It starts with consideration. You can’t do the provoking until you’ve sufficiently considered the one who needs it. Much of the Christian life<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> starts with placing your thoughts on others. <a href="https://bible.com/1/php.2.3.kjv">Philippians 2:3</a> says:</p>
<blockquote>
<p>… but in lowliness of mind let each esteem other better than themselves.</p>
</blockquote>
<p>There it is again: <em>esteem other</em>. That word means <em>to reckon</em> or <em>to think of</em>. We ought to place others before us, but we cannot do that unless we first <em>think of</em> them.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
<h2 id="place-your-thoughts-on-your-work">Place your thoughts on your work</h2>
<p>Let’s face it, many of us can get by at work without putting much thought into it. The bar is so low, the competition so weak that you aren’t going to lose your job for being <em>merely</em> thoughtless.</p>
<p>But is there any satisfaction in <em>getting by</em>? I’ve challenged myself<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> to put a lot of thought into my work. I believe I owe it to my customers. <a href="http://objectlateral.com">My company’s</a> tag line is “Thoughtful, custom software” for a reason. In all things, but especially in software, thoughtfulness makes all the difference in the world.</p>
<p>It <a href="http://fuelyourcoding.com/slow-down-to-go-faster/">saves time</a>. It produces <a href="http://en.wikipedia.org/wiki/Lateral_thinking">creative solutions</a>. It improves <a href="http://en.wikipedia.org/wiki/Empathy">user experience</a>.</p>
<p>It’s also a struggle to sustain. If I <em>really</em> give my thoughts to my work all day, I’m exhausted by the end of it. My brain hurts. That’s how I know I’ve had a solid day’s work.</p>
<h2 id="place-your-thoughts-on-right-now">Place your thoughts on right now</h2>
<p>Mankind has never been as bombarded with external stimuli than we are today. Emails, texts, tweets, likes, snapchats, reminders, television hosts, radio DJs, newsman, and salesman play a never-ending game of tug-o-war for our attention.</p>
<blockquote>
<p>Look at me! Look at me! Look at me!</p>
</blockquote>
<p>It is paramount that we push these distractions away. We have to be present. <em>Now</em>. Or else we miss everything that matters.</p>
<ul>
<li>How many times has one of my kids done something <em>amazing</em> that I missed because I was answering an email?</li>
<li>Why do I require Rachel to repeat herself so often because I&rsquo;m tweeting or finding out who caught that touchdown from Manning?</li>
<li>How can I be so <em>stupid</em> to let a text message take my eye off the road?</li>
</ul>
<p>I lose the battle over my mind too often. I yield myself to these external forces. I hate that about me. If we don’t purpose to place our thoughts on right now, then we aren’t truly living. We’re just reacting.</p>
<h2 id="a-secret-weapon">A Secret Weapon</h2>
<p>So why is thoughtfulness a secret weapon? Let’s take the terms in reverse order.</p>
<h3 id="a-weapon">A Weapon</h3>
<p>Thoughtfulness is a <em>weapon</em> because it is an effective tool to help achieve your goals.</p>
<p>Thinking about <strong>others</strong> helps you build substantial, long-lasting relationships that pay dividends over a lifetime. You can be a real blessing to people, which earns the trust and admiration of those whom you care about.</p>
<p>Thinking about <strong>your work</strong> makes you more useful in the marketplace and yields more satisfaction from your efforts. It will likely result in higher pay and more challenging and interesting work, as well.</p>
<p>Thinking about <strong>right now</strong> increases your quality of life because you won’t miss out on the little things that make it so sweet. You won’t offend others by ignoring them. And you won’t get hit by a bus while you’re crossing the street and texting at the same time.</p>
<h3 id="a-secret">A Secret</h3>
<p>Thoughtfulness is a <em>secret</em> because so few people seem to know how powerful it is. This ought not be, and I hope that this helps you think about your thought life<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup> and how you can put it to work for you.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>OK fine, grammar freaks. It is not a verb. It’s a noun. But you know what I’m getting at here!&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Do <em>they</em> think that, or does their brain make them think that?&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>The good works must proceed from our love which must proceed from <a href="https://bible.com/1/heb.11.6.kjv">our faith</a>. Otherwise, they&rsquo;re not <em>good</em>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Not <em>just</em> the Christian life, but as time passes <a href="https://jerodsanto.net/2015/02/bubble-boy/">it&rsquo;s becoming the only kind of life I know</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Gut check time: how much have you thought of others this week? This month? Let&rsquo;s trump up a hypothetical friend and think about her. Call her Suzie. Here are some questions you can ask yourself about Suzie:</p>
<ul>
<li>Does Suzie have any big events/problems that I can help with?</li>
<li>What&rsquo;s a small thing I can do for Suzie that would just make her day?</li>
<li>How would Suzie feel if I did _____?</li>
<li>How can I help Suzie achieve a goal that she&rsquo;s told me about?</li>
<li>When was the last time I told Suzie how much she means to me?</li>
</ul>
<p>No rocket science here. Just good ole&rsquo; thoughtfulness. Try it on for size, I think you&rsquo;ll like how it looks on you.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>And I challenge you as well&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>That’s So <strike>Raven</strike> Meta&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Big Week</title>
      <link>https://jerodsanto.net/2015/02/big-week/</link>
      <pubDate>Fri, 13 Feb 2015 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2015/02/big-week/</guid>
      
      
      <description><![CDATA[<p>As I <a href="https://twitter.com/jerodsanto/status/564850836438663168">teased</a> on Monday, <em>two things</em> I&rsquo;m excited about were announced this week.</p>
<p>It&rsquo;s always fun to finally share things you&rsquo;ve been quietly toiling away on. For those of you who don&rsquo;t follow me on Twitter<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, here&rsquo;s a recap of the news:</p>
<h2 id="big-news-1-were-putting-on-a-conference">Big News #1: We&rsquo;re putting on a conference!</h2>
<p>The first annual <a href="http://nejsconf.com">NebraskaJS Conference</a> will take place on August 7th, 2015 at the world famous <a href="http://en.wikipedia.org/wiki/Henry_Doorly_Zoo_and_Aquarium">Henry Doorly Zoo and Aquarium</a>.</p>
<p><a href="http://nejsconf.com"><img src="http://jerodsanto.net/drop/nejsconf-logo.png" alt=" "></a></p>]]></description>
      
      <content:encoded><![CDATA[<p>As I <a href="https://twitter.com/jerodsanto/status/564850836438663168">teased</a> on Monday, <em>two things</em> I&rsquo;m excited about were announced this week.</p>
<p>It&rsquo;s always fun to finally share things you&rsquo;ve been quietly toiling away on. For those of you who don&rsquo;t follow me on Twitter<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, here&rsquo;s a recap of the news:</p>
<h2 id="big-news-1-were-putting-on-a-conference">Big News #1: We&rsquo;re putting on a conference!</h2>
<p>The first annual <a href="http://nejsconf.com">NebraskaJS Conference</a> will take place on August 7th, 2015 at the world famous <a href="http://en.wikipedia.org/wiki/Henry_Doorly_Zoo_and_Aquarium">Henry Doorly Zoo and Aquarium</a>.</p>
<p><a href="http://nejsconf.com"><img src="http://jerodsanto.net/drop/nejsconf-logo.png" alt=" "></a></p>
<p>This conf has been in planning for a long time, and I&rsquo; couldn&rsquo;t be happier that we&rsquo;re able to make it official. There&rsquo;s a great group of organizers behind it. I&rsquo;m sure it&rsquo;ll be a smashing success.</p>
<p>Ticketing info, the CFP, and more details are yet to be announced. Here are 2 great ways to stay in the loop:</p>
<ol>
<li>Follow <a href="https://twitter.com/nejsconf">nejsconf</a> on Twitter</li>
<li>Sign up for the <a href="http://nejsconf.com">mailing list</a></li>
</ol>
<p>Oh, and you better believe <a href="http://thechangelog.com">The Changelog</a> team will be there in full force. Speaking of that&hellip;</p>
<h2 id="big-news-2-adam-is-now-working-on-the-changelog-full-time">Big News #2: Adam is now working on The Changelog full time!</h2>
<p>This is so huge for fans of our <a href="http://thechangelog.com/podcast/">podcast</a>, weekly <a href="http://thechangelog.com/weekly/">newsletter</a>, and/or <a href="http://thechangelog.com">blog</a>.</p>
<p><a href="http://thechangelog.com"><img src="http://jerodsanto.net/drop/changelog-podcast-logo.jpg" alt=" "></a></p>
<p>Two years ago<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Adam set out to make The Changelog financially sustainable. That plan has paid off in spades. Thanks to our thriving <a href="http://thechangelog.com/membership/">membership base</a> and the support of awesome sponsors he is now able to focus his entire attention on The Changelog.</p>
<p>There is so much more to say about this that we dedicated an entire post and <a href="http://thechangelog.com/141/">special podcast episode</a> to the announcement. Check it out for all the gritty details and to get a peek at what this means for The Changelog&rsquo;s future<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I will forgive this temporary lapse in judgement, but it&rsquo;s high time you <a href="https://twitter.com/jerodsanto">redeem yourself</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Right about the time I joined the team&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Spoiler alert: it&rsquo;s a bright one&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Bubble Boy</title>
      <link>https://jerodsanto.net/2015/02/bubble-boy/</link>
      <pubDate>Thu, 05 Feb 2015 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2015/02/bubble-boy/</guid>
      
      
      <description><![CDATA[<p>I’m at the tail end of a couple-day sickness. Nothing serious. I was just miserable enough to put on my woe-is-me attitude, but no worse.</p>
<p>It’s times like these — when I’m laying in bed feeling sorry for myself so leave me alone and slam the door on your way out thank you very much — when I try to make my elementary school teachers proud and “turn that frown upside down”. The only way I can get that done is by doing some honest-to-goodness reflection on my life.</p>]]></description>
      
      <content:encoded><![CDATA[<p>I’m at the tail end of a couple-day sickness. Nothing serious. I was just miserable enough to put on my woe-is-me attitude, but no worse.</p>
<p>It’s times like these — when I’m laying in bed feeling sorry for myself so leave me alone and slam the door on your way out thank you very much — when I try to make my elementary school teachers proud and “turn that frown upside down”. The only way I can get that done is by doing some honest-to-goodness reflection on my life.</p>
<p>It’s been over ten years since I first came to my senses and received Christ for who he is; the son of the living God. Ten years is almost <em>1/3 of my life</em>. I’ve had peace with God for so long now that I’m beginning to forget what life was like without it.</p>
<p>To say that my life has changed since that Summer of ’04 is a gross understatement. I’d love to list the ways in which the Lord has blessed me since I started following him, but I fear it might come across as boastful<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>Instead, I’ll just say this. For the past decade I’ve been walking around in a bubble of grace.</p>
<img src="http://jerodsanto.net/drop/bubble-me.jpg" width="325" alt="&nbsp;">
<p>Nothing outside can harm me. All that I need is provided. It is truly remarkable. Reminds me of something the Lord said once:</p>
<blockquote>
<p>I am come that they might have life, and that they might have it <em>more abundantly</em>.</p>
</blockquote>
<p>Life more abundant, indeed.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>My boast would be in the Lord, not in myself. But that’s a distinction that people struggle to make sometimes.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Find Your Fiji</title>
      <link>https://jerodsanto.net/2015/01/find-your-fiji/</link>
      <pubDate>Mon, 26 Jan 2015 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2015/01/find-your-fiji/</guid>
      
      
      <description><![CDATA[<p>I watched The <a href="http://www.imdb.com/title/tt0120382/">Truman Show</a> again recently. I always loved Truman&rsquo;s romanticization of Fiji. He believed it was exactly half way around the world from his little town.</p>
<p>That got me thinking. What is half way around the world from <em>me</em>?</p>
<h2 id="static-showdown">Static Showdown</h2>
<p>When I heard about <a href="http://2015.staticshowdown.com">Static Showdown</a>, Yet another 48 hour programming competition, but with a <a href="http://www.staticapps.org">static</a> twist, I used it as an excuse to build my little <a href="http://fiji.jerodsanto.net">Fiji Finder</a> <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p><a href="http://fiji.jerodsanto.net"><img src="http://jerodsanto.net/drop/fyf-screencap.jpg" alt="That&rsquo;s Pandloi, India, btw"></a></p>]]></description>
      
      <content:encoded><![CDATA[<p>I watched The <a href="http://www.imdb.com/title/tt0120382/">Truman Show</a> again recently. I always loved Truman&rsquo;s romanticization of Fiji. He believed it was exactly half way around the world from his little town.</p>
<p>That got me thinking. What is half way around the world from <em>me</em>?</p>
<h2 id="static-showdown">Static Showdown</h2>
<p>When I heard about <a href="http://2015.staticshowdown.com">Static Showdown</a>, Yet another 48 hour programming competition, but with a <a href="http://www.staticapps.org">static</a> twist, I used it as an excuse to build my little <a href="http://fiji.jerodsanto.net">Fiji Finder</a> <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p><a href="http://fiji.jerodsanto.net"><img src="http://jerodsanto.net/drop/fyf-screencap.jpg" alt="That&rsquo;s Pandloi, India, btw"></a></p>
<h2 id="constraints">Constraints</h2>
<p>There&rsquo;s not much to it. Use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/Using_geolocation">Geolocation API</a> to find out where people are and then find the point half way around the world. The math isn&rsquo;t even all that interesting. So I set a few constraints for myself:</p>
<ol>
<li><a href="http://vanilla-js.com">Vanilla JS</a> — I would keep it lean and mean. No jQuery, even.</li>
<li>No libraries — I would write every line of code.</li>
<li>Regular Life — I wouldn&rsquo;t skip get-togethers, church, playing with the kids, etc.</li>
</ol>
<h2 id="challenges">Challenges</h2>
<p>There were a couple of challenges, though. The biggest one is that the Earth&rsquo;s surface is 71% water. That means most of our Fijis (mine included) would be pretty boring. I needed to detect water and adjust.</p>
<p>This would be easy if Google&rsquo;s Maps API would include some kind of <code>terrain</code> attribute. Or even better, an <code>isWater</code> flag! Sadly, there&rsquo;s no such thing which means we&rsquo;re left to hack together solutions. I found some pretty bizarre advice  on Stack Overflow<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> like checking for elevation and declaring anything under sea level as water. Terrible, terrible idea.</p>
<p>Finally I came across <a href="https://tech.bellycard.com/blog/where-d-the-water-go-google-maps-water-pixel-detection-with-canvas/">this awesome hack</a> which I ended up modifying and using. Here&rsquo;s how it works:</p>
<ol>
<li>Take the latitude/longitude that you&rsquo;re trying to detect water from</li>
<li>Use the <a href="https://developers.google.com/maps/documentation/staticmaps/">Static Maps API</a> to get a small image of the location</li>
<li>Disable all features of the map <em>except</em> water, which is set to green</li>
<li>Download the image of the map and add it to an in-memory <code>canvas</code> element</li>
<li>Read the image data from the <code>canvas</code> element and check if it&rsquo;s green</li>
<li>Green = water, anything else = land</li>
</ol>
<p>Crazy, huh? Here&rsquo;s the function I ended up with:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">detectWater</span><span class="p">(</span><span class="nx">ll</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">water</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Image</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="nx">water</span><span class="p">.</span><span class="nx">crossOrigin</span> <span class="o">=</span> <span class="s2">&#34;http://maps.googleapis.com/crossdomain.xml&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">water</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="s2">&#34;http://maps.googleapis.com/maps/api/staticmap?&amp;center=&#34;</span> <span class="o">+</span> <span class="nx">ll</span><span class="p">.</span><span class="nx">toUrlValue</span><span class="p">()</span> <span class="o">+</span> <span class="s2">&#34;&amp;zoom=13&amp;size=100x100&amp;sensor=false&amp;visual_refresh=true&amp;style=element:labels|visibility:off&amp;style=feature:water|color:0x00FF00&amp;style=feature:transit|visibility:off&amp;style=feature:poi|visibility:off&amp;style=feature:road|visibility:off&amp;style=feature:administrative|visibility:off&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">waterCanvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">&#34;canvas&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">waterCanvas</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s2">&#34;width&#34;</span><span class="p">,</span> <span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">waterCanvas</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s2">&#34;height&#34;</span><span class="p">,</span> <span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">waterCanvas</span> <span class="o">=</span> <span class="nx">waterCanvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="s2">&#34;2d&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">water</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">waterCanvas</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">water</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">bytes</span> <span class="o">=</span> <span class="nx">waterCanvas</span><span class="p">.</span><span class="nx">getImageData</span><span class="p">(</span><span class="mi">50</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">).</span><span class="nx">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">bytes</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">===</span> <span class="mi">254</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">ll</span><span class="p">.</span><span class="nx">isLand</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">ll</span><span class="p">.</span><span class="nx">isLand</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>So when I find your Fiji and detect that it&rsquo;s water, I head due north 500 km and detect another one. Rinse and repeat until we hit land. It ain&rsquo;t perfect<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, but it is good enough.</p>
<p>The other challenge, oddly enough, was adding a few decent looking modal dialog boxes. I use libraries for those all of the time and I felt like quite the neophyte trying to implement it on my own. In fact, I was quickly running out of time <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> so I hacked together a butt-nasty solution.</p>
<h2 id="takeaways">Takeaways</h2>
<p>It&rsquo;s fun to conceive an idea and be able to bring it to life in just a couple of days. I&rsquo;m often disappointed when I do hackathons because project scopes are often too large for the time allotted. This was just enough work to challenge me while still being able to finish.</p>
<p>The end product is by no means perfect, but just like Fiji, it&rsquo;s less about the final destination and more about how far you have to travel to get there.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I had no illusion of winning the contest, but I was working alone so I thought <em>maybe</em> I&rsquo;d be in the running for the solo prize. Then I saw <a href="http://2015.staticshowdown.com/teams/spacetme">this entry</a>, which destroyed all hope for me.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>As you do&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>It will skip over islands that are smaller than 500 km. But it was either that or fire off way too many Static Maps API calls (Google was throttling me).&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Not competition time. I had plenty of that. But I had to go to my parents&rsquo; for dinner soon!&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Art-directed, data-driven image files to accompany your website</title>
      <link>https://jerodsanto.net/2015/01/art-directed-data-driven-image-files-to-accompany-your-website/</link>
      <pubDate>Mon, 19 Jan 2015 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2015/01/art-directed-data-driven-image-files-to-accompany-your-website/</guid>
      
      
      <description><![CDATA[<p><a href="http://codyjamespeterson.com">Cody</a> and I recently added a subtle feature<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> to <a href="http://omahype.com">Oma</a>/<a href="http://outwithsprout.com">Sprout</a> that we think is pretty neat. I haven&rsquo;t seen other people doing this, so I thought I&rsquo;d publish the idea and how we pulled it off.</p>
<h2 id="the-problem">The Problem</h2>
<p>Much of our recurring traffic comes from social media<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. To foster this flow of traffic, we post our sites&rsquo; daily event pages to Facebook and Twitter on a regular basis.</p>
<p>The web in general — and social media in particular — is a fight for attention. What makes it worse is that <a href="http://www.nngroup.com/articles/how-users-read-on-the-web/">people don&rsquo;t read words</a>. What they do do<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> is look at pictures. Disagree with that? Just ask Instagram or Pinterest about it.</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="http://codyjamespeterson.com">Cody</a> and I recently added a subtle feature<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> to <a href="http://omahype.com">Oma</a>/<a href="http://outwithsprout.com">Sprout</a> that we think is pretty neat. I haven&rsquo;t seen other people doing this, so I thought I&rsquo;d publish the idea and how we pulled it off.</p>
<h2 id="the-problem">The Problem</h2>
<p>Much of our recurring traffic comes from social media<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. To foster this flow of traffic, we post our sites&rsquo; daily event pages to Facebook and Twitter on a regular basis.</p>
<p>The web in general — and social media in particular — is a fight for attention. What makes it worse is that <a href="http://www.nngroup.com/articles/how-users-read-on-the-web/">people don&rsquo;t read words</a>. What they do do<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> is look at pictures. Disagree with that? Just ask Instagram or Pinterest about it.</p>
<p>Social media posts that include imagery out-perform posts that don&rsquo;t. Any Social Media Guru worth her weight in Likes will tell you that. Because of this, we include our site&rsquo;s logo with each post. (Posts promoting specific events often have better images that we use, but our logo is the fallback.)</p>
<p><img src="http://jerodsanto.net/drop/oma-sprout.png" alt="Omahype / Out with Sprout logos"></p>
<p>An image of our logo is better than nothing. It makes our posts immediatey recognizable, but it&rsquo;s also <em>boring</em>. In a zero-sum fight for attention, boring loses every time. What can we do to improve our reach?</p>
<h2 id="the-solution">The Solution</h2>
<p>Each day&rsquo;s events are unique. If we could somehow reflect that uniqueness in the imagery that gets posted to social media, then we would have de facto fresh image content each day<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>.</p>
<p>Enter our art-directed, data-driven image files. Check out the day view for Saturday, January 17th.</p>
<p><a href="http://www.outwithsprout.com/2015/01/17">http://www.outwithsprout.com/2015/01/17</a></p>
<p><a href="http://www.outwithsprout.com/2015/01/17"><img src="http://jerodsanto.net/drop/ows-day-view.png" alt=" "></a></p>
<p>You will notice:</p>
<ul>
<li>3 <a href="http://www.outwithsprout.com/category/explore">Explore</a> events (highlighted in red)</li>
<li>1 <a href="http://www.outwithsprout.com/category/experience">Experience</a> event (yellow highlighted in yellow)</li>
<li>1 <a href="http://www.outwithsprout.com/category/play">Play</a> event (highlighted in green)</li>
<li>2 <a href="http://www.outwithsprout.com/category/create">Create</a> events (highlighted in pink)</li>
<li>1 <a href="http://www.outwithsprout.com/category/cheer">Cheer</a> event (highlighted in blue)</li>
</ul>
<p>Now, append a <code>.png</code> to the end of that URL:</p>
<p><a href="http://www.outwithsprout.com/2015/01/17.png">http://www.outwithsprout.com/2015/01/17.png</a></p>
<p>You get this instead:</p>
<p><a href="http://www.outwithsprout.com/2015/01/17"><img src="http://www.outwithsprout.com/2015/01/17.png" alt=" "></a></p>
<p>Pretty cool, right? Not only is the day&rsquo;s date presented in a little calendar widget, but each event for that day is represented by a color-coordinated circle. It&rsquo;s a subtle-but-unique way to re-enforce how color plays a role in our sites&rsquo; content.</p>
<p>Next, check out the <a href="http://www.outwithsprout.com/2015/01/18">18th</a>:</p>
<p><a href="http://www.outwithsprout.com/2015/01/18"><img src="http://www.outwithsprout.com/2015/01/18.png" alt="Saturday is king for kids events. Sunday, not so much."></a></p>
<p>There are only 3 events on the 18th. Hence, 3 colored circles on its image. Each image is generated <em>on request</em> and represents the current state of our database of events. We use these images in place of our logo on social media.</p>
<p><img src="http://jerodsanto.net/drop/ows-fb-post.png" alt="A familiar, branded, &amp; unique image for each day&rsquo;s post"></p>
<h2 id="how-we-pull-it-off">How We Pull it Off</h2>
<p>There&rsquo;s an awesome gem called <a href="https://github.com/csquared/IMGKit">IMGKit</a> — which wraps <a href="https://github.com/wkhtmltopdf/wkhtmltopdf">wkhtmltoimage</a> — that we use to pull this all off. The gem takes our authored HTML and converts it in to an image.</p>
<div class="notice">This app is on Rails, so these setup instructions are specific to that environment, but the concept itself can easily be ported to other stacks.</div>
<p>Rails is designed to handle alternate MIME types — via the <a href="http://api.rubyonrails.org/classes/ActionController/MimeResponds.html#method-i-respond_to">respond_to</a> API — really well. We already have a controller action that fetches the day&rsquo;s events, so all we have to do is tell it to respond to <code>png</code> requests:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">respond_to</span> <span class="k">do</span> <span class="o">|</span><span class="nb">format</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="nb">format</span><span class="o">.</span><span class="n">html</span>
</span></span><span class="line"><span class="cl">  <span class="nb">format</span><span class="o">.</span><span class="n">png</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">kit</span> <span class="o">=</span> <span class="no">IMGKit</span><span class="o">.</span><span class="n">new</span> <span class="n">render_to_string</span><span class="p">,</span> <span class="ss">width</span><span class="p">:</span> <span class="mi">300</span><span class="p">,</span> <span class="ss">height</span><span class="p">:</span> <span class="mi">300</span>
</span></span><span class="line"><span class="cl">    <span class="n">send_data</span> <span class="n">kit</span><span class="o">.</span><span class="n">to_png</span><span class="p">,</span> <span class="ss">type</span><span class="p">:</span> <span class="s2">&#34;image/png&#34;</span><span class="p">,</span> <span class="ss">disposition</span><span class="p">:</span> <span class="s2">&#34;inline&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Requests for <code>html</code> will render the <code>app/views/events/day.html.erb</code> view, just like always. Requests for <code>png</code> will render the <code>app/views/events/day.png.erb</code> view. Now we add a view that includes the HTML to be converted in to the image and we&rsquo;re done!</p>
<p>Here&rsquo;s the guts of the view (sans CSS):</p>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;body class=&#34;&lt;%= Omahype.theme %&gt;&#34;&gt;
  &lt;div class=&#34;calendar&#34;&gt;
    &lt;h1 class=&#34;calendar-month&#34;&gt;&lt;%= day.to_s :month %&gt;&lt;/h1&gt;
    &lt;h2 class=&#34;calendar-day&#34;&gt;&lt;%= day.to_s :day_number %&gt;&lt;/h2&gt;
    &lt;ul class=&#34;event-dots&#34;&gt;
      &lt;% day.events.each do |event| %&gt;
        &lt;li class=&#34;&lt;%= event.event_classes %&gt;&#34;&gt;&lt;/li&gt;
      &lt;% end %&gt;
    &lt;/li&gt;
  &lt;/div&gt;
&lt;/body&gt;
</code></pre><p>Pretty simple, huh? One thing to note: you cannot easily reference external style sheets from your image views. We inline everything in a <code>style</code> tag to get around this limitation.</p>
<p>Finally, we point the social medias at this new <code>.png</code> URL so they grab it instead of the logo image. Here&rsquo;s the meta tags generated to handle this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">meta</span> <span class="na">property</span><span class="o">=</span><span class="s">&#34;og:image&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;http://www.outwithsprout.com/2015/01/17.png&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">meta</span> <span class="na">property</span><span class="o">=</span><span class="s">&#34;og:image&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;http://ows-production.s3.amazonaws.com/assets/ows/fb-og-8eb228bfcc6eebb2397df92c5ccf3b3d.png&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">meta</span> <span class="na">property</span><span class="o">=</span><span class="s">&#34;twitter:image:src&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;http://www.outwithsprout.com/2015/01/17.png&#34;</span> <span class="p">/&gt;</span>
</span></span></code></pre></div><p>Why two <code>og:image</code> tags, you wonder? Because Facebook will accept a cascade of images and use the first (or best) one by default. We want to use our custom day view image, but if something goes wrong we fall back to our logo image instead.</p>
<h3 id="caching">Caching</h3>
<p>Generating these images isn&rsquo;t the fastest process in the world. It generally takes between 500ms and 1800ms per request. We don&rsquo;t need them to be <em>up-to-the-moment</em> fresh, so we add a <a href="https://github.com/mperham/dalli">cache</a> and only regenerate them once every 24 hours. This is just a single line of code added to the <code>respond_to</code> block:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">format</span><span class="o">.</span><span class="n">png</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">expires_in</span> <span class="mi">24</span><span class="o">.</span><span class="n">hours</span><span class="p">,</span> <span class="kp">public</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="n">kit</span> <span class="o">=</span> <span class="no">IMGKit</span><span class="o">.</span><span class="n">new</span> <span class="n">render_to_string</span><span class="p">,</span> <span class="ss">width</span><span class="p">:</span> <span class="mi">612</span><span class="p">,</span> <span class="ss">height</span><span class="p">:</span> <span class="mi">612</span>
</span></span><span class="line"><span class="cl">  <span class="n">send_data</span> <span class="n">kit</span><span class="o">.</span><span class="n">to_png</span><span class="p">,</span> <span class="ss">type</span><span class="p">:</span> <span class="s2">&#34;image/png&#34;</span><span class="p">,</span> <span class="ss">disposition</span><span class="p">:</span> <span class="s2">&#34;inline&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="other-uses">Other Uses</h2>
<p>Once we went through all the work of setting up the dynamic images, we started to think of other ways we could reuse them. Our newsletter lists the upcoming week&rsquo;s events day-by-day, so that was a natural fit. We link the day images in to it via a good ole&rsquo; <code>img</code> tag. No extra coding necessary!</p>
<p><a href="http://eepurl.com/NRfgD"><img src="http://jerodsanto.net/drop/weekly-images.png" alt=" "></a></p>
<p>Caching plays a huge role in this use case because each person that opens the email will hit our server for five day images.</p>
<p>We&rsquo;ve been experimenting with another use for these images which could be awesome (especially for Omahype).</p>
<p>We could just as easily design a custom <em>event</em> image. It would frame in the event&rsquo;s artwork and overlay it with date/time/location info. Then these event images could be made available for download so others can use them to promote the event.</p>
<p>We made a few steps down that road, but haven&rsquo;t fully fleshed the idea out yet.</p>
<h2 id="all-the-small-things">All The Small Things</h2>
<p>This is a minor feature that many people will never even notice. Why all the fuss? Because it&rsquo;s small details like these that surprise and delight people.</p>
<p>If your website has unique and interesting content, I&rsquo;m sure you can come up with some rad uses for art-directed, data-driven image files. It&rsquo;ll set you apart from the crowd.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The beauty of side projects — especially those with active users — is that you can use them as playgrounds for trying out new things.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>A fact that I loathe. We&rsquo;re trying to break free of Facebook&rsquo;s shackles with a <a href="http://eepurl.com/NRfgD">newsletter</a>. So far that strategy is working just OK.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Snicker at this and you&rsquo;re as juvenile as I am.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Cody gets full credit for this idea. I just made it happen.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>How we use Trello to generate The Changelog Weekly</title>
      <link>https://jerodsanto.net/2015/01/how-we-use-trello-to-generate-the-changelog-weekly/</link>
      <pubDate>Wed, 14 Jan 2015 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2015/01/how-we-use-trello-to-generate-the-changelog-weekly/</guid>
      
      
      <description><![CDATA[<p>I published a lengthy post on <a href="http://thechangelog.com">The Changelog</a> all about how we turned <a href="http://trello.com">Trello</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> in to a CMS for <a href="http://thechangelog.com/weekly">Weekly</a> — our email newsletter that covers everything on our open source radar.</p>
<p><a href="http://thechangelog.com/trello-as-a-cms/"><img src="http://jerodsanto.net/drop/publish-flow.png" alt="A simplified flow chart of the process"></a></p>
<p>Want to know why and <em>how</em> we do it? <a href="http://thechangelog.com/trello-as-a-cms/">Read the full post here</a>!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Trello is quickly becoming one of my favorite tools of all time. Who would&rsquo;ve thought that a list of lists could be so useful? Fog Creek, I guess!&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>]]></description>
      
      <content:encoded><![CDATA[<p>I published a lengthy post on <a href="http://thechangelog.com">The Changelog</a> all about how we turned <a href="http://trello.com">Trello</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> in to a CMS for <a href="http://thechangelog.com/weekly">Weekly</a> — our email newsletter that covers everything on our open source radar.</p>
<p><a href="http://thechangelog.com/trello-as-a-cms/"><img src="http://jerodsanto.net/drop/publish-flow.png" alt="A simplified flow chart of the process"></a></p>
<p>Want to know why and <em>how</em> we do it? <a href="http://thechangelog.com/trello-as-a-cms/">Read the full post here</a>!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Trello is quickly becoming one of my favorite tools of all time. Who would&rsquo;ve thought that a list of lists could be so useful? Fog Creek, I guess!&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Two Guys B.S.in&#39;</title>
      <link>https://jerodsanto.net/2015/01/two-guys-b.s.in/</link>
      <pubDate>Mon, 05 Jan 2015 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2015/01/two-guys-b.s.in/</guid>
      
      
      <description><![CDATA[<p>I&rsquo;ve been quietly publishing a <a href="http://biblicallyspeaking.fm/">Bible podcast</a> — hosted by myself and <a href="http://biblestudy.net">John Malone</a> — since the start of 2014.</p>
<p>The reason I&rsquo;ve been <em>quietly</em> publishing it is because it took a bit for us to iron out the podcasting kinks. I love podcasting, but it still hasn&rsquo;t had its &ldquo;Blogger moment&rdquo;<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>The show is low on ceremony and (hopefully) high on content. It&rsquo;s just two guys talking to each other about the Bible. Asking questions and sometimes getting answers. We try to keep it &ldquo;commute friendly&rdquo;, and if you enjoy the Bible at all, I think you&rsquo;ll enjoy the show.</p>]]></description>
      
      <content:encoded><![CDATA[<p>I&rsquo;ve been quietly publishing a <a href="http://biblicallyspeaking.fm/">Bible podcast</a> — hosted by myself and <a href="http://biblestudy.net">John Malone</a> — since the start of 2014.</p>
<p>The reason I&rsquo;ve been <em>quietly</em> publishing it is because it took a bit for us to iron out the podcasting kinks. I love podcasting, but it still hasn&rsquo;t had its &ldquo;Blogger moment&rdquo;<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>The show is low on ceremony and (hopefully) high on content. It&rsquo;s just two guys talking to each other about the Bible. Asking questions and sometimes getting answers. We try to keep it &ldquo;commute friendly&rdquo;, and if you enjoy the Bible at all, I think you&rsquo;ll enjoy the show.</p>
<p>If you don&rsquo;t currently enjoy the Bible (or think it&rsquo;s stupid, filled with old wives tails, etc., etc.), humor me and give it a listen. Who knows, we may surprise you.</p>
<p>We&rsquo;ve published 49 shows so far. Here are a handful of my favorites:</p>
<ul>
<li>
<p><a href="http://biblicallyspeaking.fm/episodes/1-i-can-stare-into-a-campfire-for-hours/">Episode 1: I Can Stare Into a Campfire for Hours</a> — What does the Bible claim about itself? Can we know that God wrote it?</p>
</li>
<li>
<p><a href="http://biblicallyspeaking.fm/episodes/3-word-fetcher/">Episode 3: Word Fetcher</a> — Are the individual words of the Bible inspired? Have they been preserved by God?</p>
</li>
<li>
<p><a href="http://biblicallyspeaking.fm/episodes/7-salt-lick-of-salvation/">Episode 7: Salt Lick of Salvation</a> — A thorough explanation of the warning in Hebrews 6:3-8</p>
</li>
<li>
<p><a href="http://biblicallyspeaking.fm/episodes/19-firing-every-cylinder/">Episode 19: Not Firing on Every Cylinder</a> — A must-listen for all the Mary worshipers out there.</p>
</li>
<li>
<p><a href="http://biblicallyspeaking.fm/episodes/21-bible-tsunami/">Episode 21: Bible Tsunami</a> — Is the order of the books in the Bible divinely inspired or not?</p>
</li>
<li>
<p><a href="http://biblicallyspeaking.fm/episodes/32-hotness/">Episode 32: The Hotness</a> — I BS on my own about pride and its effects.</p>
</li>
</ul>
<p>Let me know what you think. If you like it, there are <a href="http://biblicallyspeaking.fm/subscribe/">plenty of ways to subscribe</a>!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>&ldquo;Blogger for podcasting&rdquo; is an open opportunity for somebody out there to provide a lot of value (and make a lot of money doing it).&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>GIF of the Year</title>
      <link>https://jerodsanto.net/2014/12/gif-of-the-year/</link>
      <pubDate>Tue, 30 Dec 2014 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2014/12/gif-of-the-year/</guid>
      
      
      <description><![CDATA[<p>My vote for <strong>&ldquo;GIF of the Year&rdquo;</strong> goes to&hellip;</p>
<p><img src="http://jerodsanto.net/drop/mavs-white-boy-dance.gif" alt="White men can dance"></p>
<p>There&rsquo;s so much to love here. The Michael Jackson glove<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. The girlfriend who wants nothing to do with it. Home boy in the background pimpin&rsquo; his soda. I&rsquo;ve looped this one countless times in 2014.</p>
<hr>
<p>What&rsquo;s your favorite GIF this year? Upload or link it up in the comments!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I revel in the possibility of that guy having thought to himself, &ldquo;I gotta remember to bring my Michael Jackson glove to the game!&rdquo;&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>]]></description>
      
      <content:encoded><![CDATA[<p>My vote for <strong>&ldquo;GIF of the Year&rdquo;</strong> goes to&hellip;</p>
<p><img src="http://jerodsanto.net/drop/mavs-white-boy-dance.gif" alt="White men can dance"></p>
<p>There&rsquo;s so much to love here. The Michael Jackson glove<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. The girlfriend who wants nothing to do with it. Home boy in the background pimpin&rsquo; his soda. I&rsquo;ve looped this one countless times in 2014.</p>
<hr>
<p>What&rsquo;s your favorite GIF this year? Upload or link it up in the comments!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I revel in the possibility of that guy having thought to himself, &ldquo;I gotta remember to bring my Michael Jackson glove to the game!&rdquo;&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Beloved Bits, 2014</title>
      <link>https://jerodsanto.net/2014/12/beloved-bits-2014/</link>
      <pubDate>Sun, 21 Dec 2014 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2014/12/beloved-bits-2014/</guid>
      
      
      <description><![CDATA[<p><a href="http://necro-skeletal.deviantart.com/art/Pixel-Heart-332654763"><img src="http://jerodsanto.net/drop/pixel-heart-by-necro-skeletal.png" alt="via Necro-Skeletal on Deviant Art"></a></p>
<p>Welcome to my fourth annual list of my favorite digital things! Remember how I roll? Like this:</p>
<ol>
<li>List items must be anatomically digital</li>
<li>List items must have won <em>me</em> over in 2014</li>
<li>List items may or may not have been released in 2014</li>
</ol>
<p>Here’s the goods. Oh, and if you’re really bored, check out <a href="https://jerodsanto.net/2011/12/beloved-bits-2011">2011</a>, <a href="https://jerodsanto.net/2012/12/beloved-bits-2012">2012</a>, and <a href="https://jerodsanto.net/2013/12/beloved-bits-2013">2013</a>.</p>
<h2 id="ios-apps">iOS Apps</h2>
<h3 id="overcast"><a href="https://overcast.fm">Overcast</a></h3>
<p>I was a loyal <a href="http://vemedio.com/products/instacast">Instacast</a> user for years. Emphasis on <em>was</em>. Somehow, Marco Arment always manages to write software that I <em>love</em> to use.</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="http://necro-skeletal.deviantart.com/art/Pixel-Heart-332654763"><img src="http://jerodsanto.net/drop/pixel-heart-by-necro-skeletal.png" alt="via Necro-Skeletal on Deviant Art"></a></p>
<p>Welcome to my fourth annual list of my favorite digital things! Remember how I roll? Like this:</p>
<ol>
<li>List items must be anatomically digital</li>
<li>List items must have won <em>me</em> over in 2014</li>
<li>List items may or may not have been released in 2014</li>
</ol>
<p>Here’s the goods. Oh, and if you’re really bored, check out <a href="https://jerodsanto.net/2011/12/beloved-bits-2011">2011</a>, <a href="https://jerodsanto.net/2012/12/beloved-bits-2012">2012</a>, and <a href="https://jerodsanto.net/2013/12/beloved-bits-2013">2013</a>.</p>
<h2 id="ios-apps">iOS Apps</h2>
<h3 id="overcast"><a href="https://overcast.fm">Overcast</a></h3>
<p>I was a loyal <a href="http://vemedio.com/products/instacast">Instacast</a> user for years. Emphasis on <em>was</em>. Somehow, Marco Arment always manages to write software that I <em>love</em> to use.</p>
<a href="https://overcast.fm">
  <img width="275" alt="That's only 7 of the 29 podcasts I'm subscribed to" src="http://jerodsanto.net/drop/overcast.png">
</a>
<p>For me, the nerdy playlist stuff<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> is not the killer feature of Overcast. What I can no longer live without is Voice Boost and Smart Speed. Emphasis on <em>Smart Speed</em>.</p>
<h3 id="vesper"><a href="http://vesperapp.co">Vesper</a></h3>
<p>As an Evernote refugee<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, the built-in Notes app on iOS was sufficient for me. But it was never a joy to use. <a href="http://vesperapp.co">Vesper’s</a> attention to detail sets it apart from any other notes app.</p>
<p>Once its companion Mac app drops, all will be well in my note taking world.</p>
<h3 id="signnow"><a href="https://www.signnow.com">SignNow</a></h3>
<p>You know what sucks?</p>
<ol>
<li>Receive email with attachment</li>
<li>Download said attachment</li>
<li>Print the file</li>
<li>Sign the printed page</li>
<li>Scan the printed page</li>
<li>Send email with attachment</li>
</ol>
<p><a href="https://www.signnow.com">SignNow</a> cuts steps 3-6 out of that process, which is a huge win for me.</p>
<p><strong>Honorable Mentions:</strong> <a href="http://www.reddit.com/r/alienblue">Alien Blue</a>, <a href="https://itunes.apple.com/us/app/hyperlapse-from-instagram/id740146917?mt=8">Hyperlapse</a>, <a href="https://my.workflow.is">Workflow</a></p>
<h2 id="os-x-apps">OS X Apps</h2>
<p>Remember back on <a href="http://en.wikipedia.org/wiki/MTV_Cribs">MTV Cribs</a> when all the Dudes would stop right before entering their bedroom, look at the camera, and <strike>lie</strike> say: &ldquo;This is where the magic happens&rdquo;?</p>
<p>OS X is where the magic <em>really</em> happens.</p>
<h3 id="glui"><a href="http://glui.me">Glui</a></h3>
<p>I’ve been waiting for <a href="http://glui.me">Glui</a> ever since Skitch got bought and ruined by Evernote. Bonus: I think it’s better than Skitch ever was. A few reasons:</p>
<ol>
<li>Built-in <a href="https://www.getcloudapp.com">CloudApp</a> support</li>
<li>Easier drag/drop out of app</li>
<li>Cleaner UI (remember how odd Skitch’s framing was?)</li>
<li>Pixelate feature (to hide sensitive info)</li>
</ol>
<p><a href="http://glui.me"><img src="http://jerodsanto.net/drop/glui-example.png" alt="Using Glui on Glui"></a></p>
<h3 id="miro-video-converter"><a href="http://www.mirovideoconverter.com">Miro Video Converter</a></h3>
<p>I’ve wasted way too much of my life finding that perfect incantation to make ffmpeg do what I want it to do. This year I found <a href="http://www.mirovideoconverter.com">Miro Video Converter</a>, which is exactly what it says on the tin:</p>
<blockquote>
<p>A beautiful, simple way to convert almost any video to MP4, WebM (vp8), Ogg Theora, or for Android, iPhone, and iPad. Batch conversion, custom sizing, and more! 100% Free and open-source.</p>
</blockquote>
<p>Where have you been all my life?</p>
<h3 id="slack"><a href="https://slack.com">Slack</a></h3>
<p>Jump on the hype train. Slack is worthy. Speaking of attention to detail… this team gets all the little stuff right. Smooth onboarding, sane defaults, extreme customization, and all the integrations a nerd like me could ever ask for.</p>
<p>I used Slack in 2013, but I <em>fell in love</em> with it in 2014. The fast team switching they added a month or so ago didn’t hurt any.</p>
<p><strong>Honorable Mentions:</strong> <a href="https://eggerapps.at/pgcommander/">PG Commander</a>, <a href="https://www.rogueamoeba.com/audiohijackpro/">Audio Hijack Pro</a></p>
<h2 id="games">Games</h2>
<p>I keep warning Rachel that I&rsquo;m going to go buy a PS4. Until that day comes, I do all of my gaming on iOS.</p>
<h3 id="monument-valley"><a href="http://www.monumentvalleygame.com">Monument Valley</a></h3>
<p>A game that’s like an <a href="http://www.mcescher.com/gallery/">M.C. Escher</a> drawing come to life? Count me in. Tons of fun and just gorgeous.</p>
<p><a href="http://www.monumentvalleygame.com"><img src="http://jerodsanto.net/drop/monument-valley.png" alt="Every level is exquisitely designed"></a></p>
<p>I wish it were a <em>little</em> harder, but that’s just a quibble. Monument Valley is well deserving of its <a href="https://developer.apple.com/design/awards/2014/Monument-Valley/">ADA</a>.</p>
<h3 id="desert-golfing"><a href="https://itunes.apple.com/us/app/desert-golfing/id902062673?mt=8">Desert Golfing</a></h3>
<p><a href="https://itunes.apple.com/us/app/desert-golfing/id902062673?mt=8">Desert Golfing</a> is a bare bones 2D golfing game. And boy do I mean it. It is perhaps more striking because of what the game <em>doesn’t</em> do than what it <em>does</em> do. It lacks:</p>
<ul>
<li>Menus</li>
<li>Level selection</li>
<li>Mulligans</li>
<li>Replays</li>
<li>Music</li>
<li>Congratulations of any kind</li>
</ul>
<p>You hit the ball in the hole. The screen slides right to left. A new hole appears. You hit the ball in the hole. <em>Ad infinitum</em>.</p>
<p><a href="https://itunes.apple.com/us/app/desert-golfing/id902062673?mt=8"><img src="http://jerodsanto.net/drop/desert-golfing.png" alt="Cinderella story, outta no where, a former greenskeeper now - about to become the Masters champion."></a></p>
<p>It’s also a lot of fun and pretty addicting. It’s so simple, in fact, that I feel like I could’ve made it. In fact, I’m kind of mad I didn’t.</p>
<h3 id="threes"><a href="http://asherv.com/threes/">Threes!</a></h3>
<p>If imitation is the sincerest form of flattery, then <a href="http://asherv.com/threes/">Threes!</a> should feel hella flattered. Even its <a href="http://gabrielecirulli.github.io/2048/">rip-offs</a> found massive success. The game deems itself:</p>
<blockquote>
<p>A tiny puzzle that grows on you.</p>
</blockquote>
<p>Did it ever grow on me. Unlike Desert Golfing, Threes! is a game that I have no delusions of being able to create. It is subtle, polished, and absolutely maddening. And I love it.</p>
<p><strong>Honorable Mentions:</strong> <a href="https://itunes.apple.com/us/app/kill-shot/id839703707?mt=8">Kill Shot</a>, <a href="http://epicskater.com">Epic Skater</a>, <a href="http://www.rocketcat-games.com/wayward/">Wayward Souls</a></p>
<h2 id="podcasts">Podcasts</h2>
<p>I&rsquo;m a long-time podcast nerd. It may just be my favorite medium. Whether I&rsquo;m in the car, taking my daily consitutional<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, or just trying to fall asleep, I usually have a podcast playing.</p>
<h3 id="we-have-concerns"><a href="http://www.wehaveconcerns.com">We Have Concerns</a></h3>
<p><a href="http://www.wehaveconcerns.com"><img src="http://jerodsanto.net/drop/whc.jpg" alt="Anthony Carboni and Jeff Cannata"></a></p>
<p><a href="http://www.wehaveconcerns.com">We Have Concerns</a> is 20 minutes of insane conversation with impromptu improv mixed in for the lulz. Here’s a <a href="http://www.wehaveconcerns.com/2014/12/your-sht-dont-stink/">recent show’s</a> synopsis with which to whet your appetite:</p>
<blockquote>
<p>By actually printing DNA, researchers are now able to create entirely new species.  They started with a glow-in-the-dark plant, and now have plans to design microbes that will live in your stomach and actually change the smell of your fecal matter. Even crazier, they may be able to associate particular gas odors with biological states, so you can effectively have an early warning system built inside you.  Anthony couldn’t be more excited, but Jeff wonders when our Frankenstein microbes will inevitably destroy us all in a flowery-smelling apocalypse.</p>
</blockquote>
<p>Does this show have too many typical dick-and-fart jokes? <em>Yes</em>. But it also hits on deeper levels. Rarely does an episode pass where I don&rsquo;t LOLIRL.</p>
<h3 id="startup-podcast"><a href="http://gimletmedia.com/show/startup/">StartUp Podcast</a></h3>
<p>This show is so meta. It tracks ex-NPR producer Alex Blumberg as he tries to start a podcast network. It’s a podcast about a guy who used to make podcasts, but now wants to start a company that makes podcasts. And it’s absolutely riveting.</p>
<p>If you need convincing, just listen to <a href="http://gimletmedia.com/episode/1-how-not-to-pitch-a-billionaire/">episode 1</a> where  Blumberg tries to pitch <a href="https://twitter.com/sacca">Chris Sacca</a> on investing in his company. Blumberg fails miserably, so Chris turns the tables on him. He delivers convincing pitches both for <em>and</em> against the business idea. Seriously impressive.</p>
<h3 id="reply-all"><a href="http://gimletmedia.com/show/reply-all/">Reply All</a></h3>
<p><a href="http://gimletmedia.com/show/reply-all/">Reply All</a> is the first podcast produced by Gimlet Media (the company that Alex Blumberg starts on the StartUp Podcast). Also it’s great.</p>
<p>This show is all about one of my favorite things: The Internet. They cover the unique ways the Internet has changed our lives. One episode profiled app called “Figure One”, which is like Instagram, but for doctors. Another covered the story of the guy who invented the pop-up ad, and how he regrets what he did. Here&rsquo;s one all about stocking people&rsquo;s financial transactions:</p>
<iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/180955849&color=969699"></iframe>
<p>Reply All is just getting started, but they already have me hooked.</p>
<p><strong>Honorable Mentions:</strong> <a href="http://www.irltalk.com">IRLTalk</a>, <a href="http://www.econtalk.org">EconTalk</a></p>
<h2 id="music">Music</h2>
<p>I listen to music pretty much all day, every day. That being said, I don&rsquo;t pay close to attention to what&rsquo;s going in my ears. Every once in awhile, though, an artist will grab me by the ear lobes and won&rsquo;t let go. Here are a few that did just that this year.</p>
<h3 id="com-truise"><a href="http://rd.io/x/QVNNbzFU3BM/">Com Truise</a></h3>
<p>They say Com Truise is “slow-motion funk”. I’m not sure if that quite describes it, but whatever genre it is, I’m a huge fan.</p>
<iframe width="500" height="80" src="http://rd.io/i/QVNNbyJGUY4/" frameborder="0"></iframe>
<p>Whether I’m driving, coding, or working out: Com Truise has become a staple in my playlists. Also, that name tho.</p>
<h3 id="cold-fresh-pillows"><a href="https://soundcloud.com/cold-fresh-pillows">Cold Fresh Pillows</a></h3>
<p>Add <a href="https://soundcloud.com/cold-fresh-pillows">Cold Fresh Pillows</a> to the growing list of artists found solely on SoundCloud. It’s super chilled out electronic music that is somehow bursting with emotion.</p>
<iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/120579912&amp;color=ff5500&amp;auto_play=false&amp;hide_related=false&amp;show_comments=true&amp;show_user=true&amp;show_reposts=false"></iframe>
<h3 id="dveloped"><a href="https://soundcloud.com/dveloped">D.VELOPED</a></h3>
<p>Excellent remixes which often feature music from my former days. This is absolutely great workout music. Here’s a taste:</p>
<iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/142420750&amp;color=ff5500&amp;auto_play=false&amp;hide_related=false&amp;show_comments=true&amp;show_user=true&amp;show_reposts=false"></iframe>
<p><strong>Honorable Mentions:</strong> <a href="http://rd.io/x/QVNNbyIVHVs/">Little Machines — LIGHTS</a>, <a href="http://rd.io/x/QVNNbyJjr2A/">After the Disco — Broken Bells</a></p>
<hr>
<p>That’s my list. I hope you find one or more of these things as enjoyable and/or useful as I have.</p>
<p>Shout out your most beloved digital things of 2014 in the comments so I can discover them in 2015!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I am not a playlist guy at all. I manually select the next show I want to listen to each time. Control freak!&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Only Charlie Sheen&rsquo;s medicine cabinet is more crammed with junk than today&rsquo;s Evernote.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Sorry, too much Downton Abbey&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>List all Git commits that are on one branch, but aren&#39;t on the other</title>
      <link>https://jerodsanto.net/2014/09/list-all-git-commits-that-are-on-one-branch-but-arent-on-the-other/</link>
      <pubDate>Sat, 06 Sep 2014 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2014/09/list-all-git-commits-that-are-on-one-branch-but-arent-on-the-other/</guid>
      
      
      <description><![CDATA[<p>Common need: show me all the comits that branch <code>X</code> has, but branch <code>Y</code> doesn&rsquo;t.</p>
<p>Why is this need common? Because Heroku-style deployment environments are increasingly common and it&rsquo;s super handy to quickly see which commits you haven&rsquo;t pushed to production.</p>
<p>Here is the Git command in all its glory</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">git log --left-right --graph --cherry-pick --oneline X...Y
</span></span></span></code></pre></div><p>Hopefully obvious, but worth a mention: replace <code>X</code> and <code>Y</code> above with the names of the branches you want to compare.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Common need: show me all the comits that branch <code>X</code> has, but branch <code>Y</code> doesn&rsquo;t.</p>
<p>Why is this need common? Because Heroku-style deployment environments are increasingly common and it&rsquo;s super handy to quickly see which commits you haven&rsquo;t pushed to production.</p>
<p>Here is the Git command in all its glory</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">git log --left-right --graph --cherry-pick --oneline X...Y
</span></span></span></code></pre></div><p>Hopefully obvious, but worth a mention: replace <code>X</code> and <code>Y</code> above with the names of the branches you want to compare.</p>
<p>This is nice, but that doesn&rsquo;t mean we have to memorize it. Aliases to the rescue! Add the following to your <code>.gitconfig</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">[alias]
</span></span></span><span class="line"><span class="cl"><span class="go">  compare = log --left-right --graph --cherry-pick --oneline
</span></span></span></code></pre></div><p>Now if you want to see the commits on your master branch that aren&rsquo;t on Heroku&rsquo;s master branch:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">git compare master...heroku/master
</span></span></span></code></pre></div><p>Admittedly, <code>compare</code> is a pretty generic name for this alias. Naming things is <a href="https://jerodsanto.net/2014/08/naming-things-is-hard-lets-go-shopping/">hard</a>. Let me know if you have a better one!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>A step by step guide to bulletproof 404s on Rails</title>
      <link>https://jerodsanto.net/2014/06/a-step-by-step-guide-to-bulletproof-404s-on-rails/</link>
      <pubDate>Fri, 20 Jun 2014 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2014/06/a-step-by-step-guide-to-bulletproof-404s-on-rails/</guid>
      
      
      <description><![CDATA[<h2 id="step-1-configure-your-router-as-the-exceptions-app">Step 1: Configure your router as the exceptions app</h2>
<p>Since Rails 3 you&rsquo;ve been able to configure an app to handle exceptions, which you want to point right at your router. To do this, add the following to <code>config/application.rb</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">MyApp</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Application</span> <span class="o">&lt;</span> <span class="no">Rails</span><span class="o">::</span><span class="no">Application</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</span><span class="o">.</span><span class="n">exceptions_app</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">routes</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="step-2-add-a-catch-all-route">Step 2: Add a catch-all route</h2>
<p>Make sure this is the last rule in <code>config/routes.rb</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">MyApp</span><span class="o">::</span><span class="no">Application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span> <span class="s2">&#34;*any&#34;</span><span class="p">,</span> <span class="ss">via</span><span class="p">:</span> <span class="ss">:all</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s2">&#34;errors#not_found&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>With this, any requested path — whatever the request type — that doesn&rsquo;t match the previous routing rules will match this rule. The <code>*any</code> path starts with the <code>*</code> wildcard, so it will match anything. The <code>any</code> part is arbitrary, but you have to put <em>something</em> after the <code>*</code> to make it work. I&rsquo;m sure there&rsquo;s a good reason why, but somebody else will have to explain it.</p>]]></description>
      
      <content:encoded><![CDATA[<h2 id="step-1-configure-your-router-as-the-exceptions-app">Step 1: Configure your router as the exceptions app</h2>
<p>Since Rails 3 you&rsquo;ve been able to configure an app to handle exceptions, which you want to point right at your router. To do this, add the following to <code>config/application.rb</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">MyApp</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Application</span> <span class="o">&lt;</span> <span class="no">Rails</span><span class="o">::</span><span class="no">Application</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</span><span class="o">.</span><span class="n">exceptions_app</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">routes</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="step-2-add-a-catch-all-route">Step 2: Add a catch-all route</h2>
<p>Make sure this is the last rule in <code>config/routes.rb</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">MyApp</span><span class="o">::</span><span class="no">Application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span> <span class="s2">&#34;*any&#34;</span><span class="p">,</span> <span class="ss">via</span><span class="p">:</span> <span class="ss">:all</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s2">&#34;errors#not_found&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>With this, any requested path — whatever the request type — that doesn&rsquo;t match the previous routing rules will match this rule. The <code>*any</code> path starts with the <code>*</code> wildcard, so it will match anything. The <code>any</code> part is arbitrary, but you have to put <em>something</em> after the <code>*</code> to make it work. I&rsquo;m sure there&rsquo;s a good reason why, but somebody else will have to explain it.</p>
<h2 id="step-3-implement-errorscontrollernot_found">Step 3: Implement ErrorsController#not_found</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ErrorsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">not_found</span>
</span></span><span class="line"><span class="cl">    <span class="n">respond_to</span> <span class="k">do</span> <span class="o">|</span><span class="nb">format</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="nb">format</span><span class="o">.</span><span class="n">html</span> <span class="p">{</span> <span class="n">render</span> <span class="ss">status</span><span class="p">:</span> <span class="mi">404</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">rescue</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">UnknownFormat</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span> <span class="ss">status</span><span class="p">:</span> <span class="mi">404</span><span class="p">,</span> <span class="ss">text</span><span class="p">:</span> <span class="s2">&#34;nope&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>You may be wondering why I suggest rescuing <code>ActionController::UnknownFormat</code> instead of adding a <code>format.any</code> block to handle any non-html request types. The problem with <code>format.any</code> is that it will only handle <em>known</em> mime types. This is a-okay for 404&rsquo;ing <code>.png</code>s, <code>.json</code>, <code>.xml</code>, etc., but it doesn&rsquo;t handle the real crazy stuff, like <code>wp-login.php</code>. In other words, when it comes to catch-alls, <code>ActionController::UnknownFormat</code> has a bigger glove than <code>format.any</code>.</p>
<h2 id="step-4-add-a-view">Step 4: Add a view</h2>
<p>Create <code>app/views/errors/not_found.html.erb</code> and put your 404 page&rsquo;s markup in there. This will use the application layout by default, so your 404 page will fit in with the rest of your site&rsquo;s style.</p>
<h2 id="step-5-have-a-moscow-mule">Step 5: Have a Moscow Mule</h2>
<p>Or an iced tea. Or some lemonade. Your call.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Ruby Rebus! Part Deux</title>
      <link>https://jerodsanto.net/2014/04/ruby-rebus-part-deux/</link>
      <pubDate>Mon, 07 Apr 2014 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2014/04/ruby-rebus-part-deux/</guid>
      
      
      <description><![CDATA[<p>It&rsquo;s been ~18 months since my <a href="https://jerodsanto.net/2012/10/ruby-rebus">first Ruby Rebus challenge</a> and, frankly, that&rsquo;s just too long to ask y&rsquo;alls nerds to wait. Wait no more! I&rsquo;ve dreamt up 15 brand spankin&rsquo; new rebuses<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> for your guessing pleasure.</p>
<p>Last round I only picked movies from IMDB&rsquo;s <a href="http://www.imdb.com/chart/top">Top 250</a> list. This time the tie that binds these movies is that I&rsquo;ve seen them <em>and</em> I like them.</p>
<p>The other big change this time around is <strong>instant gratification</strong>. Instead of a follow-up post with the answers, just click the button under each rebus once you&rsquo;ve figured it out (or given up)! Enough intro,</p>]]></description>
      
      <content:encoded><![CDATA[<p>It&rsquo;s been ~18 months since my <a href="https://jerodsanto.net/2012/10/ruby-rebus">first Ruby Rebus challenge</a> and, frankly, that&rsquo;s just too long to ask y&rsquo;alls nerds to wait. Wait no more! I&rsquo;ve dreamt up 15 brand spankin&rsquo; new rebuses<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> for your guessing pleasure.</p>
<p>Last round I only picked movies from IMDB&rsquo;s <a href="http://www.imdb.com/chart/top">Top 250</a> list. This time the tie that binds these movies is that I&rsquo;ve seen them <em>and</em> I like them.</p>
<p>The other big change this time around is <strong>instant gratification</strong>. Instead of a follow-up post with the answers, just click the button under each rebus once you&rsquo;ve figured it out (or given up)! Enough intro,</p>
<h2 id="rebus-1">Rebus #1</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="kp">throw</span> <span class="ss">:me</span> <span class="k">if</span> <span class="n">person</span><span class="o">.</span><span class="n">is_a?</span> <span class="no">ConMan</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <a href="http://www.imdb.com/title/tt0264464/"><img src="http://jerodsanto.net/drop/cmiyc.jpg" alt="Catch Me If You can"></a>
</details>

<h2 id="rebus-2">Rebus #2</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">expect</span><span class="p">(</span><span class="no">Game</span><span class="p">)</span><span class="o">.</span><span class="n">to</span> <span class="n">receive</span><span class="p">(</span><span class="ss">:play</span><span class="p">)</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <p><a href="http://www.imdb.com/title/tt0266987/"><img src="http://jerodsanto.net/drop/spy-game.jpg" alt="Spy Game"></a></p>
<p>Gotta pull out your RSpec to solve this one.</p>

</details>

<h2 id="rebus-3">Rebus #3</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">[].</span><span class="n">method</span> <span class="ss">:each</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <p><a href="http://www.imdb.com/title/tt1276104/"><img src="http://jerodsanto.net/drop/looper.jpg" alt="Looper"></a></p>
<p>If you were thinking <code>The Enumerator</code>, you may have just stumbled upon Schwarzenegger&rsquo;s next flick&hellip;</p>

</details>

<h2 id="rebus-4">Rebus #4</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="s2">&#34;CATCGTAATGACGGCCT&#34;</span><span class="o">.</span><span class="n">dup</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <p><a href="http://www.imdb.com/title/tt0119177/"><img src="http://jerodsanto.net/drop/gattaca.jpg" alt="Gattaca"></a></p>
<p>This one would be better if you couldn&rsquo;t practically see the name in the string.</p>

</details>

<h2 id="rebus-5">Rebus #5</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">[</span><span class="no">NoMethodError</span><span class="p">,</span> <span class="no">NameError</span><span class="o">]</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <p><a href="http://www.imdb.com/title/tt0114814/"><img src="http://jerodsanto.net/drop/usual-suspects.jpg" alt="The Usual Suspects"></a></p>
<p>Is it just me or do <code>NoMethodError</code> and <code>NameError</code> account for 95% of all errors?</p>

</details>

<h2 id="rebus-6">Rebus #6</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">[</span><span class="nb">Float</span><span class="o">.</span><span class="n">instance_method</span><span class="p">(</span><span class="ss">:ceil</span><span class="p">),</span> <span class="nb">Float</span><span class="o">.</span><span class="n">instance_method</span><span class="p">(</span><span class="ss">:floor</span><span class="p">)</span><span class="o">]</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <p><a href="http://www.imdb.com/title/tt0128442/"><img src="http://jerodsanto.net/drop/rounders.jpg" alt="Rounders"></a></p>
<p>At first I included the <code>round</code> method, but thought that&rsquo;d be too easy.</p>

</details>

<h2 id="rebus-7">Rebus #7</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Actor</span> <span class="o">=</span> <span class="no">Struct</span><span class="o">.</span><span class="n">new</span> <span class="ss">:movie</span><span class="p">,</span> <span class="ss">:first_name</span>
</span></span><span class="line"><span class="cl"><span class="no">Actor</span><span class="o">.</span><span class="n">new</span> <span class="s2">&#34;Argo&#34;</span><span class="p">,</span> <span class="s2">&#34;John&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">Actor</span><span class="o">.</span><span class="n">new</span> <span class="s2">&#34;Monsters, Inc.&#34;</span><span class="p">,</span> <span class="s2">&#34;John&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">Actor</span><span class="o">.</span><span class="n">new</span> <span class="s2">&#34;The Big Lebowski&#34;</span><span class="p">,</span> <span class="s2">&#34;John&#34;</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <p><a href="http://www.imdb.com/title/tt0104257/"><img src="http://jerodsanto.net/drop/fgm.jpg" alt="A Few Good Men"></a></p>
<p>It&rsquo;s a few John Goodman movies. A few Goodman. Get it?!</p>

</details>

<h2 id="rebus-8">Rebus #8</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Payment</span>
</span></span><span class="line"><span class="cl">  <span class="n">delegate</span> <span class="ss">:give</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="ss">:other</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <p><a href="http://www.imdb.com/title/tt0223897/"><img src="http://jerodsanto.net/drop/pif.jpg" alt="Pay it Forward"></a></p>
<p>This code requires Rails to actually execute, but hopefully it will never actually execute.</p>

</details>

<h2 id="rebus-9">Rebus #9</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Object</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@afraid</span> <span class="o">=</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <p><a href="http://www.imdb.com/title/tt0117381/"><img src="http://jerodsanto.net/drop/primal-fear.jpg" alt="Primal Fear"></a></p>
<p><code>Object</code> is Ruby&rsquo;s primordial ooze.</p>

</details>

<h2 id="rebus-10">Rebus #10</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">%</span><span class="n">i</span><span class="p">(</span><span class="n">stop</span> <span class="n">danger</span> <span class="k">yield</span> <span class="n">detour</span><span class="p">)</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <p><a href="http://www.imdb.com/title/tt0286106/"><img src="http://jerodsanto.net/drop/signs.jpg" alt="Signs"></a></p>
<p>Symbols. Signs. You see what I did there.</p>

</details>

<h2 id="rebus-11">Rebus #11</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">s</span> <span class="o">==</span> <span class="s2">&#34;AZ&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">raise</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <p><a href="http://www.imdb.com/title/tt0093822/"><img src="http://jerodsanto.net/drop/raising-arizona.jpg" alt="Raising Arizona"></a></p>
<p>The challenge here was having something represent Arizona without being obvious. I may have failed at that.</p>

</details>

<h2 id="rebus-12">Rebus #12</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="mi">60</span><span class="o">.</span><span class="n">times</span> <span class="p">{</span> <span class="nb">sleep</span> <span class="mi">1</span> <span class="p">}</span> <span class="o">&amp;&amp;</span> <span class="nb">exit</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <p><a href="http://www.imdb.com/title/tt0187078/"><img src="http://jerodsanto.net/drop/gone-in-60.jpg" alt="Gone in Sixty Seconds"></a></p>
<p><a href="https://www.youtube.com/watch?v=Ll5xHq84A8E">Okay let&rsquo;s ride</a></p>

</details>

<h2 id="rebus-13">Rebus #13</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">begin</span>
</span></span><span class="line"><span class="cl">  <span class="o">[</span><span class="n">darren</span><span class="p">,</span> <span class="n">judith</span><span class="o">].</span><span class="n">join</span>
</span></span><span class="line"><span class="cl"><span class="k">rescue</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <p><a href="http://www.imdb.com/title/tt0239948/"><img src="http://jerodsanto.net/drop/silverman.jpg" alt="Saving Silverman"></a></p>
<p>Judith escaped. <a href="https://www.youtube.com/watch?v=UNEfICd4dMY">DURR!</a></p>

</details>

<h2 id="rebus-14">Rebus #14</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Time</span><span class="p">;</span> <span class="k">def</span> <span class="nf">kill</span><span class="p">;</span> <span class="nb">abort</span><span class="p">;</span> <span class="k">end</span><span class="p">;</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="n">t</span> <span class="o">=</span> <span class="no">Time</span><span class="o">.</span><span class="n">now</span>
</span></span><span class="line"><span class="cl"><span class="n">t</span><span class="o">.</span><span class="n">kill</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <p><a href="http://www.imdb.com/title/tt0117913/"><img src="http://jerodsanto.net/drop/time-to-kill.jpg" alt="A Time to Kill"></a></p>
<p>This code is utterly asinine.</p>

</details>

<h2 id="rebus-15">Rebus #15</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="s2">&#34;スカーレット&#34;</span><span class="o">.</span><span class="n">encode</span> <span class="s2">&#34;ascii&#34;</span> <span class="k">rescue</span> <span class="s2">&#34;&#34;</span>
</span></span></code></pre></div>
<details>
  <summary>Reveal Answer</summary>
  <p><a href="http://www.imdb.com/title/tt0335266/"><img src="http://jerodsanto.net/drop/lit.jpg" alt="Lost in Translation"></a></p>
<p>This one is pretty rad, if I do say so myself.</p>

</details>

<hr>
<p>Which was your favorite? Least favorite? How many of the 15 did you get right? Let me know in the comments!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>or is it rebi?&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Out with Sprout</title>
      <link>https://jerodsanto.net/2014/02/out-with-sprout/</link>
      <pubDate>Tue, 11 Feb 2014 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2014/02/out-with-sprout/</guid>
      
      
      <description><![CDATA[<p>A little over a year ago, I <a href="http://objectlateral.com/blog/omahype-trim-flexxive/">had the pleasure</a> of helping my good friend<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> <a href="http://codyjamespeterson.com">Cody</a> build the 3rd iteration of <a href="http://omahype.com">Omahype</a>. I wasn&rsquo;t a user before the project, but I quickly became one after launch.</p>
<p>Omahype is <em>the</em> place to go when you want to find awesome events in the area. It wasn&rsquo;t long before Rachel and I began pining for a similar site specifically serving<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> families.</p>
<p>We asked Cody if that was something he would help make happen, teamed up with our close friends/family (Jason and Sarah), and created <a href="http://www.outwithsprout.com">Out with Sprout</a>: Omaha&rsquo;s Family Event Calendar.</p>]]></description>
      
      <content:encoded><![CDATA[<p>A little over a year ago, I <a href="http://objectlateral.com/blog/omahype-trim-flexxive/">had the pleasure</a> of helping my good friend<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> <a href="http://codyjamespeterson.com">Cody</a> build the 3rd iteration of <a href="http://omahype.com">Omahype</a>. I wasn&rsquo;t a user before the project, but I quickly became one after launch.</p>
<p>Omahype is <em>the</em> place to go when you want to find awesome events in the area. It wasn&rsquo;t long before Rachel and I began pining for a similar site specifically serving<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> families.</p>
<p>We asked Cody if that was something he would help make happen, teamed up with our close friends/family (Jason and Sarah), and created <a href="http://www.outwithsprout.com">Out with Sprout</a>: Omaha&rsquo;s Family Event Calendar.</p>
<p><a href="http://www.outwithsprout.com"><img src="http://jerodsanto.net/drop/ows.png" alt="Cody killed it with our branding, IMO"></a></p>
<p>Out with Sprout&rsquo;s mission is to help families experience the best places &amp; events that Omaha has to offer. If you live in the area, have kids, and ever want to get out of the house and do something fun together, we think you&rsquo;ll love <a href="http://www.outwithsprout.com">Out with Sprout</a>!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Omahype&rsquo;s designer/developer and frequent Object Lateral collaborator&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Try saying that ten times fast&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Beloved Bits, 2013</title>
      <link>https://jerodsanto.net/2013/12/beloved-bits-2013/</link>
      <pubDate>Tue, 31 Dec 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/12/beloved-bits-2013/</guid>
      
      
      <description><![CDATA[<p>Welcome to the third annual (<a href="https://jerodsanto.net/2011/12/beloved-bits-2011">2011</a>, <a href="https://jerodsanto.net/2012/12/beloved-bits-2012">2012</a>) list of my favorite digital things. That&rsquo;s right kids, another year in the can. Is it just me or are they passing progressively faster?</p>
<p>Remember how I roll? Like this:</p>
<ol>
<li>List items must be anatomically digital</li>
<li>List items must have won <em>me</em> over in 2013</li>
<li>List items may or may not have been released in 2013</li>
</ol>
<p>&lsquo;Nuff said. Let&rsquo;s get right to it:</p>
<h2 id="ios-apps">iOS Apps</h2>
<p>iOS 7 and a retina iPad mini rocked my iOS world this year, along with these apps:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Welcome to the third annual (<a href="https://jerodsanto.net/2011/12/beloved-bits-2011">2011</a>, <a href="https://jerodsanto.net/2012/12/beloved-bits-2012">2012</a>) list of my favorite digital things. That&rsquo;s right kids, another year in the can. Is it just me or are they passing progressively faster?</p>
<p>Remember how I roll? Like this:</p>
<ol>
<li>List items must be anatomically digital</li>
<li>List items must have won <em>me</em> over in 2013</li>
<li>List items may or may not have been released in 2013</li>
</ol>
<p>&lsquo;Nuff said. Let&rsquo;s get right to it:</p>
<h2 id="ios-apps">iOS Apps</h2>
<p>iOS 7 and a retina iPad mini rocked my iOS world this year, along with these apps:</p>
<h3 id="editorial">Editorial</h3>
<p>I used <a href="http://www.hogbaysoftware.com/products/plaintext">PlainText</a> for writing-on-the-go for a long time, but then I realized that almost all my writing is in Markdown. PlainText handles Markdown okay, but it isn&rsquo;t designed <em>specifically</em> for it.</p>
<p><a href="http://omz-software.com/editorial/">Editorial</a>, on the other hand, is designed to write in Markdown. It extends the built-in keyboard with handy shortcuts, syncs to iCloud/Dropbox, and has a ridiculously powerful Workflows system that I&rsquo;ve barely touched the surface of.</p>
<p><a href="http://omz-software.com/editorial/"><img src="http://jerodsanto.net/drop/editorial-pic.png" alt="selfie" width="512" height="384"></a></p>
<p>Not convinced? Read some more <a href="http://www.macstories.net/stories/editorial-for-ipad-review/">thorough</a> <a href="http://www.macdrifter.com/2013/08/editorial-for-ipad-a-landmark-in-ios-text-editors.html">reviews</a>.</p>
<h3 id="soulver">Soulver</h3>
<p><a href="http://www.acqualia.com/soulver/">Soulver for OS X</a> made my list in 2011 and I still use it daily.  This year, I finally made the leap and bought it for <a href="http://www.acqualia.com/soulver/iphone/">iPhone</a>. iCloud syncing convinced me because it&rsquo;s really nice to share ongoing calculations between platforms.</p>
<p>I was still holdng out on buying it for iPad when they released their iOS 7 upgrade which also supported iPad. Procrastination FTW once again!</p>
<p>If you aren&rsquo;t on the <a href="http://www.acqualia.com/soulver/iphone/">Soulver</a> train you need to give it a serious look as it will change how you use (and judge) calculator apps forever.</p>
<h3 id="scanner-pro">Scanner Pro</h3>
<p>I just picked up <a href="http://readdle.com/products/scannerpro/">Scanner Pro</a> a few weeks ago, and boy am I glad I did. I have a crappy hardware scanner and have to scan stuff just often enough to dread using it. I was shopping <a href="http://www.amazon.com/exec/obidos/ASIN/B00ATZ9QMO/ref=nosim&amp;tag=jerodsanto-20">Scan Snaps</a> and wondering if it&rsquo;d be worth the cost for me when Scanner Pro  crossed my radar.</p>
<p>It  does a surprisingly good job of using your device&rsquo;s camera to create high quality scans. Once scanned, it can upload directly to all the usual cloud services.</p>
<p>If you don&rsquo;t have to scan giant heaps of documents often, you should try <a href="http://readdle.com/products/scannerpro/">Scanner Pro</a> before buying any hardware.</p>
<p><strong>Honorable Mentions:</strong> <a href="https://itunes.apple.com/us/app/trello-organize-anything/id461504587?mt=8">Trello</a>, <a href="http://forecast.io">Forecast</a>, <a href="https://itunes.apple.com/us/app/sms-gif/id584608275?mt=8">SMS Gif</a></p>
<h2 id="os-x-apps">OS X Apps</h2>
<p>Not much new in my OS X world this year. I&rsquo;ve either reached critical mass of useful apps or I&rsquo;m getting old and lazy. Still, a couple things worth mentioning:</p>
<h3 id="moom">Moom</h3>
<p>There are a glut of window managers for OS X<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. Overwhelmingly so. If you need one, but you don&rsquo;t want to shop &rsquo;em, you can&rsquo;t go wrong with <a href="http://manytricks.com/moom/">Moom</a>.</p>
<p><img src="http://jerodsanto.net/drop/moom-settings.png
" alt="I guess this means I'm mooming you?"></p>
<p>It covers all the standard bases and adds a nice little UI to the traffic signals in case you forget the keyboard shortcuts you set up.</p>
<h3 id="scribe">Scribe</h3>
<p><a href="http://www.pepperedsoftware.com/scribe.html">Scribe</a> is a great little outliner. I use it whenever I want to organize my thoughts heirarchically<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. It has a near-perfect mix of features and simplicity. $13 on the App Store and worth every penny.</p>
<p><strong>Honorable Mentions:</strong> <a href="http://screenhero.com/">Screen Hero</a>, <a href="http://www.tastycocoabytes.com/cpa/">Cocoa Packet Analyzer</a></p>
<h2 id="games">Games</h2>
<p>Honestly, my post-played game of the year can&rsquo;t make the list because it breaks rule #2. I covered <a href="http://www.robotentertainment.com/games/heroacademy/">Hero Academy</a> last year and I still play it as much now as I did then. It is the best turn-based strategy game on iOS (or maybe any platform) and if it had static item selection<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> I&rsquo;d declare it a Perftect Game. But enough about that, here are two games I <em>did</em> fall in love with in 2013.</p>
<h3 id="badland">Badland</h3>
<p>You&rsquo;ve probably heard of <a href="http://www.badlandgame.com">Badland</a> as it&rsquo;s received much (deserved) praise from Apple and others, but still. This game is <em>GORGEOUS</em>. And fun to boot. A genuine must-have for any gamer with a heart beat.</p>
<iframe width="420" height="315" src="https://jerodsanto.net//www.youtube.com/embed/7nkxyKo09Qs" frameborder="0" allowfullscreen></iframe>
<h3 id="puerto-rico">Puerto Rico</h3>
<p>If you like <a href="http://www.amazon.com/exec/obidos/ASIN/B000W7JWUA/ref=nosim&amp;tag=jerodsanto-20">Settlers of Catan</a> and haven&rsquo;t played <a href="http://www.amazon.com/exec/obidos/ASIN/B00008URUT/ref=nosim&amp;tag=jerodsanto-20">Puerto Rico</a>&hellip; well you just need to play some <a href="http://www.amazon.com/exec/obidos/ASIN/B00008URUT/ref=nosim&amp;tag=jerodsanto-20">Puerto Rico</a>.</p>
<p><a href="http://www.amazon.com/exec/obidos/ASIN/B00008URUT/ref=nosim&amp;tag=jerodsanto-20"><img src="http://jerodsanto.net/drop/puerto-rico.jpg" alt="This non-digital bit is belobved, but did NOT make the list"></a></p>
<p>It has the same feel as Settlers but removes the element of chance and allows for deeper strategies and more nuance. I can&rsquo;t pick the board game here, but there is a <a href="https://itunes.apple.com/us/app/puerto-rico-hd/id438437326?mt=8">decent iPad app</a> that I played to get my fix between game nights.</p>
<p><strong>Honorable Mention:</strong> <a href="https://itunes.apple.com/us/app/samurai-wars/id442916628?mt=8">Samurai Wars</a></p>
<h2 id="music">Music</h2>
<p>Still rockin&rsquo; dat Rdio subscription. Here are a few of my favorite new jams:</p>
<h3 id="triangle--slow-magic">Triangle — Slow Magic</h3>
<p>Slow Magic&rsquo;s <a href="http://www.amazon.com/exec/obidos/ASIN/B007TUJA1O/ref=nosim&amp;tag=jerodsanto-20">debut album</a> is right in my wheelhouse. Chilled out electronic beats with super interesting elements mixing in and out.</p>
<iframe width="500" height="250" src="https://rd.io/i/QVNNbyJIPEM/" frameborder="0"></iframe>
<h3 id="lets-be-still--the-head-and-the-heart">Let&rsquo;s Be Still — The Head and the Heart</h3>
<p>The Head and the Heart have been wildly successful these last few years, and deservedly so. They represent Americana at its best and their <a href="http://www.amazon.com/exec/obidos/ASIN/B00ECL7ZR4/ref=nosim&amp;tag=jerodsanto-20">sophomore effort</a> did not disappoint.</p>
<iframe width="500" height="250" src="https://rd.io/i/QVNNbyJqt7w/" frameborder="0"></iframe>
<h3 id="ghost-on-ghost--iron-and-wine">Ghost on Ghost — Iron and Wine</h3>
<p>I haven&rsquo;t always been a huge Iron and Wine fan, but <a href="http://www.amazon.com/exec/obidos/ASIN/B00BI6SSMC/ref=nosim&amp;tag=jerodsanto-20">Ghost on Ghost</a> is a bit of a departure for him, and a welcomed one to my ears. I love this ablum.</p>
<iframe width="500" height="250" src="https://rd.io/i/QVNNbyJ0eQY/" frameborder="0"></iframe>
<p><strong>Honorable Mentions:</strong> <a href="http://www.amazon.com/exec/obidos/ASIN/B00EJU8ZIG/ref=nosim&amp;tag=jerodsanto-20">Pure Heroine</a>, <a href="https://soundcloud.com/keithkenniff">Keith Kenniff</a>, <a href="http://www.amazon.com/exec/obidos/ASIN/B00C061I3K/ref=nosim&amp;tag=jerodsanto-20">Random Access Memories</a></p>
<h2 id="podcasts">Podcasts</h2>
<p>I think it&rsquo;s safe to say I listen to more podcasts now than ever. It is my most favorite medium for one of my least favorite words: edutainment. In fact, 2013 was the year that I moved beyond mere podcast consumerism and in to podcast creationism<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> with my involvement in <a href="http://thechangelog.com/podcast/">The Changelog</a>.</p>
<h3 id="accidental-tech-podcast">Accidental Tech Podcast</h3>
<p>Last year I grieved at the loss of my (still) favorite podcast: <a href="http://5by5.tv/hypercritical">Hypercritical</a> with John Siracusa. Thankfully, John has gone on to podcast yet again in the form of the <a href="http://atp.fm">Accidental Tech Podcast</a>. Admittedly, ATP is not as good as Hypercritical. Hypercritical was pretty much all John with Dan Benjamin playing support. ATP is a 3-person show and Dan is more interesting &amp; funny than either of the 2 other guys. Oh well, take what you can get. And right now happily I take ATP every week.</p>
<h3 id="the-incomparable">The Incomparable</h3>
<p>I also lost <a href="http://thetotallyradshow.com/">TRS</a> last year<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>, which was an awesome web show covering movies, video games, tv shows, and other geeky things. Then along came <a href="http://5by5.tv/incomparable">The Incomparable</a>.</p>
<p><a href="http://5by5.tv/incomparable"><img src="http://jerodsanto.net/drop/incomparable.jpg" alt="That's some awesome album art right there" width="320" height="320"></a></p>
<p>Hosted by Jason Snell and featuring an ever-rotating panel, The Incomparable is never boring. The show covers all forms of geeky media and I can&rsquo;t recommend it highly enough. It&rsquo;s most fun to listen to when you&rsquo;ve already consumed whatever content is currently under the panel&rsquo;s scrutiney, but I even enjoy listening when I have no idea what they&rsquo;re talking about. Great show.</p>
<p><strong>Honorable Mention:</strong> <a href="http://www.imore.com/debug">Debug</a></p>
<h2 id="copypasted-outro-from-last-year">Copy/Pasted Outro from last Year</h2>
<p>So, that’s my list. I hope you find one or more of these things as enjoyable and/or useful as I have.</p>
<p>Please do recommend any of your most beloved digital things of 2013 in the comments so I can discover them in 2014!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Hopefully Apple sees this as a shortcoming and addresses it in OS X 10.10 Tijuana&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>You&rsquo;re not gonna believe this, but I spelled &lsquo;heirarchically&rsquo; right the first time sans typos or aid. No idea how.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>The game&rsquo;s only flaw is the randomized item queue. With a static queue you could really develop out repeatable strategies.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Stay tuned for even more podcasting from me in 2014, both technical and non.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>The spirit of TRS lives on, though, with Jeff Cannata&rsquo;s new show: <a href="http://www.youtube.com/user/CannataJeff">Newest Latest Best</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Rails 4: let specific actions be embedded as iframes</title>
      <link>https://jerodsanto.net/2013/12/rails-4-let-specific-actions-be-embedded-as-iframes/</link>
      <pubDate>Wed, 04 Dec 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/12/rails-4-let-specific-actions-be-embedded-as-iframes/</guid>
      
      
      <description><![CDATA[<p>Rails 4 added a default <code>X-Frame-Options</code> HTTP header value of <code>SAMEORIGIN</code>. This is good <a href="http://en.wikipedia.org/wiki/Clickjacking">for security</a>, because browsers use this header to decide whether or not your site can be <code>iframe</code>d by other sites.</p>
<p>However, sometimes you <em>do</em> want a particular action to be embeddable in another site. If you know the site which embeds the action, you can simply change the header to explicitly allow it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">my_embeddable_widget</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="o">[</span><span class="s2">&#34;X-FRAME-OPTIONS&#34;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;ALLOW-FROM http://example.com&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>If, instead, you want the action to be embeddable by any site on the web, just delete the header:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Rails 4 added a default <code>X-Frame-Options</code> HTTP header value of <code>SAMEORIGIN</code>. This is good <a href="http://en.wikipedia.org/wiki/Clickjacking">for security</a>, because browsers use this header to decide whether or not your site can be <code>iframe</code>d by other sites.</p>
<p>However, sometimes you <em>do</em> want a particular action to be embeddable in another site. If you know the site which embeds the action, you can simply change the header to explicitly allow it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">my_embeddable_widget</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="o">[</span><span class="s2">&#34;X-FRAME-OPTIONS&#34;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;ALLOW-FROM http://example.com&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>If, instead, you want the action to be embeddable by any site on the web, just delete the header:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">my_embeddable_widget</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">delete</span> <span class="s2">&#34;X-Frame-Options&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>For a single controller action, inlining these changes makes sense. If you&rsquo;re gonna allow multiple actions to be <code>iframe</code>d, you can put the logic in a method and call it from an <code>after_filter</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">allow_iframe</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">delete</span> <span class="s2">&#34;X-Frame-Options&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">after_filter</span> <span class="ss">:allow_iframe</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="ss">:my_embeddable_widget</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">my_embeddable_widget</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>You can get even fancier if you want, but in my experience YAGNI.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Make ActiveAdmin and FriendlyId play nice</title>
      <link>https://jerodsanto.net/2013/11/make-activeadmin-and-friendlyid-play-nice/</link>
      <pubDate>Fri, 15 Nov 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/11/make-activeadmin-and-friendlyid-play-nice/</guid>
      
      
      <description><![CDATA[<p><a href="http://activeadmin.info">ActiveAdmin</a> and <a href="https://github.com/norman/friendly_id">FriendlyId</a> are both handy gems, but they don&rsquo;t play nice together out of the box.</p>
<p>This is because <code>FriendlyId</code>&rsquo;d models don&rsquo;t follow the default <code>to_param</code> style of id-based lookups which ActiveAdmin expects.</p>
<p>However, the two are easily reconciled by changing how ActiveAdmin fetches resources. Throw this in your ActiveAdmin initializer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/initializers/active_admin.rb</span>
</span></span><span class="line"><span class="cl"><span class="no">ActiveAdmin</span><span class="o">::</span><span class="no">ResourceController</span><span class="o">.</span><span class="n">class_eval</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">find_resource</span>
</span></span><span class="line"><span class="cl">    <span class="n">id_field</span> <span class="o">=</span> <span class="s2">&#34;id&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">scoped_collection</span><span class="o">.</span><span class="n">is_a?</span> <span class="no">FriendlyId</span>
</span></span><span class="line"><span class="cl">      <span class="n">id_field</span> <span class="o">=</span> <span class="n">scoped_collection</span><span class="o">.</span><span class="n">friendly_id_config</span><span class="o">.</span><span class="n">query_field</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">scoped_collection</span><span class="o">.</span><span class="n">find_by!</span> <span class="n">id_field</span> <span class="o">=&gt;</span> <span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>That&rsquo;ll do it. You can alternatively define <code>find_resource</code> inside each registered resource&rsquo;s <code>controller</code> block if you want to be picky.</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="http://activeadmin.info">ActiveAdmin</a> and <a href="https://github.com/norman/friendly_id">FriendlyId</a> are both handy gems, but they don&rsquo;t play nice together out of the box.</p>
<p>This is because <code>FriendlyId</code>&rsquo;d models don&rsquo;t follow the default <code>to_param</code> style of id-based lookups which ActiveAdmin expects.</p>
<p>However, the two are easily reconciled by changing how ActiveAdmin fetches resources. Throw this in your ActiveAdmin initializer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/initializers/active_admin.rb</span>
</span></span><span class="line"><span class="cl"><span class="no">ActiveAdmin</span><span class="o">::</span><span class="no">ResourceController</span><span class="o">.</span><span class="n">class_eval</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">find_resource</span>
</span></span><span class="line"><span class="cl">    <span class="n">id_field</span> <span class="o">=</span> <span class="s2">&#34;id&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">scoped_collection</span><span class="o">.</span><span class="n">is_a?</span> <span class="no">FriendlyId</span>
</span></span><span class="line"><span class="cl">      <span class="n">id_field</span> <span class="o">=</span> <span class="n">scoped_collection</span><span class="o">.</span><span class="n">friendly_id_config</span><span class="o">.</span><span class="n">query_field</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">scoped_collection</span><span class="o">.</span><span class="n">find_by!</span> <span class="n">id_field</span> <span class="o">=&gt;</span> <span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>That&rsquo;ll do it. You can alternatively define <code>find_resource</code> inside each registered resource&rsquo;s <code>controller</code> block if you want to be picky.</p>
<p>There may be a better way to derive the <code>id_field</code>, but I don&rsquo;t know it. Let me know if you do.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Your dev user should be really crazy</title>
      <link>https://jerodsanto.net/2013/11/your-dev-user-should-be-really-crazy/</link>
      <pubDate>Fri, 08 Nov 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/11/your-dev-user-should-be-really-crazy/</guid>
      
      
      <description><![CDATA[<p>Up until now, my dev user on systems I&rsquo;m building has been pretty predictable. Something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">User</span><span class="o">.</span><span class="n">create</span> <span class="ss">first_name</span><span class="p">:</span> <span class="s2">&#34;Jerod&#34;</span><span class="p">,</span> <span class="ss">last_name</span><span class="p">:</span> <span class="s2">&#34;Santo&#34;</span>
</span></span></code></pre></div><p>There are other attributes like email, password, etc., but you get the point.</p>
<p>This is convenient, but pretty short sighted. I&rsquo;ll use this account for the duration of development. Why not make it more useful by populating its attributes with crazy data?</p>
<p>This user is much more useful:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">User</span><span class="o">.</span><span class="n">create</span> <span class="ss">first_name</span><span class="p">:</span> <span class="s2">&#34;&lt;script&gt;alert(&#39;OHAI!&#39;);&lt;/script&gt;&#34;</span><span class="p">,</span> <span class="ss">last_name</span><span class="p">:</span> <span class="s2">&#34;朋美&#34;</span>
</span></span></code></pre></div><p>Now we&rsquo;re talking! This user will help catch any <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">XSS</a> vulnerabilities and ensure that unicode text is rendered properly.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Up until now, my dev user on systems I&rsquo;m building has been pretty predictable. Something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">User</span><span class="o">.</span><span class="n">create</span> <span class="ss">first_name</span><span class="p">:</span> <span class="s2">&#34;Jerod&#34;</span><span class="p">,</span> <span class="ss">last_name</span><span class="p">:</span> <span class="s2">&#34;Santo&#34;</span>
</span></span></code></pre></div><p>There are other attributes like email, password, etc., but you get the point.</p>
<p>This is convenient, but pretty short sighted. I&rsquo;ll use this account for the duration of development. Why not make it more useful by populating its attributes with crazy data?</p>
<p>This user is much more useful:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">User</span><span class="o">.</span><span class="n">create</span> <span class="ss">first_name</span><span class="p">:</span> <span class="s2">&#34;&lt;script&gt;alert(&#39;OHAI!&#39;);&lt;/script&gt;&#34;</span><span class="p">,</span> <span class="ss">last_name</span><span class="p">:</span> <span class="s2">&#34;朋美&#34;</span>
</span></span></code></pre></div><p>Now we&rsquo;re talking! This user will help catch any <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">XSS</a> vulnerabilities and ensure that unicode text is rendered properly.</p>
<p>I know, I know. Your apps are never subject to such fails because you&rsquo;re smart and good looking to boot. But why not?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Ruby Quick Tip: Easily Count Occurrences of Array Elements</title>
      <link>https://jerodsanto.net/2013/10/ruby-quick-tip-easily-count-occurrences-of-array-elements/</link>
      <pubDate>Thu, 24 Oct 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/10/ruby-quick-tip-easily-count-occurrences-of-array-elements/</guid>
      
      
      <description><![CDATA[<p>Have you ever needed to see how many times each element occurs in an array? Perhaps sort the elements by occurrence? Using an awesome feature of <code>Hash.new</code> makes this task quick and easy.</p>
<p><code>Hash.new</code> takes an argument that will be returned when a key that doesn&rsquo;t correspond to a hash entry is accessed. Basically, a default value for all hash keys.</p>
<p>With that knowledge in hand, we can take our array of elements and easily get occurrence counts for each unique element. We just need to initialize each key to 0 and then increment the count each time the element appears.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Have you ever needed to see how many times each element occurs in an array? Perhaps sort the elements by occurrence? Using an awesome feature of <code>Hash.new</code> makes this task quick and easy.</p>
<p><code>Hash.new</code> takes an argument that will be returned when a key that doesn&rsquo;t correspond to a hash entry is accessed. Basically, a default value for all hash keys.</p>
<p>With that knowledge in hand, we can take our array of elements and easily get occurrence counts for each unique element. We just need to initialize each key to 0 and then increment the count each time the element appears.</p>
<p>Let&rsquo;s trump up an array of words, shall we?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">words</span> <span class="o">=</span> <span class="sx">%w(how much wood would a wood chuck chuck)</span>
</span></span></code></pre></div><p>Good enough. Now, we want to know how many times each word occurs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">counts</span> <span class="o">=</span> <span class="no">Hash</span><span class="o">.</span><span class="n">new</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">words</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">word</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">counts</span><span class="o">[</span><span class="n">word</span><span class="o">]</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># {&#34;how&#34;=&gt;1, &#34;much&#34;=&gt;1, &#34;wood&#34;=&gt;2, &#34;could&#34;=&gt;1, &#34;a&#34;=&gt;1, &#34;chuck&#34;=&gt;2}</span>
</span></span></code></pre></div><p>Isn&rsquo;t that easy? Here&rsquo;s a bonus tip. If we use <code>each_with_object</code> we can turn that baby into a one-liner:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">words</span><span class="o">.</span><span class="n">each_with_object</span><span class="p">(</span><span class="no">Hash</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span> <span class="p">{</span> <span class="o">|</span><span class="n">word</span><span class="p">,</span><span class="n">counts</span><span class="o">|</span> <span class="n">counts</span><span class="o">[</span><span class="n">word</span><span class="o">]</span> <span class="o">+=</span> <span class="mi">1</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># {&#34;how&#34;=&gt;1, &#34;much&#34;=&gt;1, &#34;wood&#34;=&gt;2, &#34;could&#34;=&gt;1, &#34;a&#34;=&gt;1, &#34;chuck&#34;=&gt;2}</span>
</span></span></code></pre></div><p>From this point sorting or filtering our elements by occurrence count should be a breeze. Enjoy!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>My biggest disappointment with Apple&#39;s 2013 iPhone upgrades</title>
      <link>https://jerodsanto.net/2013/09/my-biggest-disappointment-with-apples-2013-iphone-upgrades/</link>
      <pubDate>Fri, 20 Sep 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/09/my-biggest-disappointment-with-apples-2013-iphone-upgrades/</guid>
      
      
      <description><![CDATA[<p>On the <a href="http://atp.fm/episodes/31-swimming-in-16-gb-gold">latest episode</a> of ATP (and the &ldquo;after dark&rdquo; of the <a href="http://atp.fm/episodes/30-full-frontal-thumb">previous ep.</a>) John Siracusa ranted<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> about his disappointment in Apple&rsquo;s failure to &ldquo;storage shift&rdquo; the iPhone 5S. Storage sizes for the iPhone have been stuck at 16GB/32GB/64GB for a few generations and the 5S does not improve the situation at all.</p>
<p>I get his disappointment even though I&rsquo;m not personally too affected by Apple&rsquo;s decision. I&rsquo;ve always bought the smallest storage capacity available (I&rsquo;ve owned the 3G, 4, and now 5) and have never run out of space<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. I&rsquo;m not sure why. I listen to music, take pictures, download podcasts, and watch videos on my phone. I&rsquo;ve always been a bit of a &ldquo;purger&rdquo; when it comes to digital media, so maybe that explains it. But I digress&hellip;</p>]]></description>
      
      <content:encoded><![CDATA[<p>On the <a href="http://atp.fm/episodes/31-swimming-in-16-gb-gold">latest episode</a> of ATP (and the &ldquo;after dark&rdquo; of the <a href="http://atp.fm/episodes/30-full-frontal-thumb">previous ep.</a>) John Siracusa ranted<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> about his disappointment in Apple&rsquo;s failure to &ldquo;storage shift&rdquo; the iPhone 5S. Storage sizes for the iPhone have been stuck at 16GB/32GB/64GB for a few generations and the 5S does not improve the situation at all.</p>
<p>I get his disappointment even though I&rsquo;m not personally too affected by Apple&rsquo;s decision. I&rsquo;ve always bought the smallest storage capacity available (I&rsquo;ve owned the 3G, 4, and now 5) and have never run out of space<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. I&rsquo;m not sure why. I listen to music, take pictures, download podcasts, and watch videos on my phone. I&rsquo;ve always been a bit of a &ldquo;purger&rdquo; when it comes to digital media, so maybe that explains it. But I digress&hellip;</p>
<p>What John didn&rsquo;t mention — and a sin equally, if not more egregious — is the lack of &ldquo;cloud storage shift&rdquo;. If you think 16GB is a limiting amount of storage for a phone in 2013/2014, try backing that phone up to your <em>5GB iCloud storage</em>.</p>
<p>Remember above when I said I don&rsquo;t use much storage on my device? Check this out:</p>
<p><img src="http://jerodsanto.net/drop/iCloudStorage.jpg" alt="2.8GB of that 4.4GB is in Camera Roll"></p>
<p>Let me just say preemptively that I know I can buy more storage. That&rsquo;s not the point. My point is that I am <em>not</em> a heavy storage user and I can&rsquo;t even back up my phone and an iPad successfully.</p>
<p>This is not an isolated incident either. My wife and my sister have both come to me with the same problem. We end up deleting a bunch of stuff off their phones in order to get backups working again. I guarantee thousands (millions?) of Apple&rsquo;s customers are similarly frustrated with their iCloud backup experience.</p>
<p>Cloud services have been Apple&rsquo;s achilles heel and here is just one more glaring example of them sucking at it.</p>
<p>If there&rsquo;s one<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> feature that I&rsquo;m demanding in 2014 it&rsquo;s not a shift in <em>flash</em> storage. It&rsquo;s a shift in <em>cloud</em> storage.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>which is pretty much my favorite thing in the nerd world&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>right now I have 4GB free on my 16GB iPhone 5&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>hilarious hypothetical, since I have hundreds of &ldquo;demands&rdquo;&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>You don&#39;t have to annotate your AngularJS injections anymore</title>
      <link>https://jerodsanto.net/2013/08/you-dont-have-to-annotate-your-angularjs-injections-anymore/</link>
      <pubDate>Fri, 02 Aug 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/08/you-dont-have-to-annotate-your-angularjs-injections-anymore/</guid>
      
      
      <description><![CDATA[<p>One unfortunate drawback of how <a href="http://angularjs.org">AngularJS&rsquo;s</a> dependency injection works is that it breaks when your code is minified. The reason it fails is that minification renames variables that Angular uses to resolve the dependencies.</p>
<p>So the natural way of injecting dependencies into a controller doesn&rsquo;t work:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">controller</span><span class="p">(</span><span class="s2">&#34;MyCtrl&#34;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">$scope</span><span class="p">,</span> <span class="nx">$http</span><span class="p">,</span> <span class="nx">MyService</span><span class="p">)</span> <span class="p">{});</span>
</span></span></code></pre></div><p>The minifier renames <code>$scope</code>, <code>$http</code>, and <code>MyService</code> (in order to, ahem, minify them) and the injection breaks.</p>
<p>The old answer to this problem (which you will still see people promoting) is to &ldquo;annotate&rdquo; your injections. At every injection point you pass in an array that resolves the variable/injection pair manually.</p>]]></description>
      
      <content:encoded><![CDATA[<p>One unfortunate drawback of how <a href="http://angularjs.org">AngularJS&rsquo;s</a> dependency injection works is that it breaks when your code is minified. The reason it fails is that minification renames variables that Angular uses to resolve the dependencies.</p>
<p>So the natural way of injecting dependencies into a controller doesn&rsquo;t work:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">controller</span><span class="p">(</span><span class="s2">&#34;MyCtrl&#34;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">$scope</span><span class="p">,</span> <span class="nx">$http</span><span class="p">,</span> <span class="nx">MyService</span><span class="p">)</span> <span class="p">{});</span>
</span></span></code></pre></div><p>The minifier renames <code>$scope</code>, <code>$http</code>, and <code>MyService</code> (in order to, ahem, minify them) and the injection breaks.</p>
<p>The old answer to this problem (which you will still see people promoting) is to &ldquo;annotate&rdquo; your injections. At every injection point you pass in an array that resolves the variable/injection pair manually.</p>
<p>The above code changes to this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">controller</span><span class="p">(</span><span class="s2">&#34;MyCtrl&#34;</span><span class="p">,</span> <span class="p">[</span><span class="s2">&#34;$scope&#34;</span><span class="p">,</span> <span class="s2">&#34;$http&#34;</span><span class="p">,</span> <span class="s2">&#34;MyService&#34;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">$scope</span><span class="p">,</span> <span class="nx">$http</span><span class="p">,</span> <span class="nx">MyService</span><span class="p">)</span> <span class="p">{}]);</span>
</span></span></code></pre></div><p>I&rsquo;m sorry but that code is just crazy town.</p>
<p>Thankfully, there is no longer a good reason to manually annotate your injections. Just make the computer do it for you by utilizing Brian Ford&rsquo;s excellent <a href="https://github.com/btford/ngmin">ngmin</a>.</p>
<p>The tool is available as a grunt task via <a href="https://github.com/btford/grunt-ngmin">grunt-ngmin</a> and in Rails&rsquo; asset pipeline via <a href="http://rubygems.org/gems/ngmin-rails">ngmin-rails</a> so you can integrate it easily into your build process and avoid the headache and ugliness that is manual dependency injection annotations.</p>
<p>So please do.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>CoffeeScript Classes Play Nicely with AngularJS Factories</title>
      <link>https://jerodsanto.net/2013/07/coffeescript-classes-play-nicely-with-angularjs-factories/</link>
      <pubDate>Sun, 28 Jul 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/07/coffeescript-classes-play-nicely-with-angularjs-factories/</guid>
      
      
      <description><![CDATA[<p>One of CoffeeScript&rsquo;s many virtues is built-in support for <a href="http://coffeescript.org/#classes">classes, inheritance, and super</a> via the <code>class</code> keyword. One of AngularJS&rsquo;s many virtues is built-in <a href="http://docs.angularjs.org/guide/dev_guide.services.creating_services">dependency injection of services</a> via factory functions.</p>
<p>How do you use CoffeeScript classes and AngularJS factories together? There is no trickery needed at all. You just define your class like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coffeescript" data-lang="coffeescript"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">factory</span> <span class="s">&#34;Ticket&#34;</span><span class="p">,</span> <span class="nf">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nx">Ticket</span>
</span></span><span class="line"><span class="cl">        <span class="nv">constructor: </span><span class="nf">(@price, @count) -&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nv">amount: </span><span class="nf">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nx">@price</span> <span class="o">*</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">@count</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
</span></span></code></pre></div><p>Now you can inject a <code>Ticket</code> factory into, for instance, your controller context and use it like so:</p>]]></description>
      
      <content:encoded><![CDATA[<p>One of CoffeeScript&rsquo;s many virtues is built-in support for <a href="http://coffeescript.org/#classes">classes, inheritance, and super</a> via the <code>class</code> keyword. One of AngularJS&rsquo;s many virtues is built-in <a href="http://docs.angularjs.org/guide/dev_guide.services.creating_services">dependency injection of services</a> via factory functions.</p>
<p>How do you use CoffeeScript classes and AngularJS factories together? There is no trickery needed at all. You just define your class like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coffeescript" data-lang="coffeescript"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">factory</span> <span class="s">&#34;Ticket&#34;</span><span class="p">,</span> <span class="nf">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nx">Ticket</span>
</span></span><span class="line"><span class="cl">        <span class="nv">constructor: </span><span class="nf">(@price, @count) -&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nv">amount: </span><span class="nf">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nx">@price</span> <span class="o">*</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">@count</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
</span></span></code></pre></div><p>Now you can inject a <code>Ticket</code> factory into, for instance, your controller context and use it like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coffeescript" data-lang="coffeescript"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">controller</span> <span class="s">&#34;MainCtrl&#34;</span><span class="p">,</span> <span class="nf">($scope, Ticket) -&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$scope.ticket = </span><span class="k">new</span> <span class="nx">Ticket</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
</span></span></code></pre></div><p>And use it in a template if you like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{% raw %}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">ng-controller</span><span class="o">=</span><span class="s">&#34;MainCtrl&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>That&#39;ll be {{ticket.amount()}} pesos, amigo<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{% endraw %}
</span></span></code></pre></div><p>Thanks for playing nice, you two.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>So I gave a talk for NebraskaJS about AngularJS</title>
      <link>https://jerodsanto.net/2013/07/so-i-gave-a-talk-for-nebraskajs-about-angularjs/</link>
      <pubDate>Sat, 13 Jul 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/07/so-i-gave-a-talk-for-nebraskajs-about-angularjs/</guid>
      
      
      <description><![CDATA[<p>During the talk, I walked through building a little <a href="http://angularjs.org">Angular</a> app called <a href="https://github.com/jerodsanto/starlight">Starlight</a>. I think it turned out not too bad.</p>
<p><a href="http://techomaha.com">TechOmaha</a> was awesome enough to record and publish a video of the talk, so here it is for your viewing pleasure. Be sure to watch it in full screen mode so you can actually read the code as it&rsquo;s discussed.</p>
<iframe width="560" height="315" src="https://jerodsanto.net//www.youtube.com/embed/1JS07_sFMcs?rel=0" frameborder="0" allowfullscreen></iframe>
<p>You can also check out <a href="http://slid.es/jerodsanto/nebraskajs-angularjs">the slides</a> and <a href="https://github.com/jerodsanto/starlight">the source code</a> of the web app built during the talk.</p>]]></description>
      
      <content:encoded><![CDATA[<p>During the talk, I walked through building a little <a href="http://angularjs.org">Angular</a> app called <a href="https://github.com/jerodsanto/starlight">Starlight</a>. I think it turned out not too bad.</p>
<p><a href="http://techomaha.com">TechOmaha</a> was awesome enough to record and publish a video of the talk, so here it is for your viewing pleasure. Be sure to watch it in full screen mode so you can actually read the code as it&rsquo;s discussed.</p>
<iframe width="560" height="315" src="https://jerodsanto.net//www.youtube.com/embed/1JS07_sFMcs?rel=0" frameborder="0" allowfullscreen></iframe>
<p>You can also check out <a href="http://slid.es/jerodsanto/nebraskajs-angularjs">the slides</a> and <a href="https://github.com/jerodsanto/starlight">the source code</a> of the web app built during the talk.</p>
<p>Thanks to <a href="http://nebraskajs.com/">NebraskaJS</a> for having me and all who came out!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Jekyll with Footnotes</title>
      <link>https://jerodsanto.net/2013/05/jekyll-with-footnotes/</link>
      <pubDate>Mon, 06 May 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/05/jekyll-with-footnotes/</guid>
      
      
      <description><![CDATA[<div class="notice"><a href="https://twitter.com/kraminator">Jonathon Kram</a> has informed me that Kramdown does, indeed, support Footnotes. Using it is likely easier than the method I describe in this post. Thanks, Jonathon!</div>
<p>None of Jekyll&rsquo;s Markdown renderers support footnotes out of the box<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, but a <a href="https://github.com/triplecanopy/redcarpet">RedCarpet fork</a> has the feature. Here&rsquo;s how I got footnotes working in two easy steps:</p>
<h2 id="1-use-the-fork">1) Use the fork</h2>
<p>The RedCarpet gem we want to use is not available on <a href="https://rubygems.org">RubyGems.org</a>, but Bundler can install it directly from GitHub. Add a <code>Gemfile</code> to your Jekyll site&rsquo;s root directory that looks like this:</p>]]></description>
      
      <content:encoded><![CDATA[<div class="notice"><a href="https://twitter.com/kraminator">Jonathon Kram</a> has informed me that Kramdown does, indeed, support Footnotes. Using it is likely easier than the method I describe in this post. Thanks, Jonathon!</div>
<p>None of Jekyll&rsquo;s Markdown renderers support footnotes out of the box<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, but a <a href="https://github.com/triplecanopy/redcarpet">RedCarpet fork</a> has the feature. Here&rsquo;s how I got footnotes working in two easy steps:</p>
<h2 id="1-use-the-fork">1) Use the fork</h2>
<p>The RedCarpet gem we want to use is not available on <a href="https://rubygems.org">RubyGems.org</a>, but Bundler can install it directly from GitHub. Add a <code>Gemfile</code> to your Jekyll site&rsquo;s root directory that looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">source</span> <span class="s2">&#34;https://rubygems.org&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s2">&#34;jekyll&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s2">&#34;redcarpet&#34;</span><span class="p">,</span> <span class="ss">github</span><span class="p">:</span> <span class="s2">&#34;triplecanopy/redcarpet&#34;</span>
</span></span></code></pre></div><p>Run <code>bundle install</code> and then you should be all ready to go.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<h2 id="2-configure-redcarpet">2) Configure RedCarpet</h2>
<p>You have to configure Jekyll to use RedCarpet for Markdown rendering, but you also have to configure RedCarpet to use the <code>footnotes</code> extension. Add these lines to your <code>_config.yml</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="ss">markdown</span><span class="p">:</span> <span class="n">redcarpet</span>
</span></span><span class="line"><span class="cl"><span class="ss">redcarpet</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="ss">extensions</span><span class="p">:</span> <span class="o">[</span><span class="n">footnotes</span><span class="o">]</span>
</span></span></code></pre></div><p>That&rsquo;s it! You now have footnotes with Jekyll.</p>
<p>Enjoy.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Footnotes is not an official Markdown feature, but was added by <a href="http://michelf.ca/projects/php-markdown/">PHP-Markdown</a> and a few derivative projects. The feature hasn&rsquo;t made it in to RedCarpet proper (by way of <a href="https://github.com/vmg/sundown">Sundown</a>) because of the concerted effort by GitHub, Reddit, etc. to develop and support a Markdown standard which will replace Sundown. See <a href="https://github.com/vmg/sundown/pull/141#issuecomment-10846092">this issue</a> if you&rsquo;re interested in learning more.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>You will now have to use <code>bundle exec jekyll</code> to invoke Bundler and pick up the correct RedCarpet gem. If that&rsquo;s a pain, there are <a href="https://github.com/mpapis/rubygems-bundler">workarounds</a> for <code>bundle exec</code>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Tips for a successful switch to a standing desk</title>
      <link>https://jerodsanto.net/2013/05/tips-for-a-successful-switch-to-a-standing-desk/</link>
      <pubDate>Thu, 02 May 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/05/tips-for-a-successful-switch-to-a-standing-desk/</guid>
      
      
      <description><![CDATA[<p>I&rsquo;ve been standing while I work for over two years now.</p>
<p>People often ask me about the particulars of how I transitioned away from sitting all day. Sometimes they&rsquo;re just making convo, but often they are looking for tips on making the switch themselves, so I thought I&rsquo;d jot down a few things for y&rsquo;all to reference.</p>
<h2 id="1-avoid-the-sitstand-desk-trap">1) Avoid the sit/stand desk trap</h2>
<p>You can waste a lot of time and money shopping awesome sit/stand desks online. Whether manual, motorized, or hydraulically charged, these convertible desks aren&rsquo;t worth their exorbitant costs and they surely won&rsquo;t help you succeed more than any old tall desk would.</p>]]></description>
      
      <content:encoded><![CDATA[<p>I&rsquo;ve been standing while I work for over two years now.</p>
<p>People often ask me about the particulars of how I transitioned away from sitting all day. Sometimes they&rsquo;re just making convo, but often they are looking for tips on making the switch themselves, so I thought I&rsquo;d jot down a few things for y&rsquo;all to reference.</p>
<h2 id="1-avoid-the-sitstand-desk-trap">1) Avoid the sit/stand desk trap</h2>
<p>You can waste a lot of time and money shopping awesome sit/stand desks online. Whether manual, motorized, or hydraulically charged, these convertible desks aren&rsquo;t worth their exorbitant costs and they surely won&rsquo;t help you succeed more than any old tall desk would.</p>
<p>You may believe that sinking a bunch of money into your new goal is a good first step toward accomplishing it. It is not. The best way to accomplish your goal is to set about <em>doing it</em>, not <em>shopping it</em>.</p>
<p>I started down the sit/stand rat hole, but I ended up saving my hard-earned cash and went with a homemade standing desk fashioned from plywood. I used that desk for 18 months or so and just recently upgraded to a build-it-yourself Ikea desk that looks pretty good and still didn&rsquo;t break my bank like a sit/stand would.</p>
<p><img src="http://jerodsanto.net/drop/standing-desk.jpg" alt="Made from Ikea parts totaling &lt; $200"></p>
<p>I can&rsquo;t speak from personal experience, but I&rsquo;ve read &amp; seen quite a few folks who bought a fancy sit/stand desk and ended up just sitting the whole time anyhow.</p>
<p>You want to be able to sit down when you&rsquo;re tired (see #3 for more on that), but you don&rsquo;t want it to be <em>that easy</em> to sit down or you&rsquo;ll never stand up.</p>
<h3 id="2-height-matters-most">2) Height matters most</h3>
<p>What <em>is</em> worth your time is getting the height of your standing desk right. The particulars will vary from person-to-person, but a good rule of thumb is to have your arms at slightly more than a 90° angle when typing.</p>
<p>If your desk is too tall (my first one was) then the blood will rush out of your hands. This can be painful and probably not good for you.</p>
<p>If your desk is too short (my second one was until I readjusted it) you will have to slouch down while you stand there. This is a quick way to acquire a neck or back injury.</p>
<p><img src="http://jerodsanto.net/drop/arm-angles.jpg" alt="The 3rd angle is good, but the drawing is still bad"></p>
<p>I&rsquo;ve also read that the preferred position for your displays is at eye level, which leads many people to prop them up higher than their keyboard somehow, but I look down at mine a bit and it&rsquo;s never bothered me. YMMV.</p>
<h3 id="3-ease-into-it">3) Ease into it</h3>
<p>There&rsquo;s no easier way to fail than to define success as something outside of your ability. If you go cold turkey from sitting all day to standing all day you will fail. Your body is not ready for it.</p>
<p>Instead, start small and build from there. When I first started my transition I would stand for an hour in the morning, sit until lunch, stand for an hour after lunch, and sit until the day was over.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>A week or so later I would stand for two hours in the morning, sit until lunch, stand for an hour after lunch, and sit again until the end of the day.</p>
<p>It takes time, so don&rsquo;t be discouraged if your feet hurt too much in the beginning (and they will — see #4 for more on that). All in all it probably took me 4-6 months before I was standing for the majority of the day.</p>
<p>And you know what? I still sit down sometimes! My principle is to <em>just sit down when I&rsquo;m tired</em>. Lately this has been a of couple times each week, in the late afternoons.</p>
<p>Standing all day should not be some kind of Stoic routine (Stoicism is always misplaced, btw). The point is to make your work day <em>better</em>! If it&rsquo;s not <em>better</em>, then by all means sit down!</p>
<h3 id="4-protect-your-feet">4) Protect your feet</h3>
<p>The only unavoidable discomfort from using a standing desk will manifest in your feet. You <em>cannot</em> avoid this and eventually they will grow stronger and the pain will subside, but in the meantime there are a couple things you can do to lessen the pain.</p>
<p>The best strategy is to wear a good pair of athletic shoes while you work. This is easy and just works, but it is not always practical or even desirable.</p>
<p>So, in addition to athletic shoes, I suggest getting an anti-fatigue mat. I got <a href="http://www.amazon.com/exec/obidos/ASIN/B003BYRDKM/ref=nosim&amp;tag=jerodsanto-20">this mat</a> off Amazon and am quite happy with it. You can tell from the picture below how cushy it is.</p>
<p><a href="http://www.amazon.com/exec/obidos/ASIN/B003BYRDKM/ref=nosim&amp;tag=jerodsanto-20"><img src="http://jerodsanto.net/drop/mat.jpg" alt=""></a></p>
<p>I like to work with bare feet or in slippers, so I switch back and forth between the mat and athletic shoes.</p>
<h3 id="5-get-away-from-the-desk">5) Get away from the desk</h3>
<p>One of the great advantages of standing is that you can more easily move around. Take advantage of that!</p>
<p>On a phone call? Walk away from the desk. Brain storming a new product? Walk away from the desk. Toiling away on a hard problem? Walk away from the desk.</p>
<p>Another great thing about standing is that it makes sitting down feel like a treat! Feeling unproductive? Go find a comfy chair and just relax for a few minutes. Your feet will recover quickly and you&rsquo;ll be recharged in no time.</p>
<h3 id="6-you-should-try-it">6) You should try it</h3>
<p>This isn&rsquo;t so much a tip as it is an exhortation. You can read all the pros &amp; cons of sitting vs. standing if you want, but you won&rsquo;t know if standing is good for you until you try it.</p>
<p>Anecodotal evidence and all that, but I have come to love standing and doubt that I&rsquo;ll ever switch back to sitting full time. I find my mind is sharper in the morning, I&rsquo;m <em>less</em> tired during the early afternoon lull, and I&rsquo;m <em>more</em> tired at the end of the day than I was when I sat.</p>
<p>I hope one or more of these tips helps you during the switch!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>You may be thinking that this advice contradicts #1 where I said don&rsquo;t get a sit/stand desk. You <em>will</em> need a place to sit down and get work done, but you do <em>not</em> need that place to be the same desk. I use a laptop and can move about my office quite easily. If you are tied to a desktop you may need to ignore #1.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Changeloggin&#39;</title>
      <link>https://jerodsanto.net/2013/04/changeloggin/</link>
      <pubDate>Mon, 22 Apr 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/04/changeloggin/</guid>
      
      
      <description><![CDATA[<p>It&rsquo;s been quieter around these parts lately<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, but that&rsquo;s not because I haven&rsquo;t been writing. If you keep up with me <a href="https://twitter.com/jerodsanto">on Twitter</a>, you may have noticed that I&rsquo;ve been <a href="http://thechangelog.com/author/jerodsanto/">contributing</a> to <a href="http://thechangelog.com">The Changelog</a> for the last few months!</p>
<p><a href="http://thechangelog.com"><img src="http://jerodsanto.net/drop/changelog-logo.png" alt=""></a></p>
<p>I build my living on top of open source, so it&rsquo;s an honor and a privilege to help Adam, Andrew, and the team shine the spotlight on deserving projects.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>Speaking of The Changelog&hellip; did you know it recently <em>eschewed advertisers</em> and is now run on <a href="https://thechangelog.com/membership/">memberships</a> alone? That&rsquo;s pretty rad, if you ask me.</p>]]></description>
      
      <content:encoded><![CDATA[<p>It&rsquo;s been quieter around these parts lately<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, but that&rsquo;s not because I haven&rsquo;t been writing. If you keep up with me <a href="https://twitter.com/jerodsanto">on Twitter</a>, you may have noticed that I&rsquo;ve been <a href="http://thechangelog.com/author/jerodsanto/">contributing</a> to <a href="http://thechangelog.com">The Changelog</a> for the last few months!</p>
<p><a href="http://thechangelog.com"><img src="http://jerodsanto.net/drop/changelog-logo.png" alt=""></a></p>
<p>I build my living on top of open source, so it&rsquo;s an honor and a privilege to help Adam, Andrew, and the team shine the spotlight on deserving projects.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>Speaking of The Changelog&hellip; did you know it recently <em>eschewed advertisers</em> and is now run on <a href="https://thechangelog.com/membership/">memberships</a> alone? That&rsquo;s pretty rad, if you ask me.</p>
<p>They also relaunched the podcast! Yours truly joined a live panel for the kickoff show last week, which you can listen to <a href="http://thechangelog.com/085/">here</a>. Oh, and if you aren&rsquo;t subscribed to the podcast, be sure to check <a href="http://thechangelog.com/podcast">this page</a> for more info!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I was publishing 1+ posts a week for awhile there, thanks to <a href="http://habitlist.com/">Habit list</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>If you want me to pimp your project on The Changelog, <a href="mailto:jerod.santo@gmail.com">email me</a> or hit me up <a href="https://twitter.com/jerodsanto">on Twitter</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Be Careful When You Create a Unified SSL Certificate for Nginx</title>
      <link>https://jerodsanto.net/2013/04/be-careful-when-you-create-a-unified-ssl-certificate-for-nginx/</link>
      <pubDate>Mon, 08 Apr 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/04/be-careful-when-you-create-a-unified-ssl-certificate-for-nginx/</guid>
      
      
      <description><![CDATA[<p>If one of the files doesn&rsquo;t have a newline at the end and you create the unified certificate (as instructed for <a href="http://www.startssl.com/?app=42">StartSSL</a> certs) like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">cat ssl.crt sub.class1.server.ca.pem ca.pem &gt; /etc/nginx/conf/ssl-unified.crt
</span></span></span></code></pre></div><p>Then you will end up with an error that looks like this:</p>
<blockquote>
<p>SSL_CTX_use_certificate_chain_file failed (SSL: error:0906D066:PEM routines:PEM_read_bio:bad end line error:140DC009:SSL routines:SSL_CTX_use_certificate_chain_file:PEM lib)</p>
</blockquote>
<p>Check out your <code>ssl-unified.crt</code> and you&rsquo;ll see that there is no newline between one or more of the certificates, like this:</p>]]></description>
      
      <content:encoded><![CDATA[<p>If one of the files doesn&rsquo;t have a newline at the end and you create the unified certificate (as instructed for <a href="http://www.startssl.com/?app=42">StartSSL</a> certs) like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">cat ssl.crt sub.class1.server.ca.pem ca.pem &gt; /etc/nginx/conf/ssl-unified.crt
</span></span></span></code></pre></div><p>Then you will end up with an error that looks like this:</p>
<blockquote>
<p>SSL_CTX_use_certificate_chain_file failed (SSL: error:0906D066:PEM routines:PEM_read_bio:bad end line error:140DC009:SSL routines:SSL_CTX_use_certificate_chain_file:PEM lib)</p>
</blockquote>
<p>Check out your <code>ssl-unified.crt</code> and you&rsquo;ll see that there is no newline between one or more of the certificates, like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">-----END CERTIFICATE----------BEGIN CERTIFICATE-----
</span></span></span></code></pre></div><p>Add it yourself and everything should be peachy:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">-----END CERTIFICATE-----
</span></span></span><span class="line"><span class="cl"><span class="go">-----BEGIN CERTIFICATE-----
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Name That Trend - Nose Dive Edition</title>
      <link>https://jerodsanto.net/2013/03/name-that-trend-nose-dive-edition/</link>
      <pubDate>Thu, 07 Mar 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/03/name-that-trend-nose-dive-edition/</guid>
      
      
      <description><![CDATA[<p>I recently stumbled across a spectacular nose dive in public perception that I thought would make a fun diversion. Can you figure out what dataset this trend represents?</p>
<p>I&rsquo;ll give you a few hints:</p>
<ol>
<li>I found it after watching a trailer for a movie set to release in June</li>
<li>I&rsquo;ll reiterate — it is related to public perception</li>
<li>The data is sourced from <a href="http://imdb.com">IMDB</a></li>
</ol>
<div class="chart">
    <img src="https://docs.google.com/spreadsheet/oimg?key=0Anc47_2p2GgFdGZfTjhYQ1VIdlZkSW1SMTk0ZTJKNkE&oid=1&zx=1ogdontcezw0" />
</div>
<p>Know what it is? Ready to find out?!</p>]]></description>
      
      <content:encoded><![CDATA[<p>I recently stumbled across a spectacular nose dive in public perception that I thought would make a fun diversion. Can you figure out what dataset this trend represents?</p>
<p>I&rsquo;ll give you a few hints:</p>
<ol>
<li>I found it after watching a trailer for a movie set to release in June</li>
<li>I&rsquo;ll reiterate — it is related to public perception</li>
<li>The data is sourced from <a href="http://imdb.com">IMDB</a></li>
</ol>
<div class="chart">
    <img src="https://docs.google.com/spreadsheet/oimg?key=0Anc47_2p2GgFdGZfTjhYQ1VIdlZkSW1SMTk0ZTJKNkE&oid=1&zx=1ogdontcezw0" />
</div>
<p>Know what it is? Ready to find out?!</p>

<details>
  <summary>Reveal Answer</summary>
  <p>And the trend is&hellip;</p>
<p>Average IMDB ratings of movies directed by <a href="http://www.imdbcom/name/nm0796117/">M. Night Shyamalan</a>!</p>
<p>Surprised? Probably not too much. He&rsquo;s been on quite the downward spiral since the glories days of <a href="http://www.imdb.com/title/tt0167404/">The Sixth Sense</a> (8.2) and <a href="http://www.imdb.com/title/tt0217869/">Unbreakable</a> (7.2).</p>
<p>The trailer that inspired this trend spotting was for the Shyamlan written/directed <a href="http://www.imdb.com/title/tt1815862/">After Earth</a>. I was surpised to find out that he was involved since the trailer doesn&rsquo;t mention his name once. Ouch.</p>

</details>

]]></content:encoded>
    </item>
    
    <item>
      <title>Easy &#39;Share-Nothing&#39; WordPress Blog Syndication on Rails</title>
      <link>https://jerodsanto.net/2013/03/easy-share-nothing-wordpress-blog-syndication-on-rails/</link>
      <pubDate>Fri, 01 Mar 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/03/easy-share-nothing-wordpress-blog-syndication-on-rails/</guid>
      
      
      <description><![CDATA[<p>The new <a href="http://omahype.com">Omahype</a>, which is a custom Rails-based CMS centered around Omaha-area events, has a separate blog that runs on WordPress.</p>
<p>This is not all that uncommon. WordPress is great for blogging. Developing (or extending) a custom Rails blogging solution is of little value unless you&rsquo;re doing some crazy custom stuff.</p>
<p>Despite the blog and main site being on different platforms, there was still the need to integrate the blog content into every page of the Rails site.</p>]]></description>
      
      <content:encoded><![CDATA[<p>The new <a href="http://omahype.com">Omahype</a>, which is a custom Rails-based CMS centered around Omaha-area events, has a separate blog that runs on WordPress.</p>
<p>This is not all that uncommon. WordPress is great for blogging. Developing (or extending) a custom Rails blogging solution is of little value unless you&rsquo;re doing some crazy custom stuff.</p>
<p>Despite the blog and main site being on different platforms, there was still the need to integrate the blog content into every page of the Rails site.</p>
<p><img src="http://jerodsanto.net/drop/omahype-blog.jpg" alt="&lsquo;From the blog&rsquo; is in the footer of every page"></p>
<p>There are a handful of ways to skin this cat, but most of them require WordPress and Rails to have things in common: share a database, share a server, and/or share a domain.</p>
<p>Share-Something.</p>
<p>Omahype is hosted on a <a href="http://heroku.com">&lsquo;Share-Nothing&rsquo; platfom</a> and the WordPress blog was already up and running on Media Temple, so we needed an easy &lsquo;Share-Nothing&rsquo; solution.</p>
<p>Here&rsquo;s how we do it:</p>
<h2 id="the-syndication">The Syndication</h2>
<p>This part is the easiest. WordPress provides syndicated content out of the box via <a href="http://en.wikipedia.org/wiki/RSS">RSS</a> which means we don&rsquo;t have to resort to page scraping or the like.</p>
<p>Omahype&rsquo;s standard RSS feed is located <a href="http://blog.omahype.com/feed/">here</a>, but we had some simplifications and customizations of the feed&rsquo;s content in mind, so we created a special one just for Rails to consume.</p>
<p><a href="http://blog.omahype.com/recent/"><img src="http://jerodsanto.net/drop/omahype-rss-pic.jpg" alt=""></a></p>
<p>This <a href="http://blog.omahype.com/recent/">special feed</a> provides exactly what we need and nothing else: The 4 most recent posts in a format that is easier to repurpose than the main feed.</p>
<h2 id="pulling-it-in">Pulling It In</h2>
<p>The syndicated blog content is a value-add to the core features of Omahype&rsquo;s event calendars so we definitely don&rsquo;t want it to slow down page loads.</p>
<p>Because of this, we decided to pull the content in outside of Rails&rsquo; main request / response lifecycle. This is done via jQuery&rsquo;s <code>$.get</code> and <code>$.parseXML</code><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>Here&rsquo;s our CoffeeScript source which generates the requisite JavaScript to pull in the content and add it to the DOM:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coffeescript" data-lang="coffeescript"><span class="line"><span class="cl"><span class="nv">loadBlogPosts: </span><span class="nf">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">$</span><span class="p">.</span><span class="nx">get</span> <span class="s">&#34;http://blog.omahype.com/recent/&#34;</span><span class="p">,</span> <span class="nf">(data) -&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">doc = </span><span class="nx">$</span><span class="p">.</span><span class="nx">parseXML</span> <span class="nx">data</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nx">$</span><span class="p">(</span><span class="nx">doc</span><span class="p">).</span><span class="nx">find</span><span class="p">(</span><span class="s">&#34;item&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="nx">each</span> <span class="nf">-&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nv">$item = </span><span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="nv">title = </span><span class="nx">$item</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="s">&#34;title&#34;</span><span class="p">).</span><span class="nx">text</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="nv">link = </span><span class="nx">$item</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="s">&#34;link&#34;</span><span class="p">).</span><span class="nx">text</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="nv">image = </span><span class="nx">$item</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="s">&#34;image&#34;</span><span class="p">).</span><span class="nx">text</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="nv">description = </span><span class="nx">$item</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="s">&#34;description&#34;</span><span class="p">).</span><span class="nx">text</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="nv">categories = </span><span class="nx">$item</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="s">&#34;category&#34;</span><span class="p">).</span><span class="nx">map</span> <span class="nf">-&gt;</span> <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">text</span><span class="p">().</span><span class="nx">toLowerCase</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="nv">classes = </span><span class="nx">$</span><span class="p">.</span><span class="nx">makeArray</span><span class="p">(</span><span class="nx">categories</span><span class="p">).</span><span class="nx">join</span> <span class="s">&#34; &#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="nx">image</span><span class="p">.</span><span class="nx">length</span>
</span></span><span class="line"><span class="cl">                    <span class="nx">classes</span> <span class="o">+=</span> <span class="s">&#34; hasImage&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="nv">description = </span><span class="s">&#34;&lt;img src=&#39;</span><span class="si">#{</span><span class="nx">image</span><span class="si">}</span><span class="s">&#39;&gt;&lt;/img&gt; </span><span class="si">#{</span><span class="nx">description</span><span class="si">}</span><span class="s">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="nv">html = </span><span class="s">&#34;&lt;a href=&#39;</span><span class="si">#{</span><span class="nx">link</span><span class="si">}</span><span class="s">&#39; title=&#39;Read Article&#39;&gt;&#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                    <span class="s">&#34;&lt;article class=&#39;</span><span class="si">#{</span><span class="nx">classes</span><span class="si">}</span><span class="s">&#39;&gt;&lt;h1&gt;</span><span class="si">#{</span><span class="nx">title</span><span class="si">}</span><span class="s">&lt;/h1&gt;&#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                    <span class="s">&#34;&lt;p&gt;</span><span class="si">#{</span><span class="nx">description</span><span class="si">}</span><span class="s">&lt;/p&gt;&lt;/article&gt;&lt;/a&gt;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="nx">$</span><span class="p">(</span><span class="s">&#34;#blog&#34;</span><span class="p">).</span><span class="nx">append</span> <span class="nx">html</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="nx">promise</span><span class="p">().</span><span class="nx">done</span> <span class="nf">-&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nx">Omahype</span><span class="p">.</span><span class="nx">equalHeight</span> <span class="nx">$</span><span class="p">(</span><span class="s">&#34;section#blog article&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>Much of that is implementation details specific to how we want the content formatted, but the gist of it is you:</p>
<ol>
<li>make the <code>GET</code> request for the content,</li>
<li>parse the raw data into <code>doc</code></li>
<li>find the <code>item</code> elements in the <code>doc</code></li>
<li>loop over them, extracting their contents and putting them in the page<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></li>
</ol>
<p>If you&rsquo;ve been reading closely, and you&rsquo;ve noticed that our &lsquo;Share-Nothing&rsquo; scenario means that Rails and WordPress don&rsquo;t event share a domain, then you&rsquo;ll notice a problem with the code above.</p>
<p>The <code>GET</code> request will fail due to JavaScript&rsquo;s <a href="http://en.wikipedia.org/wiki/Same_origin_policy">Same origin policy</a>. Bummer.</p>
<p>There are two ways to get around this problem.</p>
<ol>
<li>We could configure <a href="http://en.wikipedia.org/wiki/Cross-origin_resource_sharing">CORS</a> on the WordPress server to allow requests from omahype.com</li>
<li>We could introduce a proxy on the Rails side and serve the feed from there</li>
</ol>
<p>I chose the latter option because it brings with it an easy win: caching.</p>
<h2 id="the-middle-man">The Middle Man</h2>
<p>We need the <code>$.get</code> to hit a URL on the Rails side, so we add a <code>ProxyController</code> and route to it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/routes.rb</span>
</span></span><span class="line"><span class="cl"><span class="n">get</span> <span class="s2">&#34;/proxy&#34;</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s2">&#34;proxy#index&#34;</span><span class="p">,</span> <span class="ss">as</span><span class="p">:</span> <span class="ss">:proxy</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># app/controllers/proxy_controller.rb</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;open-uri&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ProxyController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">    <span class="n">feed</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s2">&#34;http://blog.omahype.com/recent/&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">read</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span> <span class="ss">text</span><span class="p">:</span> <span class="n">feed</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>With this in hand, we just change the <code>$.get</code> to point to our middle man:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coffeescript" data-lang="coffeescript"><span class="line"><span class="cl">    <span class="nv">loadBlogPosts: </span><span class="nf">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">$</span><span class="p">.</span><span class="nx">get</span> <span class="s">&#34;/proxy&#34;</span><span class="p">,</span> <span class="nf">(data) -&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># ... snip ...
</span></span></span></code></pre></div><p>Bada boom bada bing. The recent posts now load into our footer.</p>
<p>However, every single page load will now make a subsequent request to the blog&rsquo;s RSS feed. That seems excessive since we&rsquo;re loading content that changes maybe a few times a day, maybe less.</p>
<p>Remember above when I said a proxy gives us an easy win? Here it is.</p>
<p>We can use Rails&rsquo; built-in cache to only fetch new content from WordPress at a set interval.</p>
<p>To accomplish this, we change the <code>ProxyController</code>&rsquo;s index action to look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">  <span class="n">feed</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">cache</span><span class="o">.</span><span class="n">fetch</span> <span class="s2">&#34;blog-posts&#34;</span><span class="p">,</span> <span class="ss">expires_in</span><span class="p">:</span> <span class="mi">5</span><span class="o">.</span><span class="n">minutes</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nb">open</span><span class="p">(</span><span class="s2">&#34;http://blog.omahype.com/recent/&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">read</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">render</span> <span class="ss">text</span><span class="p">:</span> <span class="n">feed</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Now it&rsquo;s super fast when it hits the cache and doesn&rsquo;t have to hit WordPress all the time!</p>
<p>And there you have it. Easy &lsquo;Share-Nothing&rsquo; WordPress blog syndication on Rails.</p>
<p>Wow, this post&rsquo;s title is extremely accurate ;)</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Did you know jQuery has had built-in XML parsing since version 1.5? Seems like it should be a plugin to me (whereas $.parseJSON makes complete sense in core), but nevertheless it is exactly what we need for parsing RSS.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>This part is specifically messy and was <em>almost</em> means for a templating solution, but I decided it wasn&rsquo;t worth it for this  single use.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>The Curious Case of ActiveRecord Matching Yesterday&#39;s Events Even Though I Told it Not To</title>
      <link>https://jerodsanto.net/2013/02/the-curious-case-of-activerecord-matching-yesterdays-events-even-though-i-told-it-not-to/</link>
      <pubDate>Tue, 19 Feb 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/02/the-curious-case-of-activerecord-matching-yesterdays-events-even-though-i-told-it-not-to/</guid>
      
      
      <description><![CDATA[<p>We uncovered a Strange Thing when working on <a href="http://omahype.com">Omahype&rsquo;s</a> event listings.</p>
<div class="notice">Omahype is a curated events calendar helping to foster art and culture in Omaha and the surrounding areas. If you&rsquo;re in the area, you should definitely check it out!</div>
<h2 id="the-strange-thing">The Strange Thing</h2>
<p>Omahype&rsquo;s <code>Event</code>s, which are ActiveRecord models, have a <code>start_time</code> field which we use to show people when the event starts.</p>
<p>We also use this field to distinguish past events from upcoming ones. The site&rsquo;s <a href="http://omahype.com/category/music">category views</a> call <code>Event.upcoming</code> to get only events from today and into the future. It looks something like this:</p>]]></description>
      
      <content:encoded><![CDATA[<p>We uncovered a Strange Thing when working on <a href="http://omahype.com">Omahype&rsquo;s</a> event listings.</p>
<div class="notice">Omahype is a curated events calendar helping to foster art and culture in Omaha and the surrounding areas. If you&rsquo;re in the area, you should definitely check it out!</div>
<h2 id="the-strange-thing">The Strange Thing</h2>
<p>Omahype&rsquo;s <code>Event</code>s, which are ActiveRecord models, have a <code>start_time</code> field which we use to show people when the event starts.</p>
<p>We also use this field to distinguish past events from upcoming ones. The site&rsquo;s <a href="http://omahype.com/category/music">category views</a> call <code>Event.upcoming</code> to get only events from today and into the future. It looks something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">upcoming</span>
</span></span><span class="line"><span class="cl">  <span class="n">where</span><span class="p">(</span><span class="s2">&#34;start_time &gt;= ?&#34;</span><span class="p">,</span> <span class="no">Date</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">order</span><span class="p">(</span><span class="s2">&#34;start_time asc&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>That looked pretty good to me, but once the site was in production we noticed a problem.</p>
<p><img src="http://jerodsanto.net/drop/test-in-production.png" alt=""></p>
<p>The category views were displaying last night&rsquo;s events in addition to the ones we wanted. Troublesome. I added a test case<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> which reproduces the behavior:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">describe</span> <span class="no">Event</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">describe</span> <span class="s2">&#34;.upcoming&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">it</span> <span class="s2">&#34;doesn&#39;t match events from last night&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="n">last_night</span> <span class="o">=</span> <span class="no">Time</span><span class="o">.</span><span class="n">now</span><span class="o">.</span><span class="n">beginning_of_day</span> <span class="o">-</span> <span class="mi">2</span><span class="o">.</span><span class="n">hours</span>
</span></span><span class="line"><span class="cl">        <span class="n">create</span> <span class="ss">:event</span><span class="p">,</span> <span class="ss">start_time</span><span class="p">:</span> <span class="n">last_night</span>
</span></span><span class="line"><span class="cl">        <span class="n">expect</span><span class="p">(</span><span class="no">Event</span><span class="o">.</span><span class="n">upcoming</span><span class="o">.</span><span class="n">count</span><span class="p">)</span><span class="o">.</span><span class="n">to</span> <span class="n">eq</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>That test failed, but why is that?</p>
<p>The query says that the start time must be greater than or equal to today&rsquo;s date and that&rsquo;s surely not the case with last night&rsquo;s events. Or is it?</p>
<h2 id="the-reason">The Reason</h2>
<p>ActiveRecord stores all timestamps in the database as <a href="http://en.wikipedia.org/wiki/Coordinated_Universal_Time">UTC</a> and converts back and forth between the applicable time zone in Ruby land before executing queries.</p>
<p>Usually this works just fine with timestamp comparisons because the offsets are adjusted before the database makes the comparison, but it doesn&rsquo;t work so well when you are comparing against just a date.</p>
<p>Omahype only tracks events in Omaha so we have Rails configured to use US Central Time (CST):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/application.rb</span>
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Omahype</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Application</span> <span class="o">&lt;</span> <span class="no">Rails</span><span class="o">::</span><span class="no">Application</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</span><span class="o">.</span><span class="n">time_zone</span> <span class="o">=</span> <span class="s2">&#34;Central Time (US &amp; Canada)&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>The problem is that CST is UTC-6 (or -5 during daylight savings), so any event created with a <code>start_time</code> of 6pm or later will be stored in the database as bright and early on the following morning.</p>
<p>To be clear, when we do this in Ruby:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Event</span><span class="o">.</span><span class="n">create</span> <span class="ss">start_time</span><span class="p">:</span> <span class="no">Time</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="s2">&#34;2013-02-19 20:00:00&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>In the database (Postgres, in our case), it will be stored as a <a href="http://www.postgresql.org/docs/9.2/static/datatype-datetime.html">timestamp without time zone</a> of &ldquo;2013-02-20 02:00:00&rdquo;.</p>
<p>See the problem?</p>
<p>In UTC that event does start today even though in CST it starts last night. ActiveRecord can adjust the right-hand side of the comparison to UTC before executing the query, but it can&rsquo;t do anything about the left-hand side.</p>
<p>Converting the right-hand side to UTC doesn&rsquo;t help us because the date remain the same.</p>
<p>Bummer.</p>
<h2 id="the-not-so-great-solution">The (not so great) Solution</h2>
<p>We decided to convert the left-hand side of our comparison from UTC to CST.</p>
<p>In Postgres you can use <code>AT TIME ZONE</code> to explicitly set the time zone which applies to a timestamp column. With this in hand, I adjusted the method to look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">upcoming</span>
</span></span><span class="line"><span class="cl">  <span class="n">where</span><span class="p">(</span><span class="s2">&#34;start_time at time zone &#39;CST&#39; &gt;= ?&#34;</span><span class="p">,</span> <span class="no">Date</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">order</span><span class="p">(</span><span class="s2">&#34;start_time asc&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Unfortunately, my test was still red.</p>
<p>This confused me to no end, but it turns out that <code>AT TIME ZONE</code> only converts the time value when it is applied to a <code>timestamp with time zone</code> field not a <code>timestamp without time zone</code> field, which <code>start_time</code> is.</p>
<p>Here&rsquo;s <a href="http://www.postgresql.org/docs/9.2/static/functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT">their chart</a> of how it works for different field types:</p>
<p><a href="http://www.postgresql.org/docs/9.2/static/functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT"><img src="http://jerodsanto.net/drop/pg-at-time-zone-chart.jpg" alt="Click to see actual docs if it is too squished for your eyes"></a></p>
<p>So I had to first convert <code>start_time</code> to a <code>timestamp with time zone</code> of UTC and then convert it from UTC to CST. Like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">upcoming</span>
</span></span><span class="line"><span class="cl">  <span class="n">where</span><span class="p">(</span><span class="s2">&#34;start_time at time zone &#39;UTC&#39; at time zone &#39;CST&#39; &gt;= ?&#34;</span><span class="p">,</span> <span class="no">Date</span><span class="o">.</span><span class="n">today</span><span class="p">)</span><span class="o">.</span><span class="n">order</span><span class="p">(</span><span class="s2">&#34;start_time asc&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>That is pretty ugly if you ask me, but the test was green.</p>
<p>Finally.</p>
<h2 id="the-alternatives-">The Alternatives (?)</h2>
<p>There are a bevy of problems that I have with this solution.</p>
<ol>
<li>
<p>I hardcoded &lsquo;CST&rsquo; into the query, which is okay for this app but would not work if we were switching time zones based on user location or any other criteria.</p>
</li>
<li>
<p>The two calls to <code>at time zone</code> slow down the query by a smidgeon. Not much, but enough to not like it.</p>
</li>
<li>
<p>Other finder methods will have to also use this workaround or they, too, will return bad results.</p>
</li>
</ol>
<p>I&rsquo;d love to hear of others way to tackle this problem. One thing I thought of would be to store a separate <code>start_date</code> field alongside the <code>start_time</code>. That would make all comparisons apples-to-apples (dates-to-dates), but that is inelegant because it requires us to manage the two fields in the code and ensure they aren&rsquo;t different.</p>
<p>Perhaps there is a smarter way to perform the same type of query? Or maybe I&rsquo;m missing some obvious time zone conversion thing in ActiveRecord that obviates the need for db-side conversions?</p>
<p>Please let me know if you have any insight on this curious case!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I would have caught it sooner, but my original tests didn&rsquo;t cover the edges well enough. They used events from 10 days ago instead of last night.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>CLOC Counts Lines of Code</title>
      <link>https://jerodsanto.net/2013/02/cloc-counts-lines-of-code/</link>
      <pubDate>Tue, 12 Feb 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/02/cloc-counts-lines-of-code/</guid>
      
      
      <description><![CDATA[<p>If you ever find yourself in a foreign code base and want to get a quick overview of exactly how much code is in it, <a href="http://cloc.sourceforge.net">CLOC</a> is just the tool for the job.</p>
<p>It&rsquo;s easy to install on OS X:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> brew install cloc
</span></span></code></pre></div><p>And on Debian-based Linuxes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> aptitude install cloc
</span></span></code></pre></div><p>To use it, simply <code>cd</code> into the root directory and run:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> cloc .
</span></span></code></pre></div><p>The default output will show you a breakdown by language. Here&rsquo;s an example of what it&rsquo;ll look like:</p>]]></description>
      
      <content:encoded><![CDATA[<p>If you ever find yourself in a foreign code base and want to get a quick overview of exactly how much code is in it, <a href="http://cloc.sourceforge.net">CLOC</a> is just the tool for the job.</p>
<p>It&rsquo;s easy to install on OS X:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> brew install cloc
</span></span></code></pre></div><p>And on Debian-based Linuxes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> aptitude install cloc
</span></span></code></pre></div><p>To use it, simply <code>cd</code> into the root directory and run:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> cloc .
</span></span></code></pre></div><p>The default output will show you a breakdown by language. Here&rsquo;s an example of what it&rsquo;ll look like:</p>
<p><a href="http://cloc.sourceforge.net"><img src="http://jerodsanto.net/drop/cloc-redacted.png" alt="php much?"></a></p>
<p>You can, of course, customize the poop out of it as well.</p>
<p><code>cloc --help</code> to nerd out.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>A Casual Stroll Through Discourse&#39;s Source Code</title>
      <link>https://jerodsanto.net/2013/02/a-casual-stroll-through-discourses-source-code/</link>
      <pubDate>Mon, 11 Feb 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/02/a-casual-stroll-through-discourses-source-code/</guid>
      
      
      <description><![CDATA[<p>The public release (and open sourcing) of <a href="http://www.discourse.org/">Discourse</a> was the big news in the open source world last week.</p>
<p>Discourse interested me mostly because it is a large-scale, production-ready Rails application which <a href="http://eviltrout.com/2013/02/10/why-discourse-uses-emberjs.html">uses Ember.js</a> for client-side MVC.</p>
<p>Any time I see an open source app which has technically intriguing bits, I take some time to clone it and give it a quick read-through. Let&rsquo;s do that!</p>
<h2 id="the-readme">The README</h2>
<p>Let&rsquo;s start in the <a href="https://github.com/discourse/discourse/blob/3047d8afa3ae78b6e1722530577484921ce83b7f/README.md">README</a>, which is akin to the summary on the back of a book. Here we find out a few good signs. First, they have a developer <a href="https://github.com/discourse/discourse/blob/master/DEVELOPMENT.md">install guide</a>, which means they&rsquo;re serious about attracting contributors. Next we learn the grand vision of the project, how to contribute, and the major technologies that the project uses. Very nicely done.</p>]]></description>
      
      <content:encoded><![CDATA[<p>The public release (and open sourcing) of <a href="http://www.discourse.org/">Discourse</a> was the big news in the open source world last week.</p>
<p>Discourse interested me mostly because it is a large-scale, production-ready Rails application which <a href="http://eviltrout.com/2013/02/10/why-discourse-uses-emberjs.html">uses Ember.js</a> for client-side MVC.</p>
<p>Any time I see an open source app which has technically intriguing bits, I take some time to clone it and give it a quick read-through. Let&rsquo;s do that!</p>
<h2 id="the-readme">The README</h2>
<p>Let&rsquo;s start in the <a href="https://github.com/discourse/discourse/blob/3047d8afa3ae78b6e1722530577484921ce83b7f/README.md">README</a>, which is akin to the summary on the back of a book. Here we find out a few good signs. First, they have a developer <a href="https://github.com/discourse/discourse/blob/master/DEVELOPMENT.md">install guide</a>, which means they&rsquo;re serious about attracting contributors. Next we learn the grand vision of the project, how to contribute, and the major technologies that the project uses. Very nicely done.</p>
<p>With that info in hand, let&rsquo;s poke around the actual source code and see what we see.</p>
<h2 id="the-gemfile">The Gemfile</h2>
<p>For Ruby apps, I usually start in <a href="https://github.com/discourse/discourse/blob/3047d8afa3ae78b6e1722530577484921ce83b7f/Gemfile">the Gemfile</a> to get a quick overview of the 3rd party libraries being used. Discourse has a <em>big</em> Gemfile, but nothing out of the ordinary given what we read in the README.</p>
<div class="notice">Some great gems in here: <a href="https://github.com/rails-api/active_model_serializers">active_model_serializers</a>, <a href="http://nokogiri.org">nokogiri</a>, and <a href="https://github.com/charliesome/better_errors">better_errors</a></div>
<p>Notably, there&rsquo;s a lot of Redis-related gems being used, RSpec for Ruby tests, looks like Jasmine for JavaScript tests, and Sinatra for some reason, even though it is not being <code>require</code>d.</p>
<h2 id="the-test-suite">The Test Suite</h2>
<p>After perusing the Gemfile, I look for tests next. We already know Discourse has them because of what we&rsquo;ve already seen, so we can go straight to the <a href="https://github.com/discourse/discourse/tree/3047d8afa3ae78b6e1722530577484921ce83b7f/spec">spec/</a> directory and have a look around.</p>
<p>My first impression is that Discourse is very thoroughly tested. There are spec for models, views, controllers, mailers, fabricators, components and javascripts.</p>
<p>I&rsquo;m most curious about fabricators, because I don&rsquo;t know what they are, and javascripts, because I&rsquo;d like to see a well-tested Ember app.</p>
<p>Peeping in to the <a href="https://github.com/discourse/discourse/blob/3047d8afa3ae78b6e1722530577484921ce83b7f/spec/fabricators/post_fabricator.rb">PostFabricator</a>, it doesn&rsquo;t appear to be a spec file at all. Instead, there are many <code>Fabricator</code> blocks, which call methods such as <code>raw</code>, <code>cooked</code>, <code>user</code>, <code>topic</code>, and <code>created_at</code>. My guess is that fabricators aren&rsquo;t a piece of the production system at all. They&rsquo;re probably some kind of factory for use in specs.</p>
<p>So, I move on to <a href="https://github.com/discourse/discourse/blob/3047d8afa3ae78b6e1722530577484921ce83b7f/spec/javascripts">specs/javascripts</a>. My first thought in this directory is that there aren&rsquo;t as many specs as I hoped. With how much Discourse relies on client-side MVC, you&rsquo;d think those components would be more thoroughly spec&rsquo;d.</p>
<p>Oh well, you can&rsquo;t have it all, I guess.</p>
<h2 id="a-model">A Model</h2>
<p>The M in MVC is supposed to be where your application&rsquo;s business logic resides, so it&rsquo;s a logical next place to have a gander.</p>
<p>At this point I like to bring up the spec and implementation files side by side as I read. Let&rsquo;s just pick a single model and see what is up. Discourse is all about posting comments, so the <a href="https://github.com/discourse/discourse/blob/3047d8afa3ae78b6e1722530577484921ce83b7f/app/models/post.rb">Post</a> models seems like a good one to look at.</p>
<p>Opening <a href="https://github.com/discourse/discourse/blob/3047d8afa3ae78b6e1722530577484921ce83b7f/spec/models/post_spec.rb">specs/models/post_spec</a> confirms my assumption about the fabricators, as I see:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">let</span><span class="p">(</span><span class="ss">:topic</span><span class="p">)</span> <span class="p">{</span> <span class="no">Fabricate</span><span class="p">(</span><span class="ss">:topic</span><span class="p">)</span> <span class="p">}</span>
</span></span></code></pre></div><p>near the top of the file. Cool.</p>
<p>Opening <a href="https://github.com/discourse/discourse/blob/3047d8afa3ae78b6e1722530577484921ce83b7f/app/models/post.rb">app/models/post.rb</a> confirms my choice of Post as core to the system. This file is almost 500 lines long.</p>
<p>There are a couple things that worry me here.</p>
<p>First, they appear to be <a href="https://github.com/discourse/discourse/blob/3047d8afa3ae78b6e1722530577484921ce83b7f/app/models/post.rb#L329">sending email</a> from inside the model. I&rsquo;ve been guilty of this myself, but these days I try to keep email logic outside ActiveRecord descendants. I prefer it in a controller or separate domain class.</p>
<p>Second, and perhaps more dangerous to new devs, there are a bevy of callbacks being triggered, but they are not grouped together in the file. There&rsquo;s a series of them that starts <a href="https://github.com/discourse/discourse/blob/3047d8afa3ae78b6e1722530577484921ce83b7f/app/models/post.rb#L51">on line 51</a> and another series all the way down <a href="https://github.com/discourse/discourse/blob/3047d8afa3ae78b6e1722530577484921ce83b7f/app/models/post.rb#L302">on line 302</a>.</p>
<div class="notice">Grouping logically-related pieces of code together is one aspect of keeping code readable</div>
<p>Other than those two gripes, Post appears to be a decently factored, if not a little chubby, model.</p>
<p>Let&rsquo;s move on to something more interesting.</p>
<h2 id="ember-stuff">Ember Stuff</h2>
<p>The entire reason that I wanted to read Discourse&rsquo;s source code is to see some production Ember stuff. Let&rsquo;s quit dawdling and hop into the good stuff.</p>
<p>Rails 3+ puts all the Coffee/JavaScript in <a href="https://github.com/discourse/discourse/tree/3047d8afa3ae78b6e1722530577484921ce83b7f/app/assets/javascripts">app/assets/javascripts</a>, so it&rsquo;s no surprise to find a <a href="https://github.com/discourse/discourse/tree/3047d8afa3ae78b6e1722530577484921ce83b7f/app/assets/javascripts/discourse">discourse</a> subdirectory in there with what appears to be an Ember app.</p>
<p>I decide to jump in to <a href="ttps://github.com/discourse/discourse/tree/3047d8afa3ae78b6e1722530577484921ce83b7f/app/assets/javascripts/discourse/models/post.js.coffee.erb">post.js.coffee.erb</a> first, because I already am familiar with the server-side Post model.</p>
<p>The first thing I notice is just in the filename itself. They&rsquo;re using CoffeeScript, which is cool, but they&rsquo;re also relying on ERB in their CoffeeScript files.</p>
<p>This sucks because many text editors will apply embedded Ruby styling/rules to the file even though it is mostly CoffeeScript with just a few Ruby snippets in there.</p>
<p>I try to avoid this if at all possible, but sometimes it&rsquo;s not easy. I want to see why they need ERB, so I open the file and do a quick search for &lsquo;&lt;%&rsquo;. This yields just 2 hits, on <a href="https://github.com/discourse/discourse/blob/3047d8afa3ae78b6e1722530577484921ce83b7f/app/assets/javascripts/discourse/models/post.js.coffee.erb#L178-L179">line 178 and 179</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coffeescript" data-lang="coffeescript"><span class="line"><span class="cl"><span class="nv">REGULAR_TYPE: </span><span class="o">&lt;%=</span> <span class="nx">Post</span><span class="o">::</span><span class="nx">REGULAR</span> <span class="o">%&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nv">MODERATOR_ACTION_TYPE: </span><span class="o">&lt;%=</span> <span class="nx">Post</span><span class="o">::</span><span class="nx">MODERATOR_ACTION</span> <span class="o">%&gt;</span>
</span></span></code></pre></div><p>I think they&rsquo;re trying keep things DRY by reusing server-side constants to define JS globals.</p>
<p>I&rsquo;ve avoided ERB in this scenario by embedding the variables into the DOM somewhere (such as data- attributes on &lt;body&gt;) and then grabbing them out in JS.</p>
<p>I don&rsquo;t know if Ember lets you do this, but it save the hassle of pre-processing CoffeeScript with ERB.</p>
<p>Speaking of DRY, I&rsquo;m curious if there is much duplication between the Ruby Post model and the CoffeeScript Post model.</p>
<p>Reusing models on the client and server is an oft-cited panacea provided by server-side JavaScript, and Rails doesn&rsquo;t provide such a convenience.</p>
<p>Does it matter in the case of Discourse&rsquo;s Post model? You can judge for yourself, but in my small time reading both files I would say no, not at all.</p>
<h2 id="way-more">Way More</h2>
<p>There&rsquo;s a lot more to this app, and I definitely suggest giving it a read, but I will stop here because this post is already too long.</p>
<p>Reading large, production-ready open source projects is a great way to learn new techniques, see how people you admire solve complex problems, and even find places <a href="http://grantammons.me/2013/02/09/making-some-discourse-code-a-little-better/">where you can help</a>.</p>
<p>If you&rsquo;ve read a lot of other people&rsquo;s code, you are probably nodding your head in agreement.</p>
<p>If not, I highly recommend it, and hopefully this post will give you a bit of a process you can follow to dive in with confidence and quickly understand what is going on.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Rate Limit POST Requests with Rack::Throttle</title>
      <link>https://jerodsanto.net/2013/02/rate-limit-post-requests-with-rackthrottle/</link>
      <pubDate>Sun, 03 Feb 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/02/rate-limit-post-requests-with-rackthrottle/</guid>
      
      
      <description><![CDATA[<p><a href="https://github.com/datagraph/rack-throttle">Rack::Throttle</a> is an awesome piece of Rack middleware for rate limiting your Rails / Sinatra / Rack app&rsquo;s clients.</p>
<p>It ships with three rate limiting strategies out of the box: <a href="https://github.com/datagraph/rack-throttle/blob/master/lib/rack/throttle/interval.rb">Interval</a>, <a href="https://github.com/datagraph/rack-throttle/blob/master/lib/rack/throttle/hourly.rb">Hourly</a>, and <a href="https://github.com/datagraph/rack-throttle/blob/master/lib/rack/throttle/daily.rb">Daily</a>.</p>
<p>These strategies will serve 95% of people&rsquo;s needs, but you can also write your own strategy from scratch by subclassing the <a href="https://github.com/datagraph/rack-throttle/blob/master/lib/rack/throttle/limiter.rb">Limiter</a> and implementing an <code>allowed?</code> method.</p>
<p>In my experience, writing a strategy from scratch has not been necessary, but I have needed to extend the provided strategies a bit.</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="https://github.com/datagraph/rack-throttle">Rack::Throttle</a> is an awesome piece of Rack middleware for rate limiting your Rails / Sinatra / Rack app&rsquo;s clients.</p>
<p>It ships with three rate limiting strategies out of the box: <a href="https://github.com/datagraph/rack-throttle/blob/master/lib/rack/throttle/interval.rb">Interval</a>, <a href="https://github.com/datagraph/rack-throttle/blob/master/lib/rack/throttle/hourly.rb">Hourly</a>, and <a href="https://github.com/datagraph/rack-throttle/blob/master/lib/rack/throttle/daily.rb">Daily</a>.</p>
<p>These strategies will serve 95% of people&rsquo;s needs, but you can also write your own strategy from scratch by subclassing the <a href="https://github.com/datagraph/rack-throttle/blob/master/lib/rack/throttle/limiter.rb">Limiter</a> and implementing an <code>allowed?</code> method.</p>
<p>In my experience, writing a strategy from scratch has not been necessary, but I have needed to extend the provided strategies a bit.</p>
<p>For instance, I have an app where we want to limit writes per hour, but we don&rsquo;t care about reads. In this case, I just subclassed the <a href="https://github.com/datagraph/rack-throttle/blob/master/lib/rack/throttle/hourly.rb">Hourly</a> strategy and made mine allow anything that isn&rsquo;t a POST request.</p>
<p>My subclass is crazy simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Rack</span>
</span></span><span class="line"><span class="cl">  <span class="k">module</span> <span class="nn">Throttle</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nc">HourlyPosts</span> <span class="o">&lt;</span> <span class="no">Hourly</span>
</span></span><span class="line"><span class="cl">      <span class="k">def</span> <span class="nf">allowed?</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kp">true</span> <span class="k">unless</span> <span class="n">request</span><span class="o">.</span><span class="n">request_method</span> <span class="o">==</span> <span class="s2">&#34;POST&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">super</span> <span class="n">request</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Then I invoke it just like I&rsquo;d invoke the default hourly strategy:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">use</span> <span class="no">Rack</span><span class="o">::</span><span class="no">Throttle</span><span class="o">::</span><span class="no">HourlyPosts</span><span class="p">,</span> <span class="ss">max</span><span class="p">:</span> <span class="mi">10</span>
</span></span></code></pre></div><p>My app doesn&rsquo;t allow updating (PUT) or destroying (DELETE) records, but it&rsquo;d be trivial to modify the subclass to account for them as well.</p>
<p>I love libraries like Rack::Throttle with small, simple interfaces that are easily extended. It also provides a plethora of options for storage, such as in-memory, gdbm, Memcache, or Redis.</p>
<p>Highly recommended!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Fix Rails-API Oauth2 CSRF Errors On Rails 3.2.9&#43;</title>
      <link>https://jerodsanto.net/2013/02/fix-rails-api-oauth2-csrf-errors-on-rails-3.2.9/</link>
      <pubDate>Fri, 01 Feb 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/02/fix-rails-api-oauth2-csrf-errors-on-rails-3.2.9/</guid>
      
      
      <description><![CDATA[<p>If you <em>happen</em> to find yourself using <a href="https://github.com/rails-api/rails-api">Rails-API</a>, as I am. And you <em>happen</em> to be interacting with sundry <a href="https://github.com/intridea/omniauth-oauth2">Oauth2</a> providers via <a href="https://github.com/intridea/omniauth">Omniauth</a>, as I am. And you <em>happen</em> to be getting CSRF errors in your callback phase after upgrading Rails to anything higher than 3.2.8, as I was&hellip;</p>
<p>Boy do I have the post for you!</p>
<p>Rails-API excludes the session and cookie middleware by default<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, but Omniauth needs them<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. In Rails 3.2.8 and below, you can get both working by picking a session store middleware and <code>use</code>ing it in <code>config/application.rb</code>:</p>]]></description>
      
      <content:encoded><![CDATA[<p>If you <em>happen</em> to find yourself using <a href="https://github.com/rails-api/rails-api">Rails-API</a>, as I am. And you <em>happen</em> to be interacting with sundry <a href="https://github.com/intridea/omniauth-oauth2">Oauth2</a> providers via <a href="https://github.com/intridea/omniauth">Omniauth</a>, as I am. And you <em>happen</em> to be getting CSRF errors in your callback phase after upgrading Rails to anything higher than 3.2.8, as I was&hellip;</p>
<p>Boy do I have the post for you!</p>
<p>Rails-API excludes the session and cookie middleware by default<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, but Omniauth needs them<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. In Rails 3.2.8 and below, you can get both working by picking a session store middleware and <code>use</code>ing it in <code>config/application.rb</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">MyApp</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Application</span> <span class="o">&lt;</span> <span class="no">Rails</span><span class="o">::</span><span class="no">Application</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># ... snip ... #</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</span><span class="o">.</span><span class="n">middleware</span><span class="o">.</span><span class="n">use</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">SessionStore</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>In Rails 3.2.9+, however, this is insufficient. A change to how cookies are set in Rails 3.2.9 means you have to explicitly enable the <code>ActionDispatch::Cookies</code> middleware as well in order to use session storage.</p>
<p>The reason it works in 3.2.8 is because that version relies on Rack&rsquo;s cookie setting code (which is always available), but in 3.2.9 it switched to using ActionDispatch&rsquo;s cookie setting code (which is not there unless using the middleware) instead.</p>
<p>So, you need to make your config look like this if you&rsquo;re using 3.2.9+:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">MyApp</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Application</span> <span class="o">&lt;</span> <span class="no">Rails</span><span class="o">::</span><span class="no">Application</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># ... snip ... #</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</span><span class="o">.</span><span class="n">middleware</span><span class="o">.</span><span class="n">use</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">Cookies</span> <span class="c1"># order matters!</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</span><span class="o">.</span><span class="n">middleware</span><span class="o">.</span><span class="n">use</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">SessionStore</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Oh, and don&rsquo;t forget to also pick the appropriate store in <code>config/initializers/session_store.rb</code> or it still won&rsquo;t work:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">MyApp</span><span class="o">::</span><span class="no">Application</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">session_store</span> <span class="ss">:active_record_store</span>
</span></span></code></pre></div><p>But I think that advice applies to <em>all</em> versions of Rails :)</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>You can get all the Rails middleware by setting <code>config.api_only = false</code>, but this kind of defeats the purpose of Rails-API, imo.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>It uses the session to store the <code>state</code> param sent from the provider to ensure no CSRF tomfoolery.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Configuring redis-store / redis-rails on Heroku</title>
      <link>https://jerodsanto.net/2013/01/configuring-redis-store-/-redis-rails-on-heroku/</link>
      <pubDate>Wed, 23 Jan 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/01/configuring-redis-store-/-redis-rails-on-heroku/</guid>
      
      
      <description><![CDATA[<p><a href="https://github.com/jodosha/redis-store">redis-store&rsquo;s</a> redis-rails <a href="http://rubygems.org/gems/redis-rails">gem</a> is awesome, but it can be a pain in the buttox to get configured on Heroku.</p>
<p>That&rsquo;s because Heroku&rsquo;s ever-growing <a href="https://addons.heroku.com/?q=redis">list of Redis providers</a> all use <a href="https://devcenter.heroku.com/articles/config-vars">config vars</a> to set the Redis URL and redis-rails expects you to hook up the cache store and session store during the Rails application boot process, during which the config vars are unavailable.</p>
<p>Here&rsquo;s how I got it working:</p>
<p>First, delete <code>config/initializers/session_store.rb</code>. It&rsquo;s not needed because we&rsquo;ll initialize the session store and the cache store in the same place.</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="https://github.com/jodosha/redis-store">redis-store&rsquo;s</a> redis-rails <a href="http://rubygems.org/gems/redis-rails">gem</a> is awesome, but it can be a pain in the buttox to get configured on Heroku.</p>
<p>That&rsquo;s because Heroku&rsquo;s ever-growing <a href="https://addons.heroku.com/?q=redis">list of Redis providers</a> all use <a href="https://devcenter.heroku.com/articles/config-vars">config vars</a> to set the Redis URL and redis-rails expects you to hook up the cache store and session store during the Rails application boot process, during which the config vars are unavailable.</p>
<p>Here&rsquo;s how I got it working:</p>
<p>First, delete <code>config/initializers/session_store.rb</code>. It&rsquo;s not needed because we&rsquo;ll initialize the session store and the cache store in the same place.</p>
<p>Next, create <code>config/initializers/redis_store.rb</code> and put this in it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">redis_url</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s2">&#34;REDISCLOUD_URL&#34;</span><span class="o">]</span> <span class="o">||</span> <span class="s2">&#34;redis://127.0.0.1:6379/0/myapp&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">MyApp</span><span class="o">::</span><span class="no">Application</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">cache_store</span> <span class="o">=</span> <span class="ss">:redis_store</span><span class="p">,</span> <span class="n">redis_url</span>
</span></span><span class="line"><span class="cl"><span class="no">MyApp</span><span class="o">::</span><span class="no">Application</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">session_store</span> <span class="ss">:redis_store</span><span class="p">,</span> <span class="ss">redis_server</span><span class="p">:</span> <span class="n">redis_url</span>
</span></span></code></pre></div><p>That&rsquo;s all there is to it! A few notes:</p>
<ol>
<li>initializers are executed <em>after</em> <code>application.rb</code>, at which point Heroku&rsquo;s config vars are available</li>
<li>when the config var URL is nil, like in dev mode, it will revert to your local Redis server</li>
<li>I&rsquo;m using <a href="https://addons.heroku.com/rediscloud">RedisCloud</a> as a Redis provider, but you&rsquo;ll need to adjust the config var to match whichever provider you choose</li>
<li>Be sure to replace <code>MyApp</code> with the name of your app ;)</li>
<li>The <code>/myapp</code> at the end of the fallback URL is optional. It adds a namespace to all Redis keys. I like this in dev mode because I can see all of my app&rsquo;s keys by doing a <code>keys myapp*</code> in the Redis CLI. Feel free to omit</li>
</ol>
<p>Happy caching!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Dynamic Routes to Static Pages on Rails</title>
      <link>https://jerodsanto.net/2013/01/dynamic-routes-to-static-pages-on-rails/</link>
      <pubDate>Mon, 07 Jan 2013 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2013/01/dynamic-routes-to-static-pages-on-rails/</guid>
      
      
      <description><![CDATA[<p>There&rsquo;s one thing that almost every web app and web site needs: <strong>static pages</strong></p>
<p>Many Rails users turn to a gem like Thoughtbot&rsquo;s <a href="https://github.com/thoughtbot/high_voltage">High Voltage</a> to address this need. I tend not to do that for a few reasons:</p>
<ul>
<li>Size matters. When it comes to Gemfiles, smaller is better.</li>
<li>Customization is inevitable, which often obviates any wins from going 3rd party</li>
<li>This is a trivial problem to solve</li>
</ul>
<p>Having cleared that, how do we go about creating static pages in Rails?</p>]]></description>
      
      <content:encoded><![CDATA[<p>There&rsquo;s one thing that almost every web app and web site needs: <strong>static pages</strong></p>
<p>Many Rails users turn to a gem like Thoughtbot&rsquo;s <a href="https://github.com/thoughtbot/high_voltage">High Voltage</a> to address this need. I tend not to do that for a few reasons:</p>
<ul>
<li>Size matters. When it comes to Gemfiles, smaller is better.</li>
<li>Customization is inevitable, which often obviates any wins from going 3rd party</li>
<li>This is a trivial problem to solve</li>
</ul>
<p>Having cleared that, how do we go about creating static pages in Rails?</p>
<p>Each static page needs 3 things:</p>
<ol>
<li>A controller action</li>
<li>A view</li>
<li>A route</li>
</ol>
<p>Let&rsquo;s step through the process of providing these things for a static FAQ page.</p>
<p><strong>Step 1</strong> is easy peasy. Create a <code>PagesController</code> and add an action method to it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PagesController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">faq</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>That call to <code>render</code> is optional, but I think it&rsquo;s clearer than just leaving the method empty.</p>
<p><strong>Step 2</strong> is required for any static page because it has to have <em>some</em> content. Create a view file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> touch app/views/pages/faq.html.erb
</span></span></code></pre></div><p>The brute force way to handle <strong>Step 3</strong> would be to manually add a <code>get</code> route<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> to <code>config/routes.rb</code>, like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">MyApp</span><span class="o">::</span><span class="no">Application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># other routes ...</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span> <span class="s2">&#34;/faq&#34;</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s2">&#34;pages#faq&#34;</span><span class="p">,</span> <span class="ss">as</span><span class="p">:</span> <span class="ss">:faq_page</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>That works, but gets pretty un-DRY as we add more static pages. Instead, we can dynamically generate the routes based on the controller&rsquo;s action methods.</p>
<p>We put this in our routes in place of the manual route:<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">MyApp</span><span class="o">::</span><span class="no">Application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># other routes ...</span>
</span></span><span class="line"><span class="cl">  <span class="no">PagesController</span><span class="o">.</span><span class="n">action_methods</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">action</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="s2">&#34;/</span><span class="si">#{</span><span class="n">action</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s2">&#34;pages#</span><span class="si">#{</span><span class="n">action</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span> <span class="ss">as</span><span class="p">:</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">action</span><span class="si">}</span><span class="s2">_page&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>From now on we can skip <strong>Step 3</strong> when adding new static pages!</p>
<p>We are also dynamically naming these routes so we can use all of the related helper methods, like <code>faq_page_path</code> and friends.</p>
<h3 id="good-not-great">Good, not great</h3>
<p>I&rsquo;ve found this to be a <em>pretty good</em> solution, but it&rsquo;s not ideal.</p>
<p>Ideally, we&rsquo;d be able to sniff out the view files and generate action methods for them, but this has never been enough of a pain point for me to dig in and come up with a solution.</p>
<p>If you know how to get that done in an elegant fashion, please let me know!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>you can also use <code>matches</code>, but Rails 4 and beyond prefer <code>get</code>, <code>post</code>, etc.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Rails puts controllers&rsquo; <em>public</em> instance methods into <code>action_methods</code>, so any methods that you don&rsquo;t want having routes generated for need to be <code>protected</code> or <code>private</code>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Top Hacker News Content of 2012</title>
      <link>https://jerodsanto.net/2012/12/top-hacker-news-content-of-2012/</link>
      <pubDate>Mon, 31 Dec 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/12/top-hacker-news-content-of-2012/</guid>
      
      
      <description><![CDATA[<p><a href="http://news.ycombinator.com/user?id=judofyr">Judofyr</a>&rsquo;s excellent <a href="http://news.ycombinator.com/item?id=4966714">collection</a> of Hacker News threads with book recommendations made me want to see all of the best HN threads this year.</p>
<p>Thankfully, the <a href="http://www.hnsearch.com/api">HNSearch API</a> makes this kind of data easy to get at so I hacked up a <a href="https://gist.github.com/4419913">quick script</a> to do just that.</p>
<p>While I was at it, I grabbed the top submissions as well!</p>
<h2 id="top-ten-submissions-by-point-count">Top Ten Submissions by Point Count</h2>
<p><strong>#10:</strong> <a href="http://www.realdanlyons.com/blog/2012/02/13/hit-men-click-whores-and-paid-apologists-welcome-to-the-silicon-cesspool/">Hit men, click whores, and paid apologists: Welcome to the Silicon Cesspool</a></p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="http://news.ycombinator.com/user?id=judofyr">Judofyr</a>&rsquo;s excellent <a href="http://news.ycombinator.com/item?id=4966714">collection</a> of Hacker News threads with book recommendations made me want to see all of the best HN threads this year.</p>
<p>Thankfully, the <a href="http://www.hnsearch.com/api">HNSearch API</a> makes this kind of data easy to get at so I hacked up a <a href="https://gist.github.com/4419913">quick script</a> to do just that.</p>
<p>While I was at it, I grabbed the top submissions as well!</p>
<h2 id="top-ten-submissions-by-point-count">Top Ten Submissions by Point Count</h2>
<p><strong>#10:</strong> <a href="http://www.realdanlyons.com/blog/2012/02/13/hit-men-click-whores-and-paid-apologists-welcome-to-the-silicon-cesspool/">Hit men, click whores, and paid apologists: Welcome to the Silicon Cesspool</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=smacktoward">smacktoward</a> on Feburary 13th — 1,285 points (<a href="http://news.ycombinator.com/item?id=3587730">HN Thread</a>)</div>
<p><strong>#9:</strong> <a href="https://www.facebook.com/limitedpressing/posts/209534972507958">Company withdrawing from Facebook as analytics show 80% of ad clicks from bots</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=petercooper">petercooper</a> on July 30th — 1,309 points (<a href="http://news.ycombinator.com/item?id=4312731">HN Thread</a>)</div>
<p><strong>#8:</strong> &ldquo;<a href="http://therealkatie.net/blog/2012/mar/21/lighten-up/">Lighten Up</a>&rdquo;</p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=jnoller">jnoller</a> on March 21st — 1,332 points (<a href="http://news.ycombinator.com/item?id=3736037">HN Thread</a>)</div>
<p><strong>#7:</strong> <a href="http://www.meteor.com/">Show HN: Meteor, a realtime JavaScript framework</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=geoffschmidt">geoffschmidt</a> on June 10th — 1,347 points (<a href="http://news.ycombinator.com/item?id=3824908">HN Thread</a>)</div>
<p><strong>#6:</strong> <a href="http://www.bekkelund.net/2012/10/22/outlawed-by-amazon-drm/">Outlawed by Amazon DRM</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=paulsilver">paulsilver</a> on October 22nd — 1,424 points (<a href="http://news.ycombinator.com/item?id=4682392">HN Thread</a>)</div>
<p><strong>#5:</strong> <a href="http://www.chris-granger.com/2012/04/12/light-table---a-new-ide-concept/">Light Table - a new IDE concept</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=ibdknox">ibdknox</a> on April 14th — 1,543 points (<a href="http://news.ycombinator.com/item?id=3836978">HN Thread</a>)</div>
<p><strong>#4:</strong> <a href="http://tsaoutofourpants.wordpress.com/2012/03/06/1b-of-nude-body-scanners-made-worthless-by-blog-how-anyone-can-get-anything-past-the-tsas-nude-body-scanners/">$1B of TSA Nude Body Scanners Made Worthless By Blog</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=zotz">zotz</a> on March 6th — 1,610 points (<a href="http://news.ycombinator.com/item?id=3673462">HN Thread</a>)</div>
<p><strong>#3:</strong> <a href="http://ycombinator.com/rfs9.html">Request for Startups: Kill Hollywood.</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=dzlobin">dzlobin</a> on January 20th — 2,024 points (<a href="http://news.ycombinator.com/item?id=3491542">HN Thread</a>)</div>
<p><strong>#2:</strong> <a href="http://news.ycombinator.com/item?id=3746692">Poll: What&rsquo;s your favortie programming langauge?</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=GreekOphion">GreekOphion</a> on March 23rd — 2,305 points</div>
<p><strong>#1:</strong> <a href="http://news.ycombinator.com/vote?for=3742902&amp;dir=up&amp;whence=%6e%65%77%65%73%74">Show HN: This up votes itself</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=olalonde">olalonde</a> on March 23rd — 3,357 points (<a href="http://news.ycombinator.com/item?id=3742902">HN Thread</a>)</div>
<h2 id="top-ten-submissions-by-comment-count">Top Ten Submissions by Comment Count</h2>
<p><strong>#10:</strong> <a href="http://ploum.net/post/im-a-pirate">Why I&rsquo;m a Pirate</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=tbassetto">tbassetto</a> on January 18th — 526 comments (<a href="http://news.ycombinator.com/item?id=3478850">HN Thread</a>)</div>
<p><strong>#9:</strong> <a href="http://www.bbc.co.uk/news/uk-19281492">Ecuador grants Julian Assange asylum</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=anons2011">anons2011</a> on August 16th — 531 comments (<a href="http://news.ycombinator.com/item?id=4390885">HN Thread</a>)</div>
<p><strong>#8:</strong> <a href="http://arstechnica.com/information-technology/2012/06/macbook-pro-gets-retina-display-macbook-air-also-updated-with-ivy-bridge/">MacBook Pro gets Retina Display; MacBook Air updated with Ivy Bridge</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=sciurus">sciurus</a> on June 11th — 538 comments (<a href="http://news.ycombinator.com/item?id=4095814">HN Thread</a>)</div>
<p><strong>#7:</strong> <a href="http://translate.google.com/translate?hl=en&amp;sl=no&amp;tl=en&amp;u=http://www.dagensit.no/article2411857.ece">6.5 Million LinkedIn Password Hashes Leaked</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=ssclafani">ssclafani</a> on June 6th — 545 comments (<a href="http://news.ycombinator.com/item?id=4073309">HN Thread</a>)</div>
<p><strong>#6:</strong> <a href="http://andorjakab.blog.hu/2012/01/06/this_is_why_i_don_t_give_you_a_job">This is why I don&rsquo;t give you a job</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=fourspace">fourspace</a> on January 7th — 549 comments (<a href="http://news.ycombinator.com/item?id=3436244">HN Thread</a>)</div>
<p><strong>#5:</strong> <a href="http://news.ycombinator.com/item?id=3746692">Poll: What&rsquo;s Your Favorite Programming Language?</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=GreekOphion">GreekOphion</a> on March 23rd — 625 comments (<a href="http://news.ycombinator.com/item?id=3746692">HN Thread</a>)</div>
<p><strong>#4:</strong> <a href="http://thenextweb.com/shareables/2012/11/06/reddit-user-captures-video-of-2012-voting-machines-altering-votes/">Reddit user captures video of 2012 voting machines altering votes</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=jipumarino">jipumarino</a> on November 6th — 658 comments (<a href="http://news.ycombinator.com/item?id=4749574">HN Thread</a>)</div>
<p><strong>#3:</strong> <a href="http://www.microsoft.com/surface/">Microsoft Introduces New &ldquo;Surface&rdquo; Tablet</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=yottabyte47">yottabyte47</a> on June 18th — 693 comments (<a href="http://news.ycombinator.com/item?id=">HN Thread</a>)</div>
<p><strong>#2:</strong> <a href="http://ycombinator.com/rfs9.html">Request for Startups: Kill Hollywood.</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=dzlobin">dzlobin</a> on January 20th — 707 comments (<a href="http://news.ycombinator.com/item?id=3491542">HN Thread</a>)</div>
<p><strong>#1:</strong> <a href="http://therealkatie.net/blog/2012/mar/21/lighten-up/">&ldquo;Lighten up&rdquo;</a></p>
<div class="meta">submitted by <a href="http://news.ycombinator.com/user?id=jnoller">jnoller</a> on March 21st — 827 comments (<a href="http://news.ycombinator.com/item?id=3736037">HN Thread</a>)</div>
<h2 id="top-ten-comments-by-point-count">Top Ten Comments by Point Count</h2>
<p><strong>#10:</strong> <a href="http://news.ycombinator.com/user?id=olalonde">patio11</a> on <a href="http://news.ycombinator.com/item?id=4448500">walking out of an interview</a> (August 29th — 254 points)</p>
<p><strong>#9:</strong> <a href="http://news.ycombinator.com/user?id=olalonde">sriramk</a> on <a href="http://news.ycombinator.com/item?id=4448500">Farewell Stack Exchange</a> (February 6th — 255 points)</p>
<p><strong>#8:</strong> <a href="http://news.ycombinator.com/user?id=pg">pg</a> on <a href="http://news.ycombinator.com/item?id=3678796">Viral Video About Body Scanners</a> (March 8th — 257 points)</p>
<p><strong>#7:</strong> <a href="http://news.ycombinator.com/user?id=tsaoutourpants">tsaoutourpants</a> on <a href="http://news.ycombinator.com/item?id=3673589">$1B of TSA Nude Body Scanners Made Worthless By Blog</a> (March 7th — 257 points)</p>
<p><strong>#6:</strong> <a href="http://news.ycombinator.com/user?id=tptacek">tptacek</a> on <a href="http://news.ycombinator.com/item?id=4328399">Matasano Security acquired for £8.4m ($13m)</a> (August 2nd — 259 points)</p>
<p><strong>#5:</strong> <a href="http://news.ycombinator.com/user?id=jkahn">jkahn</a> on <a href="http://news.ycombinator.com/item?id=3975823">Please don&rsquo;t learn to code</a> (May 15th — 260 points)</p>
<p><strong>#4:</strong> <a href="http://news.ycombinator.com/user?id=api">api</a> on <a href="http://news.ycombinator.com/item?id=4448500">Google Fiber Plans &amp; Pricing</a> (July 26th — 264 points)</p>
<p><strong>#3:</strong> <a href="http://news.ycombinator.com/user?id=kirubakaran">kirubakaran</a> on <a href="http://news.ycombinator.com/item?id=4448500">Instagram is &ldquo;worth&rdquo; more than the New York Times</a> (April 10th — 264 points)</p>
<p><strong>#2:</strong> <a href="http://news.ycombinator.com/user?id=pg">pg</a> on <a href="http://news.ycombinator.com/item?id=4397542"> Why I now, unfortunately, hate Hacker News..</a> (August 17th — 271 points)</p>
<p><strong>#1:</strong> <a href="http://news.ycombinator.com/user?id=mapgrep">mapgrep</a> on <a href="http://news.ycombinator.com/item?id=4755594">The Best</a> (November 7th — 397 points)</p>
<h2 id="some-thoughts-and-stats">Some thoughts and stats</h2>
<p>I thought it was interesting to see how different the top submissions lists were when sorted by points and comments. Only three submissions found their way on to both lists (Kill Hollywood, Lighten Up, and Favorite Programming Language).</p>
<p>It was also interesting to see by how large a margin mapgrep&rsquo;s comment beat out the others. Over 100 votes!</p>
<p>Overall, there were 232,825 submissions to Hacker News in 2012 and 1,191,015 comments posted.</p>
<p>Busy year!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Beloved Bits, 2012</title>
      <link>https://jerodsanto.net/2012/12/beloved-bits-2012/</link>
      <pubDate>Thu, 27 Dec 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/12/beloved-bits-2012/</guid>
      
      
      <description><![CDATA[<p>It&rsquo;s that time of the year again, and I had so much fun writing up <a href="https://jerodsanto.net/2011/12/beloved-bits-2011/">my favorite digital things last year</a> that I thought I&rsquo;d make a tradition of it.</p>
<p>But first, a review of my rules:</p>
<ol>
<li>List items must be anatomically digital</li>
<li>List items must have won <em>me</em> over in 2012</li>
<li>List items may or may not have been released in 2012</li>
</ol>
<h2 id="ios-apps">iOS Apps</h2>
<p>I&rsquo;m hoping that iOS 7 does some much-needed innovating on my favorite operating system, but the app ecosystem is still head and shoulders above other platforms.</p>]]></description>
      
      <content:encoded><![CDATA[<p>It&rsquo;s that time of the year again, and I had so much fun writing up <a href="https://jerodsanto.net/2011/12/beloved-bits-2011/">my favorite digital things last year</a> that I thought I&rsquo;d make a tradition of it.</p>
<p>But first, a review of my rules:</p>
<ol>
<li>List items must be anatomically digital</li>
<li>List items must have won <em>me</em> over in 2012</li>
<li>List items may or may not have been released in 2012</li>
</ol>
<h2 id="ios-apps">iOS Apps</h2>
<p>I&rsquo;m hoping that iOS 7 does some much-needed innovating on my favorite operating system, but the app ecosystem is still head and shoulders above other platforms.</p>
<h3 id="habit-list">Habit List</h3>
<p><a href="http://habitlistapp.com/">Habit List</a> jumped up and surprised me. The conceipt is a simple one: enter things you want to turn into habits and track how you&rsquo;re doing. It&rsquo;s not social, it&rsquo;s not revolutionary, and it&rsquo;s not even complex to implement, but it <em>is</em> working for me.</p>
<p>Habits I&rsquo;ve been developing with the aid of this app include shaving regularly (which I <em>abhor</em>), flossing, blogging, exercising, and more.</p>
<p><img src="http://jerodsanto.net/drop/habit-list-pic.png" alt="I&rsquo;m getting a late start on exercising this week!"></p>
<p>The best part? Once you get a streak going it compells you not to blow it! Oh, and they just released a pretty awesome 2.0 as I write this post. Good stuff.</p>
<h3 id="sports-scores--alerts">Sports Scores &amp; Alerts</h3>
<p>I had been a long time <a href="http://mobile.yahoo.com/sports/iphone">Sportacular</a> user until Yahoo! acquired it and turned it into Craptacular. This year I went hunting for something better to track various sporting events<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, and boy did I find it.</p>
<p><a href="https://itunes.apple.com/us/app/sports-scores-alerts/id432450349?mt=8">Sports Scores &amp; Alerts</a> is fast, easy to use, works reliably, and is really really fast. Did I mention that it&rsquo;s fast?</p>
<p>It has all the in-game push notifications I could want and an interesting freemium business model where subscribing via in-app purchase unlocks more concurrent alerts and other features.</p>
<p>I am not a huge sports nut, so the free stuff gets me where I want to go. I do hope that this model is working for them, though, because I love the app and would hate to have it acquired by some behemoth that ruins it.</p>
<p><strong>Honorable Mentions:</strong> <a href="https://www.getyardsale.com/">Yard Sale</a>, <a href="http://backspac.es/">Backspaces</a>, <a href="http://darkskyapp.com/">Dark Sky</a></p>
<h2 id="os-x-apps">OS X Apps</h2>
<p>OS X is my work horse. My favorite OS X apps are almost always tools and utilities. This year is no exception, with three tools I use to make my work/life more efficient.</p>
<h3 id="dash">Dash</h3>
<p><a href="https://itunes.apple.com/us/app/dash-docs-snippets/id458034879?mt=12">Dash</a> has become one of a handful of apps that I have running all day, every day. It is a code snippet manager teamed with a documentation browser.</p>
<p>I&rsquo;m not much of a code snippet guy myself, so I mostly use Dash for docs, but boy do I love it for that purpose.</p>
<p>It so nice having offline, auto-updating, searchable docsets for every language/framework/toolset I use. I can hardly remember life before Dash.</p>
<p><img src="http://jerodsanto.net/drop/dash.png" alt="Look at all those shiny docsets"></p>
<p>Oh, and Dash punts to Google or Stack Overflow if it can&rsquo;t find what I&rsquo;m looking for, so there&rsquo;s really no reason to not use it every time I need a reference.</p>
<h3 id="imageoptim">ImageOptim</h3>
<p><a href="http://imageoptim.com/">ImageOptim</a> is a great little utility. It wraps up all the best image optimization tools (OptiPNG, PNGCrush, etc.) in a nice drag-and-drop interface.</p>
<p>It handles many image types and modifies them in place, showing me the savings.</p>
<p><a href="http://imageoptim.com/"><img src="http://jerodsanto.net/drop/image-optim.png" alt=""></a></p>
<p>I love finding tools like IMageOptim that do one thing well and make my life as a webdev easier.</p>
<h3 id="cloudapp">CloudApp</h3>
<p><a href="http://getcloudapp.com/">CloudApp</a> got my business because Evernote acquired Skitch and then released a terrible, terrible update to the software.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> It wasn&rsquo;t too much of a surprise, but it turned out to be just as awesome as so many people had told me.</p>
<p>The killer feature? The app shows you how many people have viewed the link you sent them, so you can know <em>for sure</em> whether they saw it or not.</p>
<p><a href="http://getcloudapp.com/"><img src="http://jerodsanto.net/drop/cloudapp.png" alt="this is super handy when sending mockups to clients and collaborators"></a></p>
<p>It&rsquo;s like read receipts for files!</p>
<p><strong>Honorable Mentions:</strong> <a href="http://notational.net/">Notational Velocity</a>, <a href="http://livereload.com/">LiveReload</a>, <a href="http://www.screenyapp.com/">Screeny</a>, <a href="http://shinyplasticbag.com/dragondrop/">DragonDrop</a></p>
<h2 id="games">Games</h2>
<p>I was once an avid gamer. Then I became a once-avid gamer. Thanks to iOS and the rise of &ldquo;casual gaming&rdquo;, I&rsquo;m slowly returning to form.</p>
<h3 id="hero-academy">Hero Academy</h3>
<p>When it comes to gaming and me, 2012 has been the year of <a href="http://www.robotentertainment.com/games/heroacademy">Hero Academy</a>. In fact, this epic strategy game became such a staple in my life that it made an appearance at my 30th birthday&hellip;</p>
<blockquote class="twitter-tweet tw-align-center"><p>My favorite one was the Hero Academy cupcake. It tasted like victory. <a href="http://t.co/QWd51STd" title="http://twitter.com/jerodsanto/status/227029340964134912/photo/1">twitter.com/jerodsanto/sta…</a></p>&mdash; Jerod Santo (@jerodsanto) <a href="https://twitter.com/jerodsanto/status/227029340964134912" data-datetime="2012-07-22T13:16:29+00:00">July 22, 2012</a></blockquote>
<script async src="https://jerodsanto.net//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>As of today I&rsquo;ve racked up 404 wins on Hero Academy and I have 14 games in-progress right now.</p>
<p>My wife says I&rsquo;m addicted. She might be right.</p>
<h3 id="bastion">Bastion</h3>
<p>The only game that was compelling enough to rip me away from Hero Academy has been <a href="http://supergiantgames.com/?page_id=242">Bastion</a>.</p>
<p>Bastion is a totally unique and stylish RPG that completely blew me away. It&rsquo;s available on many platforms, but I played it on the iPad and it was a near perfect video game experience.</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/TptJHeWngJs?rel=0" frameborder="0" allowfullscreen></iframe>
<p>If you haven&rsquo;t played Bastion yet, you really need to get on that.</p>
<p><strong>Honorable Mentions:</strong> <a href="http://www.atebits.com/letterpress/">Letterpress</a>, <a href="https://itunes.apple.com/us/app/the-room/id552039496?mt=8">The Room</a>, <a href="http://touchfoo.com/swordigo">Swordigo</a></p>
<h2 id="podcasts">Podcasts</h2>
<p>I cycled through a bunch of podcasts this year, but there&rsquo;s only a couple that have earned must-listen status  in my podcast rotation.</p>
<h3 id="the-vergecast">The Vergecast</h3>
<p>This year, The <a href="http://www.theverge.com/the-vergecast">Vergecast</a> took the place of <a href="http://twit.tv">TwiT</a> as my weekly technology-culture commentary podcast. The two shows are similar in their format and topics, but The Vergecast is hands down more interesting to me.</p>
<p><a href="http://www.theverge.com/the-vergecast"><img src="http://jerodsanto.net/drop/vergecast.jpg" alt=""></a></p>
<p>The hosts are funnier, the insights are more insightful, and the rabbit holes are deeper. Plus they take calls sometimes which always result in hilarity.</p>
<h3 id="giant-robots-smashing-into-other-giant-robots">Giant Robots Smashing into Other Giant Robots</h3>
<p>Leave it to the Thoughtbot team to enter a crowded market of programming discussion podcasts and still shine by way of great guests, deep insights into development processes, and episode lengths that are just right.</p>
<p>The <a href="http://learn.thoughtbot.com/podcast">Giant Robots Smashing into Other Giant Robots</a> is definitely worth checking out. A few of my favorite episodes so far are:</p>
<ul>
<li>Episode 28: <a href="http://learn.thoughtbot.com/podcast/28">Farther, further, faster</a></li>
<li>Episode 19: <a href="http://learn.thoughtbot.com/podcast/19">I have tons of guns and knives</a></li>
<li>Episode 18: <a href="http://learn.thoughtbot.com/podcast/18">Trading Hours for Money</a></li>
</ul>
<p><strong>Honorable Mentions:</strong> <a href="http://5by5.tv/criticalpath">The Critical Path</a>, <a href="http://hnpod.com/">HNpod</a></p>
<h2 id="music">Music</h2>
<p>I didn&rsquo;t include a music category last year, but thanks to signg up for <a href="http://rdio.com/">Rdio</a> this year, I&rsquo;ve been listening to a lot more new stuff. Speaking of Rdio&hellip;</p>
<h3 id="rdio">Rdio</h3>
<p>The biggest thing to happen to my musical life in 2012 was signing up for <a href="http://rdio.com/">Rdio</a>. I&rsquo;ve always naysayed subscription-based music services because I like to take ownership of music that I love, but working alone most days means I spend a lot of time listening to music and even my sizeable collection gets old quickly.</p>
<p>I gave Spotify a try first, but their interface was such utter crap that I couldn&rsquo;t stomach it.</p>
<p>Rdio to the rescue. Getting to sample from all of the newest music each week is an utter joy. Finally, a reason to look forward to Tuesdays!</p>
<h3 id="blunderbuss">Blunderbuss</h3>
<p>If I had to pick an album of the year, it&rsquo;d be <a href="http://www.amazon.com/Blunderbuss-Jack-White/dp/B007CKNX28?tag=jerodsanto-20">Blunderbuss</a> by Jack White. Everything Jack White puts out is good, and his first solo album is no exception. Every song on this album is interesting and unique, yet it feels like a cohesive work.</p>
<iframe width="500" height="250" src="https://rd.io/i/QVNNbyJI2hc" frameborder="0"></iframe>
<h3 id="the-sounds">The Sounds</h3>
<p><a href="http://the-sounds.com/">The Sounds</a> are not a new band, but they sure are new to me! It&rsquo;s not too often that I come across a band and immediately enjoy their entire collection, but I was happy to have that experience this year with The Sounds.</p>
<p>Here&rsquo;s a taste of the goods:</p>
<iframe width="500" height="250" src="https://rd.io/i/QVNNbyJWmus" frameborder="0"></iframe>
<h3 id="propaganda">Propaganda</h3>
<p>I don&rsquo;t usually get down with contemporary Christian music, especially Christian rap music. Most of it just feels like low-quality imitations of the real thing.</p>
<p><a href="https://twitter.com/prophiphop">Propaganda</a>, on the other hand, is the real deal. This man is a face-melting rapper and a poet. Here&rsquo;s Propaganda on empowerment:</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/8E28ovlb77Q?rel=0" frameborder="0" allowfullscreen></iframe>
<p>His entire album is <a href="http://www.amazon.com/Excellent/dp/B0096MDWC6?tag=jerodsanto-20">Excellent</a>. You should buy it.</p>
<p><strong>Honorable Mentions:</strong> Grizzly Bear&rsquo;s <a href="http://www.amazon.com/Shields-Grizzly-Bear/dp/B008966SIU?tag=jerodsanto-20">Shields</a>, Imagine Dragons&rsquo; <a href="http://www.amazon.com/Night-Visions-Imagine-Dragons/dp/B008K9SG9K?tag=jerodsanto-20">Night Visions</a>, Tycho&rsquo;s <a href="http://www.amazon.com/Dive-Tycho/dp/B005ILYN5E?tag=jerodsanto-20">Dive</a>, Birdy&rsquo;s <a href="http://www.amazon.com/Birdy/dp/B00722ZHG6?tag=jerodsanto-20">self-titled album</a></p>
<h2 id="things-that-ended">Things that Ended</h2>
<p>Alas, 2012 wasn&rsquo;t just about the new. Some of my favorite digital things came to a close.</p>
<h3 id="trs">TRS</h3>
<p>I had been watching the <a href="http://revision3.com/trs">Totally Rad Show</a> since way back in their first season. That&rsquo;s long enough to feel like Alex, Jeff, and Dan are close friends of mine even though they&rsquo;ve never met me. Weird, huh?</p>
<p>I lived much of my geeky life vicariously through TRS. I don&rsquo;t have time to play all the new video games, see every movie that comes out, or keep up with all the great TV shows, but TRS made me feel like I did all that and more by watching the guys review, preview, and speculate on these things.</p>
<p>I hope something will step up and fill the void that TRS leaves in my daily routine, but I doubt I&rsquo;ll be so lucky.</p>
<h3 id="hypercritical">Hypercritical</h3>
<p><a href="http://5by5.tv/hypercritical">Hypercritical</a> with John Siracusa made my <a href="https://jerodsanto.net/2011/12/beloved-bits-2011/">Beloved Bits</a> list last year. John is calling it quits, after recording 100 glorious episodes.</p>
<p>Over the last year I went from enjoying John&rsquo;s rants on technology to flat out adoring them. It is the one podcast that I look forward to every week and I&rsquo;ll miss it quite a bit.</p>
<p>Thankfully, John will still be making regular appearances on <a href="http://5by5.tv/incomparable">The Incomparable</a>, another pretty good geek culture podcast that I&rsquo;ve been listening to lately. It&rsquo;s not the same, but it&rsquo;ll have to do for now.</p>
<h2 id="quid-pro-quo">Quid Pro Quo</h2>
<p>So, that&rsquo;s my list. I hope you find one or more of these things as enjoyable and/or useful as I have.</p>
<p>Please do recommend any of your most beloved digital things of 2012 in the comments so I can discover them in 2013!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Okay fine, pretty much just Fantasy Football&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>This has become and all-too-common theme in my software-using life&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>A Handy Method to Share Data From Rails Controllers to Views Without Requiring Direct Instance Variable Access</title>
      <link>https://jerodsanto.net/2012/12/a-handy-method-to-share-data-from-rails-controllers-to-views-without-requiring-direct-instance-variable-access/</link>
      <pubDate>Tue, 18 Dec 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/12/a-handy-method-to-share-data-from-rails-controllers-to-views-without-requiring-direct-instance-variable-access/</guid>
      
      
      <description><![CDATA[<p>Rails&rsquo; method of sharing data between controllers and views via instance variables bothers many developers. For the uninitiated, a Rails controller method like this one&hellip;</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">EventsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@events</span> <span class="o">=</span> <span class="no">Event</span><span class="o">.</span><span class="n">scoped</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>&hellip; makes the <code>@events</code> instance variable accessible to the rendered view.</p>
<p>This bugs me, too. My beefs with it are:</p>
<ul>
<li>It is counterintuitive. Why would an <em>instance</em> variable be how you make something available <em>outside</em> the instance?</li>
<li>It couples the view and the controller more than necessary, shedding Ruby&rsquo;s encapsulation strength of having method calls and ivar accessors identical.</li>
<li>Views and partials have varying access to the instance variable, which is confusing.</li>
</ul>
<p>Better would be explicitly exposing the events collection to views via a reader and helper method, like this:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Rails&rsquo; method of sharing data between controllers and views via instance variables bothers many developers. For the uninitiated, a Rails controller method like this one&hellip;</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">EventsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@events</span> <span class="o">=</span> <span class="no">Event</span><span class="o">.</span><span class="n">scoped</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>&hellip; makes the <code>@events</code> instance variable accessible to the rendered view.</p>
<p>This bugs me, too. My beefs with it are:</p>
<ul>
<li>It is counterintuitive. Why would an <em>instance</em> variable be how you make something available <em>outside</em> the instance?</li>
<li>It couples the view and the controller more than necessary, shedding Ruby&rsquo;s encapsulation strength of having method calls and ivar accessors identical.</li>
<li>Views and partials have varying access to the instance variable, which is confusing.</li>
</ul>
<p>Better would be explicitly exposing the events collection to views via a reader and helper method, like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">EventsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@events</span> <span class="o">=</span> <span class="no">Event</span><span class="o">.</span><span class="n">scoped</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">attr_reader</span> <span class="ss">:events</span>
</span></span><span class="line"><span class="cl">  <span class="n">helper_method</span> <span class="ss">:events</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Now all views can just call the <code>events</code> method and not worry about the ivar.</p>
<p>This becomes cumbersome when you do it across many controllers and actions, so I use this handy method to &ldquo;automate&rdquo; the process to expose certain ivars to views:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># short hand for exposing a list of ivars to view objects</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">exposes</span><span class="p">(</span><span class="o">*</span><span class="n">ivars</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">ivars</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">ivar</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="kp">attr_reader</span> <span class="n">ivar</span><span class="o">.</span><span class="n">to_sym</span>
</span></span><span class="line"><span class="cl">      <span class="n">helper_method</span> <span class="n">ivar</span><span class="o">.</span><span class="n">to_sym</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>The method takes an arbitrary list of ivar names. Using it, the example  controller class looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">EventsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">exposes</span> <span class="ss">:events</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@events</span> <span class="o">=</span> <span class="no">Event</span><span class="o">.</span><span class="n">scoped</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Unfortunately, the view <strong>can</strong> still access <code>@events</code>, but it&rsquo;s a step in the right direction.</p>
<p>I&rsquo;m sure there are ways to stop direct access of instance variables from views, but I haven&rsquo;t gotten that drastic yet.</p>
<p>I&rsquo;ve gotten a lot of mileage out of this method. Perhaps you will too.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Capybara and Poltergeist: Snap!</title>
      <link>https://jerodsanto.net/2012/12/capybara-and-poltergeist-snap/</link>
      <pubDate>Tue, 11 Dec 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/12/capybara-and-poltergeist-snap/</guid>
      
      
      <description><![CDATA[<p>I&rsquo;ve gotten a lot of mileage out of <a href="https://rspec.info">RSpec</a> plus <a href="https://jnicklas.github.com/capybara/">Capybara</a> for integration testing my Ruby apps lately.</p>
<p><a href="http://www.flickr.com/photos/gseloff/4661719163/" title="unNative Texan by gseloff, on Flickr"><img src="http://farm5.staticflickr.com/4036/4661719163_8668525d0f.jpg" width="500" height="393" alt="obligatory Capybara pic"></a></p>
<p>Executing JavaScript with Capybara used to be a pain, but I recently switched from <a href="https://github.com/thoughtbot/capybara-webkit">capybara-webkit</a> to <a href="https://github.com/jonleighton/poltergeist">Poltergeist</a> as my JS test driver.</p>
<p>Poltergeist uses <a href="https://phantomjs.org/">PhantomJS</a>, which in my experience is faster than capybara-webkit and easier to install.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p><img src="https://jerodsanto.net/drop/poltergeist.png" alt="Sadly, Sam Raimi will be subjecting Poltergeist to the ole&rsquo; Hollywood remake"></p>]]></description>
      
      <content:encoded><![CDATA[<p>I&rsquo;ve gotten a lot of mileage out of <a href="https://rspec.info">RSpec</a> plus <a href="https://jnicklas.github.com/capybara/">Capybara</a> for integration testing my Ruby apps lately.</p>
<p><a href="http://www.flickr.com/photos/gseloff/4661719163/" title="unNative Texan by gseloff, on Flickr"><img src="http://farm5.staticflickr.com/4036/4661719163_8668525d0f.jpg" width="500" height="393" alt="obligatory Capybara pic"></a></p>
<p>Executing JavaScript with Capybara used to be a pain, but I recently switched from <a href="https://github.com/thoughtbot/capybara-webkit">capybara-webkit</a> to <a href="https://github.com/jonleighton/poltergeist">Poltergeist</a> as my JS test driver.</p>
<p>Poltergeist uses <a href="https://phantomjs.org/">PhantomJS</a>, which in my experience is faster than capybara-webkit and easier to install.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p><img src="https://jerodsanto.net/drop/poltergeist.png" alt="Sadly, Sam Raimi will be subjecting Poltergeist to the ole&rsquo; Hollywood remake"></p>
<p>Poltergeist supports the required Capybara driver API as well as a few additional features such as taking screenshots.</p>
<p>I wrote a little helper method around taking screenshots, which:</p>
<ol>
<li>generates a name for the output file (because I usually don&rsquo;t care)</li>
<li>puts the file in the trash for me (or in /tmp if there is no trash)</li>
<li>renders the entire page (overridable when I just want the viewport)</li>
<li>opens it in Preview (or whatever the default app for .png is)</li>
</ol>
<p>Not ground breaking, but pretty cool in practice. I call it <code>snap!</code>.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Here&rsquo;s the method:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">snap!</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="p">{})</span>
</span></span><span class="line"><span class="cl">  <span class="n">path</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">fetch</span> <span class="ss">:path</span><span class="p">,</span> <span class="s2">&#34;~/.Trash&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">file</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">fetch</span> <span class="ss">:file</span><span class="p">,</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">Time</span><span class="o">.</span><span class="n">now</span><span class="o">.</span><span class="n">to_i</span><span class="si">}</span><span class="s2">.png&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">full</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">fetch</span> <span class="ss">:full</span><span class="p">,</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">path</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">expand_path</span> <span class="n">path</span>
</span></span><span class="line"><span class="cl">  <span class="n">path</span> <span class="o">=</span> <span class="s2">&#34;/tmp&#34;</span> <span class="k">if</span> <span class="o">!</span><span class="no">File</span><span class="o">.</span><span class="n">exists?</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">uri</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span> <span class="n">path</span><span class="p">,</span> <span class="n">file</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">page</span><span class="o">.</span><span class="n">driver</span><span class="o">.</span><span class="n">render</span> <span class="n">uri</span><span class="p">,</span> <span class="ss">full</span><span class="p">:</span> <span class="n">full</span>
</span></span><span class="line"><span class="cl">  <span class="nb">system</span> <span class="s2">&#34;open </span><span class="si">#{</span><span class="n">uri</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Drop it anywhere that RSpec will load it<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> and use it in your integration tests when your JavaScript is misbehaving:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">feature</span> <span class="s2">&#34;search autocomplete&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">scenario</span> <span class="s2">&#34;from the homepage&#34;</span><span class="p">,</span> <span class="ss">js</span><span class="p">:</span> <span class="kp">true</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">visit</span> <span class="s2">&#34;/&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">fill_in</span> <span class="s2">&#34;search&#34;</span><span class="p">,</span> <span class="ss">with</span><span class="p">:</span> <span class="s2">&#34;Jero&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">snap!</span>
</span></span><span class="line"><span class="cl">    <span class="n">expect</span><span class="p">(</span><span class="n">page</span><span class="p">)</span><span class="o">.</span><span class="n">to</span> <span class="n">have_content</span> <span class="s2">&#34;Jerod Santo&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>In this example, you could visually inspect whether the autocomplete is just not showing up at all or if it <em>is</em> showing up and just has the wrong content in it.</p>
<p>A fringe benefit of using the <code>snap!</code> method is that it calls <code>page.driver.render</code>, which will raise an error when the active driver doesn&rsquo;t have the method.</p>
<p>Capybara&rsquo;s default driver, RackTest, doesn&rsquo;t have it so you&rsquo;ll know right away if your test is just failing because you forgot the <code>js: true</code> flag in the enclosing block.</p>
<p>That one has bit me more than once!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>PhantomJS bundles Qt so you don&rsquo;t have to install it yourself&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>I&rsquo;m using the <code>!</code> bang suffix for no other reason then it makes the method more fun to call&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>when using rspec-rails you can just drop it in <code>spec/support</code>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>The One Thing You Should Not Bring to Your Next Conference</title>
      <link>https://jerodsanto.net/2012/12/the-one-thing-you-should-not-bring-to-your-next-conference/</link>
      <pubDate>Tue, 04 Dec 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/12/the-one-thing-you-should-not-bring-to-your-next-conference/</guid>
      
      
      <description><![CDATA[<p>While packing for an upcoming conference, you think to yourself, <em>&ldquo;I know what I&rsquo;ll do. I&rsquo;ll bring my laptop!&rdquo;</em></p>
<p>Now you&rsquo;ve got problems.</p>
<h3 id="social-problems">Social Problems</h3>
<p>When asked <em>&ldquo;What&rsquo;s the most valuable aspect of attending conferences?&rdquo;</em>, nine out of ten hypothetical people said that it&rsquo;s the people they get to see, meet, and learn from.</p>
<p>Let&rsquo;s take these in order:</p>
<ol>
<li>
<p>See — you can&rsquo;t see anybody when you&rsquo;re head down in your computer! You interact with these folks online all the time, but you never get to chat IRL.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Why are you emailing, tweeting, or God forbid facebooking them right now?</p>]]></description>
      
      <content:encoded><![CDATA[<p>While packing for an upcoming conference, you think to yourself, <em>&ldquo;I know what I&rsquo;ll do. I&rsquo;ll bring my laptop!&rdquo;</em></p>
<p>Now you&rsquo;ve got problems.</p>
<h3 id="social-problems">Social Problems</h3>
<p>When asked <em>&ldquo;What&rsquo;s the most valuable aspect of attending conferences?&rdquo;</em>, nine out of ten hypothetical people said that it&rsquo;s the people they get to see, meet, and learn from.</p>
<p>Let&rsquo;s take these in order:</p>
<ol>
<li>
<p>See — you can&rsquo;t see anybody when you&rsquo;re head down in your computer! You interact with these folks online all the time, but you never get to chat IRL.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Why are you emailing, tweeting, or God forbid facebooking them right now?</p>
</li>
<li>
<p>Meet — you can&rsquo;t meet anybody when you&rsquo;re head down in your computer! I know, meeting people is tough and the glow of your laptop screen is warm and safe, but you have to put yourself out there if you ever want to meet new people. Now&rsquo;s the time!</p>
</li>
<li>
<p>Learn — you probably <em>can</em> learn from somebody when you&rsquo;re head down in your computer, but they&rsquo;ll never know it! Nothing says <em>my-talk-must-not-be-interesting-at-all</em> like a room full of people typing away on their laptops. Would you appreciate that if you were speaking?</p>
</li>
</ol>
<h3 id="logistical-problems">Logistical Problems</h3>
<p>The logistics of bringing a laptop can really cramp your style.</p>
<p>Don&rsquo;t forget, you&rsquo;ll need to bring along a power cord, perhaps a mouse, a spare battery<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, and who-knows-what other accessories.</p>
<p>That&rsquo;s a lot of stuff. You should probably wear a backpack or some kind of bag so you can haul everything.</p>
<p><a href="http://news.tacticalpants.com/ipad-andy-ihnatko-tactical-internet-pants/"><img src="http://jerodsanto.net/drop/ipad-in-pants-pocket.jpg" alt="or you could just roll like this"></a></p>
<p>Also, remember not to leave anything behind after lunch or let that bag out of your site. Somebody might steal it. Blech!</p>
<h3 id="technical-problems">Technical Problems</h3>
<p>Let&rsquo;s face it, conference wifi still sucks.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Even if you get online you will probably experience plenty of frustrating timeouts or at best crappy latency.</p>
<p><img src="http://jerodsanto.net/drop/dawcree-internet.jpg" alt=""></p>
<p>You are unlikely to get any real work done and it will probably be so bad that you just leave your laptop in its bag and haul it around all day (see above).</p>
<p>What&rsquo;s worse, power outlets are at a premium since so many people bring their laptops, so you become &ldquo;that guy/gal&rdquo; who squats the seats closest to an outlet. Unnecessary tension!</p>
<h3 id="workarounds">Workarounds</h3>
<p>So you think you&rsquo;ll need your laptop. I offer two suggestions:</p>
<ol>
<li>Take your smart phone — it will do 95% of what you want during a conference without as many drawbacks.</li>
<li>Take your laptop, but leave it in your hotel room — if you do need to do some heavy lifting during the conference, sneak off during lunch or between events. Or better yet, wait until the end of the day to catch up.</li>
</ol>
<h3 id="never-say-never">Never say never</h3>
<p>You may have good reasons why you <strong>need</strong> to bring your laptop to a conference — like if you&rsquo;re speaking and need to use it for slides/demo purposes — but before you add it to your packing list, consider these things and ask yourself if it is a <strong>need</strong> or simply a <strong>want</strong>.</p>
<p>If it is just a <strong>want</strong>, try leaving it at home and see how things go. I think you&rsquo;ll find that you get way more out of the conference sans laptop then you would with it.</p>
<p>I know that I have.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I went from hating this acronym to using it all the time without noticing the transition.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>If you&rsquo;re one of the fortunate souls whose laptop even has a removable battery.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Surely there are companies focused on this need, but it is still a space that is ripe for innovation.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Creating an SSH Tunnel to a Remote Service</title>
      <link>https://jerodsanto.net/2012/11/creating-an-ssh-tunnel-to-a-remote-service/</link>
      <pubDate>Wed, 28 Nov 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/11/creating-an-ssh-tunnel-to-a-remote-service/</guid>
      
      
      <description><![CDATA[<p>File this under <strong>just-blog-it-already-so-you-can-stop-googling-it</strong></p>
<p>Imagine having Nagios/Resque Web/Monit/Cacti/MySQL/etc all set up on one of your servers. You want to access it from your local machine, but you don&rsquo;t want to make it listen on a public interface and set up a firewall and all that junk.</p>
<p>Instead, you can use SSH to tunnel your local client to the remote server&rsquo;s service. The command looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">ssh user@remotehost -N -L localhost:localport:remotehost:remoteport
</span></span></span></code></pre></div><p>For example, let&rsquo;s say that <a href="https://github.com/defunkt/resque-web">Resque Web</a> is listening on port <strong>5678</strong> on my <strong>jerodsanto.net</strong> server and I want to access it via port <strong>9999</strong> in my local browser.</p>]]></description>
      
      <content:encoded><![CDATA[<p>File this under <strong>just-blog-it-already-so-you-can-stop-googling-it</strong></p>
<p>Imagine having Nagios/Resque Web/Monit/Cacti/MySQL/etc all set up on one of your servers. You want to access it from your local machine, but you don&rsquo;t want to make it listen on a public interface and set up a firewall and all that junk.</p>
<p>Instead, you can use SSH to tunnel your local client to the remote server&rsquo;s service. The command looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">ssh user@remotehost -N -L localhost:localport:remotehost:remoteport
</span></span></span></code></pre></div><p>For example, let&rsquo;s say that <a href="https://github.com/defunkt/resque-web">Resque Web</a> is listening on port <strong>5678</strong> on my <strong>jerodsanto.net</strong> server and I want to access it via port <strong>9999</strong> in my local browser.</p>
<p>The command would be:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">ssh me@jerodsanto.net -N -L localhost:9999:jerodsanto.net:5678
</span></span></span></code></pre></div><p>With that command running I can visit <strong>localhost:9999</strong> in my browser and it will load the remote Resque Web app.</p>
<p>A few notes about the given flags:</p>
<ul>
<li><code>-N</code> tells SSH not to execute any commands on the machine, just connect to it</li>
<li><code>-L</code> is what tells SSH to forward traffic on the given local port to the given remote port</li>
<li>you can use <code>-f</code> if you want SSH to detach and run in a background process</li>
</ul>
<p>Oh, and one more thing. If you have this host set up in your <a href="https://jerodsanto.net/2009/05/avoid-sore-fingers-while-sshing-around/">SSH config</a>, you can use that too.</p>
<p>So, if I had created an SSH host named <strong>jms</strong>, the command would instead be:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">ssh jms -N -L localhost:9999:jms:5678
</span></span></span></code></pre></div><p>Happy tunneling!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>A Domainr CLI in Less Than 15 Lines of Ruby</title>
      <link>https://jerodsanto.net/2012/11/a-domainr-cli-in-less-than-15-lines-of-ruby/</link>
      <pubDate>Tue, 27 Nov 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/11/a-domainr-cli-in-less-than-15-lines-of-ruby/</guid>
      
      
      <description><![CDATA[<p>In 2009 I wrote a brief <a href="https://jerodsanto.net/2009/05/see-which-twitterers-dont-follow-youback-in-less-than-15-lines-of-ruby/">series</a> <a href="https://jerodsanto.net/2009/05/expand-your-twitter-network-in-less-than-15-lines-of-ruby/">of</a> <a href="https://jerodsanto.net/2009/05/create-arbitrarily-sized-files-in-less-than-15-lines-of-ruby/">posts</a> where I&rsquo;d implement something in less than 15 lines of Ruby.</p>
<p>Yesterday I took <a href="https://github.com/zachwill/dom">Dom</a>, a Python-based tool for accessing the <a href="http://domai.nr">Domainr</a> API, and rewrote it as a tiny Ruby script<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. The script turned out to be so small that I thought I could squeeze it down to less than 15 lines. I was right<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>!</p>
<p>Here it is, complete with colored output!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># encoding: utf-8</span>
</span></span><span class="line"><span class="cl"><span class="sx">%w(json open-uri)</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">lib</span><span class="o">|</span> <span class="nb">require</span> <span class="n">lib</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nb">abort</span> <span class="s2">&#34;Usage: </span><span class="si">#{</span><span class="no">File</span><span class="o">.</span><span class="n">basename</span> <span class="bp">__FILE__</span><span class="si">}</span><span class="s2"> [query]&#34;</span> <span class="k">unless</span> <span class="no">ARGV</span><span class="o">.</span><span class="n">first</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">response</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s2">&#34;http://domai.nr/api/json/search?q=</span><span class="si">#{</span><span class="no">ARGV</span><span class="o">.</span><span class="n">first</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">read</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">JSON</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">response</span><span class="p">)</span><span class="o">[</span><span class="s2">&#34;results&#34;</span><span class="o">].</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">domain</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">symbol</span> <span class="o">=</span> <span class="k">if</span> <span class="n">domain</span><span class="o">[</span><span class="s2">&#34;availability&#34;</span><span class="o">]</span> <span class="o">==</span> <span class="s2">&#34;available&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;</span><span class="se">\e</span><span class="s2">[32m✓</span><span class="se">\e</span><span class="s2">[0m&#34;</span> <span class="c1"># 32 = green</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;</span><span class="se">\e</span><span class="s2">[31m✗</span><span class="se">\e</span><span class="s2">[0m&#34;</span> <span class="c1"># 31 = red</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">symbol</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">domain</span><span class="o">[</span><span class="s1">&#39;domain&#39;</span><span class="o">]</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Drop that baby in your <code>PATH</code> and <code>chmod +x</code> it for all of your domain name reconnaissance needs.</p>]]></description>
      
      <content:encoded><![CDATA[<p>In 2009 I wrote a brief <a href="https://jerodsanto.net/2009/05/see-which-twitterers-dont-follow-youback-in-less-than-15-lines-of-ruby/">series</a> <a href="https://jerodsanto.net/2009/05/expand-your-twitter-network-in-less-than-15-lines-of-ruby/">of</a> <a href="https://jerodsanto.net/2009/05/create-arbitrarily-sized-files-in-less-than-15-lines-of-ruby/">posts</a> where I&rsquo;d implement something in less than 15 lines of Ruby.</p>
<p>Yesterday I took <a href="https://github.com/zachwill/dom">Dom</a>, a Python-based tool for accessing the <a href="http://domai.nr">Domainr</a> API, and rewrote it as a tiny Ruby script<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. The script turned out to be so small that I thought I could squeeze it down to less than 15 lines. I was right<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>!</p>
<p>Here it is, complete with colored output!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># encoding: utf-8</span>
</span></span><span class="line"><span class="cl"><span class="sx">%w(json open-uri)</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">lib</span><span class="o">|</span> <span class="nb">require</span> <span class="n">lib</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nb">abort</span> <span class="s2">&#34;Usage: </span><span class="si">#{</span><span class="no">File</span><span class="o">.</span><span class="n">basename</span> <span class="bp">__FILE__</span><span class="si">}</span><span class="s2"> [query]&#34;</span> <span class="k">unless</span> <span class="no">ARGV</span><span class="o">.</span><span class="n">first</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">response</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s2">&#34;http://domai.nr/api/json/search?q=</span><span class="si">#{</span><span class="no">ARGV</span><span class="o">.</span><span class="n">first</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">read</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">JSON</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">response</span><span class="p">)</span><span class="o">[</span><span class="s2">&#34;results&#34;</span><span class="o">].</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">domain</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">symbol</span> <span class="o">=</span> <span class="k">if</span> <span class="n">domain</span><span class="o">[</span><span class="s2">&#34;availability&#34;</span><span class="o">]</span> <span class="o">==</span> <span class="s2">&#34;available&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;</span><span class="se">\e</span><span class="s2">[32m✓</span><span class="se">\e</span><span class="s2">[0m&#34;</span> <span class="c1"># 32 = green</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;</span><span class="se">\e</span><span class="s2">[31m✗</span><span class="se">\e</span><span class="s2">[0m&#34;</span> <span class="c1"># 31 = red</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">symbol</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">domain</span><span class="o">[</span><span class="s1">&#39;domain&#39;</span><span class="o">]</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Drop that baby in your <code>PATH</code> and <code>chmod +x</code> it for all of your domain name reconnaissance needs.</p>
<p><img src="http://jerodsanto.net/drop/dom-example.png" alt=""></p>
<p>The last time around I dropped the series because I ran out of ideas. I probably won&rsquo;t pick it back up again for real unless I have a good list of things to implement. Let me know if you have any ideas :)</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Dom is really cool, but Apple keeps breaking my Python install on OS upgrades. Also, one less dependency is one less dependency.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>I had to reduce whitespace to an uncomfortable level to hit the 14 line threshold, but that includes the encoding comment, which is necessary to use the ✓ and ✗ symbols in the output.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Thankful</title>
      <link>https://jerodsanto.net/2012/11/thankful/</link>
      <pubDate>Fri, 23 Nov 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/11/thankful/</guid>
      
      
      <description><![CDATA[<p>You may not know this about me, but I used to be a pretty wretched guy. I still am a pretty wretched guy, but I used to be, too<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>I say that you may not know this about me (if you know me at all) because I&rsquo;ve always been pretty good at keeping this fact under wraps.</p>
<p>If you were to ask people that graduated from Millard North High School in 2001 what they thought of Jerod Santo you&rsquo;d probably hear answers like &ldquo;nice guy&rdquo;, &ldquo;decent guy&rdquo;, &ldquo;stand-up guy&rdquo;, et cetera.</p>]]></description>
      
      <content:encoded><![CDATA[<p>You may not know this about me, but I used to be a pretty wretched guy. I still am a pretty wretched guy, but I used to be, too<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>I say that you may not know this about me (if you know me at all) because I&rsquo;ve always been pretty good at keeping this fact under wraps.</p>
<p>If you were to ask people that graduated from Millard North High School in 2001 what they thought of Jerod Santo you&rsquo;d probably hear answers like &ldquo;nice guy&rdquo;, &ldquo;decent guy&rdquo;, &ldquo;stand-up guy&rdquo;, et cetera.</p>
<p>Admittedly, you may also hear answers like &ldquo;full of himself&rdquo;, &ldquo;thinks he&rsquo;s funnier than he is&rdquo;, and other accurate criticisms, but the overall sentiment would be that I was a &ldquo;good guy&rdquo;.</p>
<p>This couldn&rsquo;t be further from the truth.</p>
<p>I lied. I cheated. I hurt people that cared about me. I was selfish. Spiteful. Hateful.</p>
<p>I had worked so hard on building the facade of a great guy — straight A student, varsity sports, pretty girlfriend, all the clichés — that my greatest fear was to be exposed as the wretched guy that I am.</p>
<p>Christ likened the Pharisees to whitewashed tombs, which appear spotless and beautiful on the outside, but on the inside are filled with the bones of a dead man.</p>
<p>I was no Pharisee, but I was certainly pharisaical.</p>
<p>During college my facade began to crumble. The real me was showing through much more clearly than before<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. I couldn&rsquo;t hold myself together.</p>
<p>I had piled up so many secret (and some not so secret) sins throughout my life that there was no possible way that I could make things right.</p>
<div class="notice">Do you ever feel like that? Do you ever wonder why you keep screwing everything up?</div>
<p>Thankfully, a series of conspiring events — that I&rsquo;m happy to share personally if you&rsquo;re interested — led me to the only satisfactory solution to my problem.</p>
<p>God revealed to me who Jesus Christ is and how his <em>righteous</em> life was laid down in atonement for my <em>pathetic</em> life.</p>
<p>Don&rsquo;t get me wrong, I knew who Jesus Christ was previously. You don&rsquo;t grow up in the middle of America without hearing the stories about him. But up until that point they were always just stories to me. Folk lore. Fairy tales told to kids to keep their behavior in line<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.</p>
<p>The facts of Jesus Christ had never reached me<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>. Maybe it was because I had been too busy covering up the facts of Jerod Santo.</p>
<p>I was heading to destruction when God picked me up and pointed me in a new direction. Not only did he save me from that end, but he gave to me liberally, as a father gives to his son.</p>
<ul>
<li>He gave me teachers</li>
<li>He gave me spiritual understanding</li>
<li>He gave me brothers and sisters</li>
<li>He gave me deep, substantive relationships with my parents</li>
<li>He gave me a wife that is beyond compare</li>
<li>He gave me my own family</li>
<li>He gave me wisdom beyond my years</li>
<li>He gave my life a direction</li>
</ul>
<p>If it weren&rsquo;t for the God and father of our Lord Jesus Christ I would be just another guy trying to navigate the train wreck that is the human condition.</p>
<p>Instead, I get to live a life of forgiveness and grace, having my sins paid for in full.</p>
<p>And I&rsquo;m thankful.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>if this phrase structure is familiar to you, it&rsquo;s because I adapted it from a joke by the late Mitch Hedberg. Do you know the one?&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>this tends to happen a lot in college, where we&rsquo;re told to &ldquo;sow our wild oats&rdquo;, &ldquo;experiment&rdquo;, &ldquo;find out who we are&rdquo;, and other such ridiculous drivel.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>like Santa Claus or this preposterous <a href="http://en.wikipedia.org/wiki/The_Elf_on_the_Shelf">Elf on the Shelf</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>For example, the fact that after his resurrection he was seen alive by over 500 eye witnesses is not something that is commonly taught. But it should be! Just think what would&rsquo;ve happened with the OJ Simpson trial if they could&rsquo;ve produced <em>one</em> solid witness. Let alone 500 of them!&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>An Algorithm to Reliably Identify Nested Substrings</title>
      <link>https://jerodsanto.net/2012/11/an-algorithm-to-reliably-identify-nested-substrings/</link>
      <pubDate>Mon, 12 Nov 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/11/an-algorithm-to-reliably-identify-nested-substrings/</guid>
      
      
      <description><![CDATA[<p>Regular expressions are great for matching many patterns, but they are usually not ideal when matching nested structures (recursive matching).</p>
<p><em>Most</em> regular expression engines can handle a known level of recursion and <em>some</em> engines can even handle arbitrary recursion, but the required expressions are often quite complex, which makes them difficult to read, modify, and extend.</p>
<p>Instead, you can use this simple algorithm to identify nested substrings.</p>
<h2 id="the-algorithm">The Algorithm</h2>
<p>Traverse the string&rsquo;s characters one by one, applying the following logic:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Regular expressions are great for matching many patterns, but they are usually not ideal when matching nested structures (recursive matching).</p>
<p><em>Most</em> regular expression engines can handle a known level of recursion and <em>some</em> engines can even handle arbitrary recursion, but the required expressions are often quite complex, which makes them difficult to read, modify, and extend.</p>
<p>Instead, you can use this simple algorithm to identify nested substrings.</p>
<h2 id="the-algorithm">The Algorithm</h2>
<p>Traverse the string&rsquo;s characters one by one, applying the following logic:</p>
<ul>
<li>When a <strong>start delimiter</strong> is met and there is not already a <strong>start index</strong>, mark its index and increment the <strong>depth counter</strong></li>
<li>When a <strong>start delimiter</strong> is met and there is already a <strong>start index</strong>, just increment the <strong>depth counter</strong></li>
<li>When an <strong>end delimiter</strong> is met and the <strong>start index</strong> isn&rsquo;t marked, do nothing</li>
<li>When an <strong>end delimiter</strong> is met and the <strong>start index</strong> is marked, decrement the <strong>depth counter</strong></li>
<li>When an <strong>end delimiter</strong> is met, the <strong>start index</strong> is marked, and the <strong>depth counter</strong> is 0, store current <strong>start index</strong> and <strong>end index</strong>, then reset both indexes</li>
</ul>
<p>The result is a series of start/end position tuples which are used to identify and return the nested substrings. How about an example?</p>
<h2 id="an-example">An Example</h2>
<p>Let&rsquo;s say that you want to take a string of text and remove any asides in it. An aside is defined as any text inside parantheses. Asides can be nested inside one another, but the goal is to remove them all.</p>
<p>So, this string of text:</p>
<blockquote>
<p>ohai there (friend), do we (that&rsquo;s the royal we (duh!)) know what&rsquo;s up?</p>
</blockquote>
<p>Would need to match:</p>
<blockquote>
<p>(friend)</p>
</blockquote>
<p>and</p>
<blockquote>
<p>(that&rsquo;s the royal we (duh!))</p>
</blockquote>
<p>Additionally, this string of text:</p>
<blockquote>
<p>Well, 1) we should go to the store (grocery (not Hy-Vee)), and 2) we should buy stuff</p>
</blockquote>
<p>Would need to match:</p>
<blockquote>
<p>(grocery (not Hy-Vee))</p>
</blockquote>
<p>Finally, this string of text:</p>
<blockquote>
<p>This is, literally, an improper use of &ldquo;literally&rdquo;. Or is it?</p>
</blockquote>
<p>Would need to not match anything. Let&rsquo;s implement the algorithm in Ruby. We&rsquo;ll call the method <code>asides</code>.</p>
<h2 id="a-ruby-implementation">A Ruby Implementation</h2>
<p>First, we turn the spec above into executable RSpec code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">describe</span> <span class="s2">&#34;asides&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">it</span> <span class="s2">&#34;matches multiple, nested substrings&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">matches</span> <span class="o">=</span> <span class="n">asides</span> <span class="s2">&#34;ohai there (friend), do we (that&#39;s the royal we (duh!)) know what&#39;s up?&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">matches</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">should</span> <span class="o">==</span> <span class="s2">&#34;(friend)&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">matches</span><span class="o">.</span><span class="n">last</span><span class="o">.</span><span class="n">should</span> <span class="o">==</span> <span class="s2">&#34;(that&#39;s the royal we (duh!))&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">it</span> <span class="s2">&#34;does not match partial substrings&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">matches</span> <span class="o">=</span> <span class="n">asides</span> <span class="s2">&#34;Well, 1) we should go to the store (grocery (not Hy-Vee)), and 2) we should buy stuff&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">matches</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">should</span> <span class="o">==</span> <span class="s2">&#34;(grocery (not Hy-Vee))&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">matches</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">should</span> <span class="o">==</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">it</span> <span class="s2">&#34;has no matches when there are no substrings&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">matches</span> <span class="o">=</span> <span class="n">asides</span> <span class="s2">&#34;This is, literally, an improper use of &#39;literally&#39;. Or is it?&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">matches</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">should</span> <span class="o">==</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Next, we write the <code>asides</code> method, which implements the algorithm above, to make all three of these tests pass:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">asides</span><span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="n">start_delimiter</span><span class="o">=</span><span class="s2">&#34;(&#34;</span><span class="p">,</span> <span class="n">end_delimiter</span><span class="o">=</span><span class="s2">&#34;)&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">depth</span> <span class="o">=</span> <span class="n">index</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">  <span class="n">asides</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">while</span> <span class="n">index</span> <span class="o">&lt;</span> <span class="n">text</span><span class="o">.</span><span class="n">length</span>
</span></span><span class="line"><span class="cl">    <span class="n">char</span> <span class="o">=</span> <span class="n">text</span><span class="o">[</span><span class="n">index</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">char</span> <span class="o">==</span> <span class="n">start_delimiter</span>
</span></span><span class="line"><span class="cl">      <span class="n">start</span> <span class="o">||=</span> <span class="n">index</span>
</span></span><span class="line"><span class="cl">      <span class="n">depth</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">elsif</span> <span class="n">char</span> <span class="o">==</span> <span class="n">end_delimiter</span> <span class="o">&amp;&amp;</span> <span class="n">start</span>
</span></span><span class="line"><span class="cl">      <span class="n">depth</span> <span class="o">-=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="n">depth</span> <span class="o">==</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="n">asides</span> <span class="o">&lt;&lt;</span> <span class="n">text</span><span class="o">[</span><span class="n">start</span><span class="o">..</span><span class="n">index</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">        <span class="n">start</span> <span class="o">=</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">index</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">asides</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="using-the-method">Using The Method</h2>
<p>Now that the <code>asides</code> method is returning a list of substrings, we just need to loop over them and remove them from the original text. This will achieve our original goal of removing all asides. Let&rsquo;s spec and write a <code>sans_asides</code> method to do just that:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">describe</span> <span class="s2">&#34;sans_asides&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">let</span><span class="p">(</span><span class="ss">:desired_text</span><span class="p">)</span> <span class="p">{</span> <span class="s2">&#34;well isn&#39;t that neat?&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">it</span> <span class="s2">&#34;creates a string with all asides removed from the original&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">text</span> <span class="o">=</span> <span class="s2">&#34;well (now) isn&#39;t that neat (if I do say so myself (and I do))?&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">sans_asides</span><span class="p">(</span><span class="n">text</span><span class="p">)</span><span class="o">.</span><span class="n">should</span> <span class="o">==</span> <span class="n">desired_text</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">it</span> <span class="s2">&#34;creates an identical string when original string has no asides&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">sans_asides</span><span class="p">(</span><span class="n">desired_text</span><span class="p">)</span><span class="o">.</span><span class="n">should</span> <span class="o">==</span> <span class="n">desired_text</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>And the implementation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">sans_asides</span><span class="p">(</span><span class="n">text</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">asides</span><span class="p">(</span><span class="n">text</span><span class="p">)</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">aside</span><span class="o">|</span> <span class="n">text</span><span class="o">.</span><span class="n">gsub!</span> <span class="sr">/\s?</span><span class="si">#{</span><span class="no">Regexp</span><span class="o">.</span><span class="n">quote</span><span class="p">(</span><span class="n">aside</span><span class="p">)</span><span class="si">}</span><span class="sr">/</span><span class="p">,</span> <span class="s2">&#34;&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">text</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Two things to note in this code:</p>
<ol>
<li>We&rsquo;re using the returned asides in a regular expression to also remove any leading whitespaces</li>
<li>We use <code>Regexp.quote</code> so that any special characters in the aside are properly escaped</li>
</ol>
<h2 id="other-solutions">Other Solutions</h2>
<p>I think this mehod of nested substring identification is pretty solid. It is relatively easy to read and it decouples the identification step from the manipulation step.</p>
<p>But surely there are other (better?) ways to skin this cat. How would you tackle it?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Ruby Rebus</title>
      <link>https://jerodsanto.net/2012/10/ruby-rebus/</link>
      <pubDate>Wed, 31 Oct 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/10/ruby-rebus/</guid>
      
      
      <description><![CDATA[<p>One of my favorite things on the <a href="http://revision3.com/trs">Totally Rad Show</a> is their RADRebus segment. In it, Alex, Dan, and Jeff try to guess movie titles from word/picture puzzles called <a href="http://en.wikipedia.org/wiki/Rebus">rebuses</a>.</p>
<p>For the uninitiated, I&rsquo;ve embedded the first ever RADRebus episode right there ↓</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/ra2O5TyW2z8?rel=0" frameborder="0" allowfullscreen></iframe>
<p>I was watching TRS the other day and thought it&rsquo;d be fun makes some rebuses with Ruby. So I did!</p>
<h2 id="an-example-rebus">An Example Rebus</h2>
<p>If you don&rsquo;t want to watch the RADRebus video and you don&rsquo;t know what a rebus is, an example may help out.  Here is a movie rebus taken from <a href="http://www.ericharshbarger.org/">Eric Harshbarger&rsquo;s</a> list of <a href="http://www.ericharshbarger.org/epp/2006/fall/movie_rebuses_100.pdf">100 Movie Rebus Puzzles</a>.</p>]]></description>
      
      <content:encoded><![CDATA[<p>One of my favorite things on the <a href="http://revision3.com/trs">Totally Rad Show</a> is their RADRebus segment. In it, Alex, Dan, and Jeff try to guess movie titles from word/picture puzzles called <a href="http://en.wikipedia.org/wiki/Rebus">rebuses</a>.</p>
<p>For the uninitiated, I&rsquo;ve embedded the first ever RADRebus episode right there ↓</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/ra2O5TyW2z8?rel=0" frameborder="0" allowfullscreen></iframe>
<p>I was watching TRS the other day and thought it&rsquo;d be fun makes some rebuses with Ruby. So I did!</p>
<h2 id="an-example-rebus">An Example Rebus</h2>
<p>If you don&rsquo;t want to watch the RADRebus video and you don&rsquo;t know what a rebus is, an example may help out.  Here is a movie rebus taken from <a href="http://www.ericharshbarger.org/">Eric Harshbarger&rsquo;s</a> list of <a href="http://www.ericharshbarger.org/epp/2006/fall/movie_rebuses_100.pdf">100 Movie Rebus Puzzles</a>.</p>
<p>The puzzle is this:</p>
<blockquote>
<p>chiTROUBLEna</p>
</blockquote>
<p>And the answer is <a href="http://www.imdb.com/title/tt0090728/">Big Touble in Little China</a>.</p>
<p>See how that works? Good.</p>
<h2 id="the-challenge">The Challenge</h2>
<p>I have created 20 Ruby-based rebuses representing movie titles. Some are pretty easy, but others I hope will pose a challenge.</p>
<p>See if you can figure out all 20 and post your answers in the comments!</p>
<h2 id="rebus-1">Rebus #1</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;active_record&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Person</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">;</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="no">Person</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="ss">sex</span><span class="p">:</span> <span class="s2">&#34;M&#34;</span><span class="p">,</span> <span class="ss">age</span><span class="p">:</span> <span class="mi">78</span><span class="p">)</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:country</span><span class="p">)</span><span class="o">.</span><span class="n">compact</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; []</span>
</span></span></code></pre></div><h2 id="rebus-2">Rebus #2</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="sx">%w(stand sit walk run him her me you)</span> <span class="o">&amp;</span> <span class="sx">%w(put place stand lean them we me us)</span>
</span></span></code></pre></div><h2 id="rebus-3">Rebus #3</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">while</span> <span class="kp">true</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Time</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="mi">1993</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">&#34;-05:00&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nb">sleep</span> <span class="mi">86_400</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="rebus-4">Rebus #4</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">inside</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl"><span class="n">ones</span> <span class="o">=</span> <span class="sx">%w(one one one one)</span>
</span></span><span class="line"><span class="cl"><span class="n">inside</span> <span class="o">&lt;&lt;</span> <span class="n">ones</span><span class="o">.</span><span class="n">last</span>
</span></span></code></pre></div><h2 id="rebus-5">Rebus #5</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">begin</span>
</span></span><span class="line"><span class="cl">  <span class="no">Class</span><span class="o">.</span><span class="n">new</span> <span class="p">{</span> <span class="kp">private</span><span class="p">;</span> <span class="k">def</span> <span class="nf">ryan</span><span class="p">;</span> <span class="k">end</span> <span class="p">}</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">ryan</span>
</span></span><span class="line"><span class="cl"><span class="k">rescue</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="rebus-6">Rebus #6</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">movie</span><span class="p">(</span><span class="n">episode</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="n">episode</span>
</span></span><span class="line"><span class="cl">  <span class="k">when</span> <span class="mi">4</span> <span class="k">then</span> <span class="s2">&#34;Guinness&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">when</span> <span class="mi">5</span> <span class="k">then</span> <span class="s2">&#34;Prowse&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">when</span> <span class="mi">6</span> <span class="k">then</span> <span class="s2">&#34;Hamill&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="rebus-7">Rebus #7</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Batman</span><span class="o">.</span><span class="n">new</span>
</span></span></code></pre></div><h2 id="rebus-8">Rebus #8</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;singleton&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Parent</span><span class="p">;</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Child</span> <span class="o">&lt;</span> <span class="no">Parent</span><span class="p">;</span> <span class="kp">include</span> <span class="no">Singleton</span><span class="p">;</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="no">ObjectSpace</span><span class="o">.</span><span class="n">each_object</span><span class="p">(</span><span class="o">::</span><span class="no">Class</span><span class="p">)</span><span class="o">.</span><span class="n">find</span> <span class="p">{</span> <span class="o">|</span><span class="n">klass</span><span class="o">|</span> <span class="n">klass</span> <span class="o">&lt;</span> <span class="no">Parent</span>  <span class="p">}</span><span class="o">.</span><span class="n">instance</span>
</span></span></code></pre></div><h2 id="rebus-9">Rebus #9</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Murder</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">inspect</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span> <span class="nb">methods</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nb">instance_methods</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nb">ancestors</span>
</span></span><span class="line"><span class="cl">    <span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;, &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Murder</span><span class="o">.</span><span class="n">inspect</span>
</span></span></code></pre></div><h2 id="rebus-10">Rebus #10</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">[[</span><span class="s2">&#34;Switch&#34;</span><span class="p">,</span> <span class="s2">&#34;Dozer&#34;</span><span class="p">,</span> <span class="s2">&#34;Mouse&#34;</span><span class="o">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="o">[</span><span class="s2">&#34;Tank&#34;</span><span class="p">,</span> <span class="s2">&#34;Cypher&#34;</span><span class="p">,</span> <span class="s2">&#34;Apoc&#34;</span><span class="o">]]</span>
</span></span></code></pre></div><h2 id="rebus-11">Rebus #11</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="s2">&#34;otnemem&#34;</span><span class="o">.</span><span class="n">reverse</span>
</span></span></code></pre></div><h2 id="rebus-12">Rebus #12</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="s2">&#34;husband &amp; wife&#34;</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34; &amp; &#34;</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="rebus-13">Rebus #13</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="s2">&#34;fish&#34;</span><span class="o">.</span><span class="n">upcase</span>
</span></span></code></pre></div><h2 id="rebus-14">Rebus #14</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="ss">lucy</span><span class="p">:</span> <span class="s2">&#34;Ishii&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">vivica</span><span class="p">:</span> <span class="s2">&#34;Green&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">daryl</span><span class="p">:</span> <span class="s2">&#34;Driver&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">michael</span><span class="p">:</span> <span class="s2">&#34;Budd&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">david</span><span class="p">:</span> <span class="s2">&#34;Bill&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="o">.</span><span class="n">clear</span>
</span></span></code></pre></div><h2 id="rebus-15">Rebus #15</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;timecop&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Timecop</span><span class="o">.</span><span class="n">freeze</span> <span class="no">Date</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="s2">&#34;2012-12-21&#34;</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Time</span><span class="o">.</span><span class="n">now</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="rebus-16">Rebus #16</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Hash</span>
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="ss">:movie</span> <span class="ss">:fetch</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="rebus-17">Rebus #17</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;rspec&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Fowl</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:kill</span><span class="p">)</span><span class="o">.</span><span class="n">with</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="p">{</span> <span class="kp">nil</span> <span class="p">}</span>
</span></span></code></pre></div><h2 id="rebus-18">Rebus #18</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Process</span><span class="o">.</span><span class="n">kill</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">1988</span>
</span></span></code></pre></div><h2 id="rebus-19">Rebus #19</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="s2">&#34;11:59&#34;</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="s2">&#34;:&#34;</span><span class="p">,</span> <span class="s2">&#34;.&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">to_f</span><span class="o">.</span><span class="n">ceil</span>
</span></span></code></pre></div><h2 id="rebus-20">Rebus #20</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="mi">10</span><span class="o">.</span><span class="n">times</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="s2">&#34;pennies&#34;</span> <span class="p">}</span><span class="o">[</span><span class="mi">5</span><span class="o">]</span>
</span></span></code></pre></div><h2 id="bonus-challenge">Bonus Challenge!</h2>
<p>All of the movies in this list have a common bond. Can you figure out what it is?</p>
<p>(tip: if you can figure out the bonus question early on may will help you get the more difficult rebuses.)</p>
<h2 id="the-answers">The Answers</h2>
<p><strong>UPDATE:</strong> The <a href="https://jerodsanto.net/2012/11/ruby-rebus-answers/">answers post</a> is now available</p>
<p>I will reveal the answers to all 20 rebuses and the bonus challenge in a follow-up post.</p>
<p>Be sure to grab the <a href="https://jerodsanto.net/feed.xml">RSS</a>, follow along on <a href="https://twitter.com/jerodsanto">Twitter</a>, or bookmark this page so you don&rsquo;t miss the BIG reveal! ;)</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>How Atebits&#39; Letterpress Could Really Shine (and Make a Lot of Money)</title>
      <link>https://jerodsanto.net/2012/10/how-atebits-letterpress-could-really-shine-and-make-a-lot-of-money/</link>
      <pubDate>Sun, 28 Oct 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/10/how-atebits-letterpress-could-really-shine-and-make-a-lot-of-money/</guid>
      
      
      <description><![CDATA[<p>iOS users rejoiced when Loren Brichter and <a href="http://atebits.com">Atebits</a> returned to indie iOS development. <a href="http://atebits.com/letterpress">Letterpress</a>, Atebits&rsquo; latest app, is an addicting little word game (<a href="http://www.macworld.com/article/2012082/review-letterpress-for-iphone-is-an-excellent-word-game.html">review</a>).</p>
<p>Letterpess is already a success by almost any measure. It reached #16 in the App Store&rsquo;s free apps chart and managed to bring Game Center <a href="http://daringfireball.net/linked/2012/10/27/letterpress-game-center">to its knees</a> over the app&rsquo;s first week of availability.</p>
<p>But I think Letterpress has an opportunity to <em>really</em> shine.</p>
<h2 id="the-problem">The Problem</h2>
<p>Like all of Loren&rsquo;s software, Letterpress is filled with little touches and flourishes that make it a joy to play. There is one moment in the game, however, that is decidedly <em>not</em> joyous. Zach Waugh explains it in a tweet:</p>]]></description>
      
      <content:encoded><![CDATA[<p>iOS users rejoiced when Loren Brichter and <a href="http://atebits.com">Atebits</a> returned to indie iOS development. <a href="http://atebits.com/letterpress">Letterpress</a>, Atebits&rsquo; latest app, is an addicting little word game (<a href="http://www.macworld.com/article/2012082/review-letterpress-for-iphone-is-an-excellent-word-game.html">review</a>).</p>
<p>Letterpess is already a success by almost any measure. It reached #16 in the App Store&rsquo;s free apps chart and managed to bring Game Center <a href="http://daringfireball.net/linked/2012/10/27/letterpress-game-center">to its knees</a> over the app&rsquo;s first week of availability.</p>
<p>But I think Letterpress has an opportunity to <em>really</em> shine.</p>
<h2 id="the-problem">The Problem</h2>
<p>Like all of Loren&rsquo;s software, Letterpress is filled with little touches and flourishes that make it a joy to play. There is one moment in the game, however, that is decidedly <em>not</em> joyous. Zach Waugh explains it in a tweet:</p>
<blockquote class="twitter-tweet tw-align-center"><p>“Refactor” is a word @<a href="https://twitter.com/letterpressapp">letterpressapp</a>, I know it because I do it all the time! <a href="http://t.co/8xkIiDvF" title="http://cl.ly/KRqz">cl.ly/KRqz</a></p>&mdash; Zach Waugh (@zachwaugh) <a href="https://twitter.com/zachwaugh/status/261642961052389376" data-datetime="2012-10-26T01:38:37+00:00">October 26, 2012</a></blockquote>
<script src="https://jerodsanto.net//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>I&rsquo;ve been playing the game all weekend and have seen plenty of messages like this one that Zach saw:</p>
<p><img src="http://jerodsanto.net/drop/letterpress-refactor.png" alt=""></p>
<p>&ldquo;Argh!&rdquo; is right. Refactor is a completely legitimate word in software development circles.</p>
<p>In an ideal world the &ldquo;Invalid Word&rdquo; dialog would only display when you misspelled a word or were really making something up, but with how much slang and jargon we use these false positives are all too common.</p>
<div class="notice">It looks like some people have even started <a href="http://addthesewordstoletterpress.tumblr.com/">documenting</a> this <a href="http://lettermiss.com/">phenomenon</a>.</div>
<p>Pain points like this one often present opportunities. Can you see where I&rsquo;m going with this?</p>
<h2 id="the-solution">The Solution</h2>
<p>Letterpress should offer topical dictionary extensions as in-app purchases.</p>
<p>Want to play &ldquo;refactor&rdquo;? Activate the &ldquo;Software&rdquo; dictionary. Want to play &ldquo;Hoth&rdquo;? Activate the &ldquo;Star Wars&rdquo; dictionary. Want to play &ldquo;Diabetes&rdquo;? Activate the &ldquo;Medical&rdquo; dictionary.</p>
<p><em>Ad infinitum.</em></p>
<p>Here&rsquo;s one way that it could work:</p>
<p>Let&rsquo;s say that Bob has purchased the &ldquo;Medical&rdquo; dictionary. When he starts a new game he selects it (only one per game) and his opponent, Alice, is told that Bob wants to play Letterpress with her using &ldquo;Medical&rdquo; terms. Alice doesn&rsquo;t have to buy the &ldquo;Medical&rdquo; dictionary to play Bob, but she does need to if she wants to use it in games that she initiates.</p>
<h2 id="the-effect">The Effect</h2>
<ol>
<li>
<p>Users win because they get to play the game with words they know and love.</p>
</li>
<li>
<p>Letterpress wins because it stays fresh and fun for much longer than it would otherwise.</p>
</li>
<li>
<p>Atebits wins because each additional dictionary is a new source of income.</p>
</li>
</ol>
<p>I don&rsquo;t know about you, but to me that sounds like a <a href="http://www.nbc.com/the-office/video/conflict-resolution/116196">win-win-win</a>.</p>
<p>Disagree with me? Let me know in the comments or <a href="https://twitter.com/jerodsanto">on Twitter</a>. Agree with me? Let Loren know <a href="https://twitter.com/lorenb">on Twitter</a>. Maybe we can convince him that this would be worth his trouble ;)</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>A Common Gotcha When Using Ruby Regexps For Input Validation</title>
      <link>https://jerodsanto.net/2012/10/a-common-gotcha-when-using-ruby-regexps-for-input-validation/</link>
      <pubDate>Fri, 19 Oct 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/10/a-common-gotcha-when-using-ruby-regexps-for-input-validation/</guid>
      
      
      <description><![CDATA[<p>One difficulty of using <a href="http://en.wikipedia.org/wiki/Regular_expression">Regular Expressions</a> effectively is that each language has their own little idiosyncrasies that can get you in to trouble.</p>
<p>Ruby has a particularly nasty Regexp foible around start-of-string and end-of-string anchors that confuses many of us, my-previous-self included.</p>
<p>In most languages, the <code>^</code> and <code>$</code> special characters match the start and end of a <em>string</em>, respectively. You can also opt in to multi-line mode where they will instead match the start and end of a <em>line</em>.</p>]]></description>
      
      <content:encoded><![CDATA[<p>One difficulty of using <a href="http://en.wikipedia.org/wiki/Regular_expression">Regular Expressions</a> effectively is that each language has their own little idiosyncrasies that can get you in to trouble.</p>
<p>Ruby has a particularly nasty Regexp foible around start-of-string and end-of-string anchors that confuses many of us, my-previous-self included.</p>
<p>In most languages, the <code>^</code> and <code>$</code> special characters match the start and end of a <em>string</em>, respectively. You can also opt in to multi-line mode where they will instead match the start and end of a <em>line</em>.</p>
<p>In Ruby, these characters start off in multi-line mode. This is not unknown. In fact, the Rails documentation <a href="http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates_format_of">states this</a> loud and clear:</p>
<blockquote>
<p>Note: use \A and \Z to match the start and end of the string, ^ and $ match the start/end of a line.</p>
</blockquote>
<p>Unfortunately, I still see this advice ignored in many codebases. Let&rsquo;s use an ActiveRecord validation example to see why this is so dangerous.</p>
<p>I present to you a recipes web app that lets users create and share dishes. Each dish has a name, which can be a string of alphanumeric characters. The model might look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Dish</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="nb">format</span><span class="p">:</span> <span class="sr">/^[\sa-z0-9]+$/i</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>You may think that is Regexp says:</p>
<ol>
<li>start of string</li>
<li>one or more spaces or alphas or numerics</li>
<li>end of string</li>
<li>case insensitive</li>
</ol>
<p>In Perl you would be correct. In Ruby; not so much. Let&rsquo;s give this a test drive and see if we can break it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Dish</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span> <span class="s2">&#34;Best Pizza Evar&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; true</span>
</span></span></code></pre></div><p>So far so good, but that <code>$</code> only matches to the end of a line. This also works:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Dish</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span> <span class="s2">&#34;Best Pizza Evar</span><span class="se">\n</span><span class="s2"> just kidding&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; true</span>
</span></span></code></pre></div><p>That&rsquo;s not good. Users can bypass our validation by inserting arbitrary <code>\n</code> characters followed by whatever else they want. You know, like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Dish</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span> <span class="s2">&#34;Best Pizza Evar</span><span class="se">\n</span><span class="s2">&lt;script&gt;alert(&#39;pwned!&#39;);&lt;/script&gt;&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; true</span>
</span></span></code></pre></div><p>Thankfully, modern versions of Rails will auto-escape the dish name on its way out of the database before sending it down the wire, but we still don&rsquo;t want it in our database for obvious reasons.</p>
<p>This gotcha can be easily avoided if we just follow the note in the Rails docs and use <code>\A</code> and <code>\Z</code> instead:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Dish</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="nb">format</span><span class="p">:</span> <span class="sr">/\A[\sa-z0-9]+\Z/i</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Now let&rsquo;s see if the same hack will subvert our validations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Dish</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span> <span class="s2">&#34;Best Pizza Evar</span><span class="se">\n</span><span class="s2">&lt;script&gt;alert(&#39;pwned!&#39;);&lt;/script&gt;&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; false</span>
</span></span></code></pre></div><p>Crisis averted!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Skype Tip: Roll Your Own Mentions System</title>
      <link>https://jerodsanto.net/2012/10/skype-tip-roll-your-own-mentions-system/</link>
      <pubDate>Tue, 09 Oct 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/10/skype-tip-roll-your-own-mentions-system/</guid>
      
      
      <description><![CDATA[<p>Mentions (being notified only when your name is referenced) are a great way to keep the signal-to-noise ratio high in group chats. <a href="http://skype.com">Skype</a> is lame. It has no such feature. BUT!</p>
<p>You can roll your own pretty easily.</p>
<p>Skype has a handful of commands that you can issue from your client. Execute <code>/help</code> in your client and you&rsquo;ll see a list like this one:</p>
<p><a href="https://support.skype.com/en/faq/FA10042/what-are-chat-commands-and-roles"><img src="http://jerodsanto.net/drop/skype-available-commands.png" alt=""></a></p>
<p>The two commands we&rsquo;ll use for our Mentions system are <code>alertsoff</code> and <code>alertson</code>.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Mentions (being notified only when your name is referenced) are a great way to keep the signal-to-noise ratio high in group chats. <a href="http://skype.com">Skype</a> is lame. It has no such feature. BUT!</p>
<p>You can roll your own pretty easily.</p>
<p>Skype has a handful of commands that you can issue from your client. Execute <code>/help</code> in your client and you&rsquo;ll see a list like this one:</p>
<p><a href="https://support.skype.com/en/faq/FA10042/what-are-chat-commands-and-roles"><img src="http://jerodsanto.net/drop/skype-available-commands.png" alt=""></a></p>
<p>The two commands we&rsquo;ll use for our Mentions system are <code>alertsoff</code> and <code>alertson</code>.</p>
<p>First, disable all alerts for the current chat room by executing the following in your Skype window:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">/alertsoff
</span></span></span></code></pre></div><p>Then, enable alerts for strings that match your name by executing the following in your Skype window (replace &ldquo;jerod&rdquo; with your name):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">/alertson jerod
</span></span></span></code></pre></div><p>And there you have it. You will no longer be bothered by anything people are saying in that chat unless they directly reference you by name. A few details on how this works:</p>
<ol>
<li>It is case-insensitive, so &ldquo;jerod&rdquo; will match &ldquo;JEROD&rdquo; and &ldquo;Jerod&rdquo;</li>
<li>It does not match sub-strings, so &ldquo;jer&rdquo; will not match &ldquo;jerod&rdquo;</li>
<li>Each time you execute <code>alertson</code> it overwrites the previous rule</li>
<li>You can pass <code>alertson</code> many strings and it will match them all</li>
</ol>
<p>Point 4 is especially useful when you don&rsquo;t know what people will be calling you. So, to alert on all the things that people might call me, I&rsquo;d execute:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">/alertson jerod santo jer sant0sk1 studly jefe boss phatty cheetos
</span></span></span></code></pre></div><p>You get the idea&hellip;</p>
<p>Hopefully this makes your Skype experience suck a little less!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>And Now For Something So Niche That Perhaps Only I Will Use It</title>
      <link>https://jerodsanto.net/2012/09/and-now-for-something-so-niche-that-perhaps-only-i-will-use-it/</link>
      <pubDate>Thu, 20 Sep 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/09/and-now-for-something-so-niche-that-perhaps-only-i-will-use-it/</guid>
      
      
      <description><![CDATA[<p>Me, on Twitter, <a href="https://twitter.com/jerodsanto/statuses/240180907380719616">a few weeks ago</a>:</p>
<blockquote class="twitter-tweet tw-align-center"><p>My latest (soon-to-be-released) open source project is so niche that perhaps only I will use it, but I freakin’ love it.</p>&mdash; Jerod Santo (@jerodsanto) <a href="https://twitter.com/jerodsanto/status/240180907380719616" data-datetime="2012-08-27T20:16:05+00:00">August 27, 2012</a></blockquote>
<script src="https://jerodsanto.net//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>What I was referring to is <a href="http://pushpop.herokuapp.com">Push Pop</a>, a system for pushing web pages from a mobile device and having them pop open in a desktop browser. Let me explain.</p>
<p>I often find myself discovering content on my mobile device that I want to consume when I get back to my desk. Sometimes the thing is work-related. Or designed with only desktop browsers in mind. Other times I&rsquo;m in a hurry and want to return to it later.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Me, on Twitter, <a href="https://twitter.com/jerodsanto/statuses/240180907380719616">a few weeks ago</a>:</p>
<blockquote class="twitter-tweet tw-align-center"><p>My latest (soon-to-be-released) open source project is so niche that perhaps only I will use it, but I freakin’ love it.</p>&mdash; Jerod Santo (@jerodsanto) <a href="https://twitter.com/jerodsanto/status/240180907380719616" data-datetime="2012-08-27T20:16:05+00:00">August 27, 2012</a></blockquote>
<script src="https://jerodsanto.net//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>What I was referring to is <a href="http://pushpop.herokuapp.com">Push Pop</a>, a system for pushing web pages from a mobile device and having them pop open in a desktop browser. Let me explain.</p>
<p>I often find myself discovering content on my mobile device that I want to consume when I get back to my desk. Sometimes the thing is work-related. Or designed with only desktop browsers in mind. Other times I&rsquo;m in a hurry and want to return to it later.</p>
<p>I think of it as the opposite use case of how I use <a href="http://instapaper.com">Instapaper</a>.</p>
<p>Now, you may be thinking: &ldquo;Just use Chrome or Safari&rsquo;s tab syncing, dummy!&rdquo;</p>
<p>Three things about that:</p>
<ol>
<li>Neither of those things existed when I first prototyped Push Pop.</li>
<li>I use Chrome on desktop and Safari on mobile. I don&rsquo;t want lock in.</li>
<li>Tab syncing requires me to poll the browser for content. I want these things staring at me in the face when I return to my desktop.</li>
</ol>
<p>For a long time I just emailed myself links. When I got sick of that I built Push Pop instead. It works great!</p>
<iframe src="http://player.vimeo.com/video/49778930?title=0&amp;byline=0&amp;portrait=0" width="500" height="281" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
<p>Push Pop is a combination of a browser extension and a bookmarklet. The bookmarklet pushes pages up to a Heroku server which then pushes them down to the extension.</p>
<p>I&rsquo;d really like to remove the middle man, but I couldn&rsquo;t think of a way to get that done.</p>
<p>The only extension I created was for Chrome, but the whole thing is <a href="https://github.com/jerodsanto">open source</a> so if you want to use Push Pop with a different browser, please lend a hand.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Announcing Object Lateral</title>
      <link>https://jerodsanto.net/2012/09/announcing-object-lateral/</link>
      <pubDate>Tue, 04 Sep 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/09/announcing-object-lateral/</guid>
      
      
      <description><![CDATA[<p>I&rsquo;m excited to announce my first foray into business ownership!</p>
<p>Well, technically, my <em>first</em> time owning a business was when I bought 20 shares of <a href="https://www.google.com/finance?client=ob&amp;q=NYSE:KKD">KKD</a> right before the <a href="http://en.wikipedia.org/wiki/Atkins_diet#Popularity">Atkins Diet craze</a>. Here&rsquo;s hoping I do a little better this time around ;)</p>
<p>I spent six great years working on networks and writing software for <a href="http://www.rsdcompany.com">RSDi</a>, but the time has come for me to venture out and see what I can do on my own.</p>]]></description>
      
      <content:encoded><![CDATA[<p>I&rsquo;m excited to announce my first foray into business ownership!</p>
<p>Well, technically, my <em>first</em> time owning a business was when I bought 20 shares of <a href="https://www.google.com/finance?client=ob&amp;q=NYSE:KKD">KKD</a> right before the <a href="http://en.wikipedia.org/wiki/Atkins_diet#Popularity">Atkins Diet craze</a>. Here&rsquo;s hoping I do a little better this time around ;)</p>
<p>I spent six great years working on networks and writing software for <a href="http://www.rsdcompany.com">RSDi</a>, but the time has come for me to venture out and see what I can do on my own.</p>
<p>The company is called <a href="http://objectlateral.com">Object Lateral</a>. We write thoughtful, custom software.</p>
<p><a href="http://objectlateral.com"><img src="http://jerodsanto.net/drop/facebook-bg-20120831-161612.png" alt=""></a></p>
<p>For now that will mostly be software for hire, but I do have a few product ideas that I might pursue in tandem.</p>
<div class="notice">The branding &amp; design is by <a href="http://codyjamespeterson.com">Cody Peterson</a>, who is awesome (and available for contract).</div>
<p>Please take a moment to check out the site and let me know what you think!</p>
<p>We&rsquo;re also on the <a href="https://twitter.com/objectlateral">Twitter</a>, the <a href="https://github.com/objectlateral">GitHub</a>, and even the <a href="https://www.facebook.com/objectlateral">FaceBook</a> if you&rsquo;re feeling social.</p>
<p>Oh yeah, and please do get in touch if you have software needs. I&rsquo;d love to help in any way that I can!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Adding Your Own Bookmarks and Bookmarklets to the iOS Simulator&#39;s Mobile Safari</title>
      <link>https://jerodsanto.net/2012/08/adding-your-own-bookmarks-and-bookmarklets-to-the-ios-simulators-mobile-safari/</link>
      <pubDate>Wed, 29 Aug 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/08/adding-your-own-bookmarks-and-bookmarklets-to-the-ios-simulators-mobile-safari/</guid>
      
      
      <description><![CDATA[<p>Developing a bookmarklet that will run on iOS devices? You&rsquo;re probably looking for a way to test it that does not include the term &ldquo;iCloud&rdquo;.</p>
<p>The good news is that you can add your own custom bookmarks and bookmarklets to the iOS Simulator&rsquo;s static list of bookmarks for testing.</p>
<p>The bad news is that doing so is not easy like Sunday mornings.</p>
<p>The post-bad-news good news is that it&rsquo;s not too much work, either, and I will show you how!</p>]]></description>
      
      <content:encoded><![CDATA[<p>Developing a bookmarklet that will run on iOS devices? You&rsquo;re probably looking for a way to test it that does not include the term &ldquo;iCloud&rdquo;.</p>
<p>The good news is that you can add your own custom bookmarks and bookmarklets to the iOS Simulator&rsquo;s static list of bookmarks for testing.</p>
<p>The bad news is that doing so is not easy like Sunday mornings.</p>
<p>The post-bad-news good news is that it&rsquo;s not too much work, either, and I will show you how!</p>
<div class="notice">This applies to XCode 4.4.1 on OS X 10.8.1. YMMV. Please post issues in the comments.</div>
<h2 id="1-find-the-plist">1) Find the plist</h2>
<p>The Property List that holds the bookmarks is buried deep inside Xcode&rsquo;s bundle. Fire up a terminal and execute this command to navigate to its directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">cd /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.1.sdk/Applications/MobileSafari.app
</span></span></span></code></pre></div><p>Once in this directory, execute:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">ls -l *Bookmarks*
</span></span></span></code></pre></div><p>You&rsquo;ll see a handful of files, one of which is named <code>StaticBookmarks.plist</code>.</p>
<p><img src="http://jerodsanto.net/drop/staticbookmarks-20120829-125416.png" alt=""></p>
<p>That&rsquo;s our guy.</p>
<h2 id="2-get-permission">2) Get permission</h2>
<p>Xcode is owned by your system&rsquo;s <code>root</code> user, so you can&rsquo;t edit the file just yet. Ideally, you could just do a <code>sudo open StaticBookmarks.plist</code>, but that doesn&rsquo;t work anymore. I think the new file versioning stuff in Lion/Mountain Lion breaks it.</p>
<p>Another means to the same end is to take ownership of the <code>MobileSafari.app</code> directory and its files, like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">sudo chown -R `whoami` ../MobileSafari.app
</span></span></span></code></pre></div><p>This isn&rsquo;t ideal, but it works well enough. There is a decent chance that future Xcode upgrades will set the ownership back to <code>root</code>, but more likely it will directly overwrite <code>StaticBookmarks.plist</code> in which case you&rsquo;re back at square one anyhow.</p>
<h2 id="3-edit-in-xcode">3) Edit in Xcode</h2>
<p>Now that you own the file and its owning directory, Xcode will let you edit it. Open it in Xcode like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">open StaticBookmarks.plist
</span></span></span></code></pre></div><p>You should see something like this:</p>
<p><img src="http://jerodsanto.net/drop/xcode-plist-editor-20120829-130203.png" alt="Property List Editor inside Xcode"></p>
<p>Each item inside the &ldquo;Root&rdquo; key is a bookmark. To add your own, just click the + button next to the &ldquo;Root&rdquo; key and add a new Dictionary item. It should have two String keys, one for the bookmark&rsquo;s Title, and one for the bookmark&rsquo;s Contents.</p>
<p>When you&rsquo;re all done adding your own bookmark(let), it should look something like this:</p>
<p><img src="http://jerodsanto.net/drop/xcode-plist-edited-20120829-130500.png" alt="Push Pop is a little bookmarklet I&rsquo;m working on"></p>
<h2 id="4-have-at-it">4) Have at it</h2>
<p>Fire up the iOS Simulator and you should see your new bookmark there in the list with the others!</p>
<p><img src="http://jerodsanto.net/drop/ios-simulator-bookmarks-20120829-130853.png" alt=""></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>7 Ways to Level Up Your Sublime Text Editing</title>
      <link>https://jerodsanto.net/2012/08/7-ways-to-level-up-your-sublime-text-editing/</link>
      <pubDate>Fri, 17 Aug 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/08/7-ways-to-level-up-your-sublime-text-editing/</guid>
      
      
      <description><![CDATA[<h2 id="1-packages-control-them">1) Packages. Control them.</h2>
<p>Installing <a href="http://wbond.net/sublime_packages/package_control">PackageControl</a> is the first step on many Sublime Text 2 (ST2) blog posts around the web. Rightly so. It opens up a new world of functionality and makes managing that world a piece o&rsquo; cake.</p>
<p>Once you have it installed, you&rsquo;re a quick ⇧⌘P and a &ldquo;pack&rdquo; from everything you need.</p>
<p><a href="http://wbond.net/sublime_packages/package_control"><img src="http://jerodsanto.net/drop/st2-package-control-20120817-104740.png" alt="&quot;Upgrade/Overwrite All Packages&quot; is super cool"></a></p>
<h2 id="2-packages-sync-them">2) Packages. Sync them.</h2>
<p>Even with PackageControl, installing and updating packages on each of your machines is more work than it needs to be.</p>]]></description>
      
      <content:encoded><![CDATA[<h2 id="1-packages-control-them">1) Packages. Control them.</h2>
<p>Installing <a href="http://wbond.net/sublime_packages/package_control">PackageControl</a> is the first step on many Sublime Text 2 (ST2) blog posts around the web. Rightly so. It opens up a new world of functionality and makes managing that world a piece o&rsquo; cake.</p>
<p>Once you have it installed, you&rsquo;re a quick ⇧⌘P and a &ldquo;pack&rdquo; from everything you need.</p>
<p><a href="http://wbond.net/sublime_packages/package_control"><img src="http://jerodsanto.net/drop/st2-package-control-20120817-104740.png" alt="&quot;Upgrade/Overwrite All Packages&quot; is super cool"></a></p>
<h2 id="2-packages-sync-them">2) Packages. Sync them.</h2>
<p>Even with PackageControl, installing and updating packages on each of your machines is more work than it needs to be.</p>
<p>Instead of doing that, just move the <code>Packages</code> directory to your Dropbox or Google Drive and symlink it so ST2 can use it.</p>
<p>On OS X with Dropbox it&rsquo;s as easy as this:</p>
<p>On your first system:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">mv ~/Library/Application\ Support/Sublime\ Text\ 2/Packages \
</span></span></span><span class="line"><span class="cl"><span class="go">  ~/Dropbox/appsync/Sublime\ Text\ 2/Packages
</span></span></span><span class="line"><span class="cl"><span class="go">ln -s ~/Dropbox/appsync/Sublime\ Text\ 2/Packages \
</span></span></span><span class="line"><span class="cl"><span class="go">  ~/Library/Application\ Support/Sublime\ Text\ 2/Packages
</span></span></span></code></pre></div><p>On subsequent systems:\</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">rm ~/Library/Application\ Support/Sublime\ Text\ 2/Packages
</span></span></span><span class="line"><span class="cl"><span class="go">ln -s ~/Dropbox/appsync/Sublime\ Text\ 2/Packages \
</span></span></span><span class="line"><span class="cl"><span class="go">  ~/Library/Application\ Support/Sublime\ Text\ 2/Packages
</span></span></span></code></pre></div><p>The really great thing about this is that ST2 stores your preferences in a package called <code>User</code> so all of your preferences come along for the syncing ride.</p>
<h2 id="3-speaking-of-preferences">3) Speaking of preferences&hellip;</h2>
<p>One of the first things about ST2 that made me go &ldquo;ZOMG THIS IS AWESOME&rdquo; is how it natively supported a handful of small features that are a PITA to get working in TextMate (my previous editor of choice) and Vim (my forever-editor-of-second-choice).</p>
<p>IN ST2, they&rsquo;re just a boolean flag in your preferences.</p>
<p>Here they are, in JSON format of course:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;ensure_newline_at_eof_on_save&#34;</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;trim_trailing_white_space_on_save&#34;</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;translate_tabs_to_spaces&#34;</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;tab_size&#34;</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;rulers&#34;</span><span class="o">:</span> <span class="p">[</span><span class="mi">80</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The <code>ensure_newline_at_eof_on_save</code> and <code>trim_trailing_white_space_on_save</code> settings will save you (and others) endless headaches when collaborating via version control.</p>
<p>The <code>translate_tabs_to_spaces</code>, <code>tab_size</code>, and <code>ruler</code> settings will help keep your code readable. (<a href="https://jerodsanto.net/2012/07/some-white-spaces-are-more-equal-than-others/">more on that here</a>)</p>
<h2 id="4-git-with-it">4) Git with it</h2>
<p>The <a href="https://github.com/kemayo/sublime-text-2-git">git package</a> is super handy to have around. It puts all the requisite git commands at your finger tips via the Command Palette.</p>
<p>I don&rsquo;t personally use it to make commits, but where it really shines is in providing <code>git blame</code> and <code>git log</code> views in the context of the current ST2 tab.</p>
<p>Here is the result of executing <code>Git: Log Current File</code> on a project&rsquo;s <code>pricing.erb</code> file:</p>
<p><a href="https://github.com/kemayo/sublime-text-2-git"><img src="http://jerodsanto.net/drop/st2-git-log-current-file-20120817-110847.png" alt="Git: Blame works similarily"></a></p>
<p>Selecting one of the log items from the list opens the commit details in a new tab. That&rsquo;s awesome.</p>
<h2 id="5-n-views-to-a-kill">5) N Views to a Kill</h2>
<p>One of my early gripes about ST2 was that it didn&rsquo;t provide split views. Then I realized that I was completely wrong and it does provide split views! Don&rsquo;t you love when that happens?</p>
<p>You can find the settings for split views in <strong>View</strong> -&gt; <strong>Layout</strong>. There are many choices there, but all I usually need is two columns side-by-side so I&rsquo;ve trained myself on two keyboard shortcuts:</p>
<ul>
<li>⌘⌥2 — switch to the two column side-by-side layout</li>
<li>⌘⌥1 — switch to the default on column layout</li>
</ul>
<p>Once you have multiple views up you can drag &amp; drop tabs between them. You can even open the same file in multiple views, if that floats your boat.</p>
<p><img src="http://jerodsanto.net/drop/st2-split-view-same-file-20120817-120223.png" alt=""></p>
<h2 id="6-enhance-the-sidebar">6) Enhance the Sidebar</h2>
<p>The <a href="https://github.com/titoBouzout/SideBarEnhancements">SidebarEnhancements package</a> fills such a huge void in ST2&rsquo;s feature list that it should really be merged into core.</p>
<p>Not only does it add handy things like the abilities to &ldquo;close&rdquo;, &ldquo;move&rdquo;, &ldquo;rename&rdquo;, and &ldquo;delete&rdquo; files (how avant garde!), but you can also &ldquo;open with&rdquo; a managed list of external applications.</p>
<p>I like to preview my Markdown files in <a href="http://markedapp.com/">Marked</a>, which this package enables.</p>
<p><img src="http://jerodsanto.net/drop/sublime-text-open-with-marked-20120813-101112.png" alt="Marked is complements ST2 nicely"></p>
<p>If any influential ST2 people are reading this, please seriously consider getting SidebarEnhancements into ST2 proper.</p>
<h2 id="7-all-the-small-keys">7) All the small keys</h2>
<p>The best way to level up your text editing is by removing round trips to the mouse.</p>
<p>In addition to supporting many of the Operating System&rsquo;s shortcuts, ST2 has a plethora of keyboard shortcuts for you to master.</p>
<p>Here are a handful of them that I believe give you the most bang for your buck:</p>
<ul>
<li>⌘T — Goto Anything. This is TextMate&rsquo;s killer feature implemented flawlessly</li>
<li>⌘D — Quick add next. When you have a text string selected and you want to quickly select subsequent matching strings, this is your best friend. Once you have all the things you want selected you can operate on them as a group (edit, delete, upcase, etc.)</li>
<li>⌘L — Select the current line. Hold it down to contine to select subsequent lines</li>
<li>⇧⌘[ and ⇧⌘] — Navigate left and right through your open tabs, just like in Chrome/Safari</li>
<li>⌘K ⌘N — Fold code at indentation N</li>
<li>⌘K ⌘J — Unfold all code in the document</li>
<li>⌘K ⌘B — Toggle the sidebar. This is essential when working on a small screen and using split columns</li>
</ul>
<h2 id="fin">Fin</h2>
<p>Those are the big ones. There&rsquo;s also writing your own custom snippets, but that&rsquo;s a big enough conversation to warrant its own post. Subscribe to the <a href="https://jerodsanto.net/feed.xml">feed</a> or follow along on <a href="http://twitter.com/jerodsanto">Twitter</a> to be notified of that and other future posts!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>How to get quick access to technical symbols and other special characters in OS X</title>
      <link>https://jerodsanto.net/2012/08/how-to-get-quick-access-to-technical-symbols-and-other-special-characters-in-os-x/</link>
      <pubDate>Wed, 08 Aug 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/08/how-to-get-quick-access-to-technical-symbols-and-other-special-characters-in-os-x/</guid>
      
      
      <description><![CDATA[<p>Technical folks like ourselves often have to describe keyboard combinations to other (sometimes not so) technical folks. If you find yourself typing out &ldquo;command&rdquo;, &ldquo;option&rdquo;, or &ldquo;control&rdquo; often and want to take a principled stand against verbosity, I have good news for you!</p>
<p>OS X has a new feature in Mountain Lion (it may have been in Lion too, but I never used it and can no longer confirm) that provides customizable symbol substitution.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Technical folks like ourselves often have to describe keyboard combinations to other (sometimes not so) technical folks. If you find yourself typing out &ldquo;command&rdquo;, &ldquo;option&rdquo;, or &ldquo;control&rdquo; often and want to take a principled stand against verbosity, I have good news for you!</p>
<p>OS X has a new feature in Mountain Lion (it may have been in Lion too, but I never used it and can no longer confirm) that provides customizable symbol substitution.</p>
<div class="notice">You may already know this feature (and figured out how to disable it) if you&rsquo;ve ever had an inadvertent TM transformed into a ™ symbol or a (c) transformed into a © symbol.</div>
<p>To customize these substitutions, open up <em>System Preferences</em> and click on the <em>Language &amp; Text</em> icon. From there, select the <em>Text</em> tab and you&rsquo;ll see screen like this one:</p>
<p><img src="http://jerodsanto.net/drop/text-settings-before-20120808-153339.png" alt="the default text substitution settings"></p>
<p>Clicking the <strong>+</strong> button in the bottom left corner let&rsquo;s you add your own substitutions. Since I don&rsquo;t always want to convert the word &ldquo;command&rdquo; to the &amp;#x2318 symbol, I came up with a system:</p>
<ul>
<li>cmd+ converts to ⌘</li>
<li>opt+ converts to ⌥</li>
<li>ctrl+ converts to ⌃</li>
</ul>
<p>Now I have easy access to my most used technical symbols, which is pretty handy if you ask me. The end result looks like this:</p>
<p><img src="http://jerodsanto.net/drop/text-settings-after-20120808-153117.png" alt="my customized text substitution settings"></p>
<p>I&rsquo;m sure you could come up with many other uses for this, like inserting Emoji, website addresses, etc.</p>
<p>It&rsquo;s like a free, built-in little <a href="http://smilesoftware.com/TextExpander/">TextExpander</a>!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Get Sinatra Up and Running with Zurb&#39;s Foundation 3 Framework</title>
      <link>https://jerodsanto.net/2012/08/get-sinatra-up-and-running-with-zurbs-foundation-3-framework/</link>
      <pubDate>Tue, 07 Aug 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/08/get-sinatra-up-and-running-with-zurbs-foundation-3-framework/</guid>
      
      
      <description><![CDATA[<p>Many developers have become smitten with Twitter&rsquo;s <a href="http://twitter.github.com/bootstrap/">Bootstrap</a>, but I&rsquo;m personally more excited by what the <a href="http://zurb.com">Zurb</a> team has drummed up for <a href="http://foundation.zurb.com">Foundation 3</a>.</p>
<p>Foundation uses the <a href="http://compass-style.org">Compass</a>/<a href="http://sass-language.com">Sass</a> (scss) combo, which is awesome, but it takes a little work to get it to play nice with <a href="http://sinatrarb.com">Sinatra</a>.</p>
<p>I was going to write a How To blog post, but then I realized that a skeleton application that sets up the tricky bits would get people up and running even faster.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Many developers have become smitten with Twitter&rsquo;s <a href="http://twitter.github.com/bootstrap/">Bootstrap</a>, but I&rsquo;m personally more excited by what the <a href="http://zurb.com">Zurb</a> team has drummed up for <a href="http://foundation.zurb.com">Foundation 3</a>.</p>
<p>Foundation uses the <a href="http://compass-style.org">Compass</a>/<a href="http://sass-language.com">Sass</a> (scss) combo, which is awesome, but it takes a little work to get it to play nice with <a href="http://sinatrarb.com">Sinatra</a>.</p>
<p>I was going to write a How To blog post, but then I realized that a skeleton application that sets up the tricky bits would get people up and running even faster.</p>
<p>So, I created <a href="https://github.com/jerodsanto/sinatra-foundation-skeleton">sinatra-foundation-skeleton</a> for anybody interested in pairing these two power tools.</p>
<p>Oh, and it&rsquo;s wired up to easily deploy to <a href="http://heroku.com">Heroku</a>. If you&rsquo;re into that sort of thing.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Some White Spaces are More Equal Than Others</title>
      <link>https://jerodsanto.net/2012/07/some-white-spaces-are-more-equal-than-others/</link>
      <pubDate>Mon, 23 Jul 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/07/some-white-spaces-are-more-equal-than-others/</guid>
      
      
      <description><![CDATA[<p>Let&rsquo;s not argue about tabs vs. spaces. Let&rsquo;s argue about spaces vs. spaces!</p>
<p>Some developers use four spaces to represent one unit of indentation. Others prefer two spaces instead. Which is better? Is there such a thing as better, or is it <em>just</em> a matter of taste?</p>
<div class="notice">I&rsquo;ve heard rumor of some crazies who use eight spaces for indentation, but I&rsquo;m fortunate enough not to have encounterd one in the wild.</div>
<p>I submit that the correct answer is almost always two spaces, with one exception where four spaces wins out. Here&rsquo;s why.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Let&rsquo;s not argue about tabs vs. spaces. Let&rsquo;s argue about spaces vs. spaces!</p>
<p>Some developers use four spaces to represent one unit of indentation. Others prefer two spaces instead. Which is better? Is there such a thing as better, or is it <em>just</em> a matter of taste?</p>
<div class="notice">I&rsquo;ve heard rumor of some crazies who use eight spaces for indentation, but I&rsquo;m fortunate enough not to have encounterd one in the wild.</div>
<p>I submit that the correct answer is almost always two spaces, with one exception where four spaces wins out. Here&rsquo;s why.</p>
<h2 id="common-ground">Common ground</h2>
<p>The underlying principle is that code is written once and read many times, therefore the goal of the writer is to optimize for legibility.</p>
<p>If you don&rsquo;t share this goal with the rest of us, you can stop reading and go back to your day job. Also, I hope your day job does not involve writing code!</p>
<h2 id="the-de-facto-is-two-spaces">The de facto is two spaces</h2>
<p>We all learned in Legibility 101 that there is a point at which squeezing more characters on the same line makes the text harder to read. If you played hookie that day, just find a fluid width website and stretch your browser horizontally as far as you can. Now try to read the text. Harder, huh?</p>
<p>The form factor of printed books has been optimized for decades and the verdict is in: ~60 characters per line is the sweet spot.</p>
<p>This reason — not the oft cited reason of accounting for legacy technologies — is why I believe that having an 80 character per line limit on your code is a good idea.</p>
<div class="notice">This constraint introduces other benefits, such as 1) easily fitting two columns of code side-by-side in your editor and 2) providing a nice warning sign of code complexity.</div>
<p>But we need to be able to express ourselves inside those 80 characters and indentation takes up precious real estate. Therefore, the ideal number of white spaces used for indentation is the minimum number that we can get away with while still being recognizable: hence, two spaces for indentation is my de facto standard.</p>
<p>No standard would be de facto if there weren&rsquo;t exceptions, and I believe we have one exception to carefully consider.</p>
<h2 id="the-exception-is-four-spaces">The exception is four spaces</h2>
<p>When it comes to white space, there are two kinds of programming languages: ones in which white space is insignificant and ones in which white space is significant.</p>
<div class="notice">Okay, so there&rsquo;s a third kind too. <a href="http://en.wikipedia.org/wiki/Whitespace_(programming_language)">Whitespace</a>, in which the white spaces <em>are</em> the programming language. But when cheese stands all alone it&rsquo;s usually because it smells funky.</div>
<p>Popular languages with significant white space include <a href="http://www.python.org/">Python</a>, <a href="http://www.haskell.org/haskellwiki/Haskell">Haskell</a>, <a href="http://coffeescript.org/">CoffeeScript</a>, and <a href="http://sass-lang.com/">Sass</a>.</p>
<p>These languages rely on white space in their very syntaxes and don&rsquo;t have other means of indentation. This makes it very reasonable, and even preferrable, to let their significant white space be more <em>significant</em> in form than in other languages.</p>
<p>White space significant languages get four spaces per unit of indentation.</p>
<h2 id="painting-the-ole-bike-shed">Painting the ole&rsquo; bike shed?</h2>
<p>Yeah, maybe so. But it&rsquo;s one of those decisions that all developers have to deal with so it&rsquo;s nice to have a system that makes sense. Your proverbial mileage may vary.</p>
<p>Oh, and one more thing. When you are contributing to somebody else&rsquo;s project, just swallow your pride and adopt whatever style they prefer. It could be the difference in getting your contributions accepted or not.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>You Might be a Unix Geek</title>
      <link>https://jerodsanto.net/2012/07/you-might-be-a-unix-geek/</link>
      <pubDate>Sat, 14 Jul 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/07/you-might-be-a-unix-geek/</guid>
      
      
      <description><![CDATA[<p><strong>If you&rsquo;re not disgusted by the thought of piping things in to or out of a cat&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>If you commonly instruct people to ask &ldquo;Your friend, Manuel&rdquo;&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>If naming software projects using a recursive acronym really trips your trigger&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>Related: if you&rsquo;ve ever laughed out loud at a joke which requires a thorough understanding and deep appreciation of the concept of recursion&hellip;</strong></p>]]></description>
      
      <content:encoded><![CDATA[<p><strong>If you&rsquo;re not disgusted by the thought of piping things in to or out of a cat&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>If you commonly instruct people to ask &ldquo;Your friend, Manuel&rdquo;&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>If naming software projects using a recursive acronym really trips your trigger&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>Related: if you&rsquo;ve ever laughed out loud at a joke which requires a thorough understanding and deep appreciation of the concept of recursion&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>If you&rsquo;ve long since forgotten that your coffee mug has <em>vi</em> commands on it&hellip;</strong></p>
<p><img src="http://jerodsanto.net/drop/mug-of-vi-20120713-084959.png" alt="this is not my wife&rsquo;s favorite coffee mug"></p>
<p>You might be a Unix Geek.</p>
<p><strong>If you maintain an SSH link to you parents&rsquo; home network&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>If you own a t-shirt that says &ldquo;No, I won&rsquo;t fix your computer&rdquo;, but you can&rsquo;t find it anymore&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>If you&rsquo;re judging me because the <em>actual</em> shirt says &ldquo;No, I <em>will not</em> fix your computer&rdquo; and you know that for a fact because you&rsquo;re wearing it right now&hellip;</strong></p>
<p>You might be a Unix Geek and a pedant. ;)</p>
<p><strong>If your preferred Twitter client has a command line interface&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>If you have to constantly remind yourself that RTFM is not a socially acceptable answer&hellip;</strong></p>
<p><img src="http://jerodsanto.net/drop/rtfm-you-must-20120714-085811.png" alt="rtfm"></p>
<p>You might be a Unix Geek.</p>
<p><strong>If you&rsquo;ve ever lectured somebody on the virtues of considering everything as a file&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>If the phrase &ldquo;less is more&rdquo; invokes your thoughts on the pros and cons of different text pagers&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>If you can enumerate the differences between OpenBSD, FreeBSD, and NetBSD&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>If &ldquo;tar czvf&rdquo; makes any sense to you at all&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>If you&rsquo;ve ever made one of your friends telnet to towel.blinkenlights.nl&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>If you have &ldquo;emacs rules&rdquo; tattooed across your fingers&hellip;</strong></p>
<a href="http://www.flickr.com/photos/rore/4457009838/">
  <img src="http://farm5.staticflickr.com/4011/4457009838_382885ee5b.jpg" width="500" height="177" alt="What's the one about emacs and how many fingers you need to use it?">
</a>
<p>You might be a Unix Geek. And a mutant.</p>
<p><strong>If you have a server in your employ with more than 3 years of uptime&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p><strong>If your notes, to-dos, grocery list, essays, and email are all stored as plain text&hellip;</strong></p>
<p>You might be a Unix Geek.</p>
<p>And finally&hellip;</p>
<p><strong>If while reading this you felt compelled to point out that most of these jokes don&rsquo;t relate directly to Unix, but instead to &lsquo;Unix-like&rsquo; derivatives such as Linux and associated software such as the suite of Gnu utilities which, for my information, are expressly NOT Unix even in their very name&hellip;</strong></p>
<p>Congratulations, you&rsquo;re a HUGE Unix Geek! ;)</p>
<hr>
<p>Surely I&rsquo;m neither the funniest nor the biggest Unix Geek in the room. Please, add your own in the comments or <a href="https://twitter.com/jerodsanto">tweet</a> them to me and I&rsquo;ll post them here!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>My Favorite Bundler Feature</title>
      <link>https://jerodsanto.net/2012/07/my-favorite-bundler-feature/</link>
      <pubDate>Tue, 03 Jul 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/07/my-favorite-bundler-feature/</guid>
      
      
      <description><![CDATA[<p>If you&rsquo;re a Ruby developer — and you haven&rsquo;t been too busy starring in <a href="http://www.youtube.com/watch?v=cvXqm0RdJms">Geico commercials</a> — you are familiar with <a href="http://gembundler.com/">Bundler</a>.</p>
<p>Bundler solved a real pain point in the lives of Ruby devs: dependency management. It was a rocky road, but we eventually got there and I think it&rsquo;s safe to say that we&rsquo;re all better off with Bundler than we were without it.</p>
<div class="notice">I&rsquo;d like to thank everybody who has contributed to Bundler. Your work is much appreciated.</div>
<p>Having used Bundler long enough to take its dependency management feature for granted (<em>snark</em>), I&rsquo;ve come to know and love another feature of Bundler: <code>bundle open</code></p>]]></description>
      
      <content:encoded><![CDATA[<p>If you&rsquo;re a Ruby developer — and you haven&rsquo;t been too busy starring in <a href="http://www.youtube.com/watch?v=cvXqm0RdJms">Geico commercials</a> — you are familiar with <a href="http://gembundler.com/">Bundler</a>.</p>
<p>Bundler solved a real pain point in the lives of Ruby devs: dependency management. It was a rocky road, but we eventually got there and I think it&rsquo;s safe to say that we&rsquo;re all better off with Bundler than we were without it.</p>
<div class="notice">I&rsquo;d like to thank everybody who has contributed to Bundler. Your work is much appreciated.</div>
<p>Having used Bundler long enough to take its dependency management feature for granted (<em>snark</em>), I&rsquo;ve come to know and love another feature of Bundler: <code>bundle open</code></p>
<p>What does <code>open</code> do? It opens a bundled gem in your editor. Simple as that.</p>
<p>Why is that awesome? Because it is now easier than ever to go code spelunking inside your project&rsquo;s dependencies.</p>
<p><img src="http://jerodsanto.net/drop/spelunking-20120703-073554.png" alt="Where did I put that stack trace?"></p>
<p>Need to know which methods <a href="https://github.com/ryanb/cancan/">CanCan</a> adds to ActionController?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">bundle open cancan
</span></span></span></code></pre></div><p>Want to toss some debug output inside an ActiveRecord query chain?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">bundle open activerecord
</span></span></span></code></pre></div><p>Would it just be easier to throw a <code>binding.pry</code> right inside <a href="http://mongoid.org">Mongoid&rsquo;s</a> call chain? (Pro tip: yes, yes it would)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">bundle open mongoid
</span></span></span></code></pre></div><p>In a perfect world, third party libraries that our projects depend upon would be like little black boxes that Just Work™ as advertised. In real life, we often need to rip open those black boxes and tinker with their innards.</p>
<p><code>bundle open</code> reduces the friction of diving into my dependencies, which means I do it sooner and more often.</p>
<p>And <em>THAT</em>&rsquo;s why it&rsquo;s my favorite feature in Bundle (besides the whole dependency management feature… that&rsquo;s great too).</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Wrangle Your iOS Apps</title>
      <link>https://jerodsanto.net/2012/06/wrangle-your-ios-apps/</link>
      <pubDate>Thu, 28 Jun 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/06/wrangle-your-ios-apps/</guid>
      
      
      <description><![CDATA[<p>We&rsquo;ve all been there.</p>
<p>Well, we probably haven&rsquo;t <em>all</em> been there, but the few of us who haven&rsquo;t can quietly ask one of us what it&rsquo;s like.</p>
<p>Where was I? Ahh, Yes. We&rsquo;ve all been there.</p>
<p>When time and curiosity set counsel together against you and your iOS device is suddenly teeming with apps.</p>
<p><img src="http://jerodsanto.net/drop/south-dakota-bison-herd-20120628-105947.png" alt="Your apps as South Dakotan Bison"></p>
<p>Apps for this. Apps for that. Apps for you-don&rsquo;t-know-what-anymore. Apps that are flat out amazing. Apps that are kinda meh. Apps that you&rsquo;re really just keeping installed in case they release an update with that feature you really want.</p>]]></description>
      
      <content:encoded><![CDATA[<p>We&rsquo;ve all been there.</p>
<p>Well, we probably haven&rsquo;t <em>all</em> been there, but the few of us who haven&rsquo;t can quietly ask one of us what it&rsquo;s like.</p>
<p>Where was I? Ahh, Yes. We&rsquo;ve all been there.</p>
<p>When time and curiosity set counsel together against you and your iOS device is suddenly teeming with apps.</p>
<p><img src="http://jerodsanto.net/drop/south-dakota-bison-herd-20120628-105947.png" alt="Your apps as South Dakotan Bison"></p>
<p>Apps for this. Apps for that. Apps for you-don&rsquo;t-know-what-anymore. Apps that are flat out amazing. Apps that are kinda meh. Apps that you&rsquo;re really just keeping installed in case they release an update with that feature you really want.</p>
<p>You get the point: <em>too many apps</em>.</p>
<p>Combatting app bloat can be difficult when you don&rsquo;t have a system, and I have a feeling that many of us end up going the &ldquo;binge and purge&rdquo; route. That&rsquo;s no good, because the only thing that leaves a worse taste in your mouth than binging is purging.</p>
<p><img src="http://jerodsanto.net/drop/spew-into-this-20120628-094054.png" alt="If you&rsquo;re gonna spew, spew into this"></p>
<p>Good news! I&rsquo;ve discovered a great way to keep our iOS devices trim and loaded with only the BEST apps. Better news! It includes a <a href="http://flickchart.com">FlickChart</a> style VS!</p>
<h2 id="step-1--set-a-limit">Step 1 — Set a limit</h2>
<p>The easiest way to do this is to only let yourself have <em>N</em> screens of apps and <em>J</em> folders. You&rsquo;ll want to find your own sweet spot, but my personal limit is two screens and two folders. One folder is for all of the Apple <strike>shovelware</strike> apps and the other folder is for rarely used, essential utilities.</p>
<p>This may sound pretty limiting (and in practice it is — there are a lot of good iOS apps), but two screens and two folders allows for 58 total apps on my device. That&rsquo;s plenty.</p>
<h2 id="step-2--have-your-fill">Step 2 — Have your fill</h2>
<p><img src="http://jerodsanto.net/drop/kid-in-candy-store-20120628-103323.png" alt="Does the App Store make you feel like this?"></p>
<p>This step is so easy that you&rsquo;ve probably already done it. You just install apps to your heart&rsquo;s delight until you&rsquo;ve maxed out your screen/folder limits set in Step 1.</p>
<p>If your device is already loaded up with apps, you&rsquo;ll get a big dose of Step 3 right away.</p>
<h2 id="step-3--versus">Step 3 — Versus</h2>
<p>Whenever you download an app that pushes you over your self-imposed limit you have a decision to make. Does this new app make the cut or are all of your current apps better? Which, if any, of your apps do you not use much anymore? Can any of them be replaced by this?</p>
<p>Or, just think of your least favorite app on your device (that Apple will actually let you delete). Face it off with this new app. Two apps enter. One app is left standing.</p>
<p>This feels cumbersome at first, but it&rsquo;s actually kind of fun and makes you think critically about the software that you use. The key is to not let slip or the whole system dissolves and you end up back where you started.</p>
<p><img src="http://jerodsanto.net/drop/kid-eating-cake-20120628-103101.png" alt="It seemed like a good idea at the time"></p>
<h2 id="the-result">The result</h2>
<p>I&rsquo;ve been doing this for a few months and I&rsquo;m surprised how much more I enjoy my iPhone these days.
It&rsquo;s topped up with best-of-breed apps and since I&rsquo;m already at my app limit I don&rsquo;t go seeking out new apps anymore.</p>
<p>Give it a try and let me know if it works for you as well as it does for me!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>3 Tools I Install on Every Server</title>
      <link>https://jerodsanto.net/2012/06/3-tools-i-install-on-every-server/</link>
      <pubDate>Thu, 14 Jun 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/06/3-tools-i-install-on-every-server/</guid>
      
      
      <description><![CDATA[<p>Over the years, I&rsquo;ve found a few tools so invaluable for managing *nix servers that I end up installing them on every machine under my employ.</p>
<p>They are, in no particular order:</p>
<h2 id="htop">htop</h2>
<p><code>htop</code> is what <code>top</code> looks like after she puts on her dancing shoes on a Friday night. Basically <code>htop</code> turns this:</p>
<p><img src="http://jerodsanto.net/drop/top-20120614-160039.png" alt="top"></p>
<p>into this:</p>
<p><img src="http://jerodsanto.net/drop/htop-20120614-155945.png" alt="htop"></p>
<p>It&rsquo;s such an improvement over plain ole&rsquo; <code>top</code> — with no known downsides — that I have an alias in my shell:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Over the years, I&rsquo;ve found a few tools so invaluable for managing *nix servers that I end up installing them on every machine under my employ.</p>
<p>They are, in no particular order:</p>
<h2 id="htop">htop</h2>
<p><code>htop</code> is what <code>top</code> looks like after she puts on her dancing shoes on a Friday night. Basically <code>htop</code> turns this:</p>
<p><img src="http://jerodsanto.net/drop/top-20120614-160039.png" alt="top"></p>
<p>into this:</p>
<p><img src="http://jerodsanto.net/drop/htop-20120614-155945.png" alt="htop"></p>
<p>It&rsquo;s such an improvement over plain ole&rsquo; <code>top</code> — with no known downsides — that I have an alias in my shell:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[[</span> -x <span class="sb">`</span>which htop<span class="sb">`</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span> <span class="nb">alias</span> <span class="nv">top</span><span class="o">=</span><span class="s2">&#34;htop&#34;</span><span class="p">;</span> <span class="k">fi</span>
</span></span></code></pre></div><p>To install <code>htop</code> from repos:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Debian</span>
</span></span><span class="line"><span class="cl">$ aptitude install htop
</span></span><span class="line"><span class="cl"><span class="c1"># Arch</span>
</span></span><span class="line"><span class="cl">$ pacman -S htop
</span></span><span class="line"><span class="cl"><span class="c1"># OS X</span>
</span></span><span class="line"><span class="cl">brew install htop-osx
</span></span></code></pre></div><h2 id="ack">ack</h2>
<p><code>ack</code> is a text search tool akin to <code>grep</code>, but with some pretty distinct advantages:</p>
<ul>
<li>fast — it only searches what makes sense to search</li>
<li>defaults to recursive searches</li>
<li>defaults to colored output</li>
<li>defaults to show line numbers of matched strings</li>
<li>takes fewer keystrokes</li>
<li>uses Perl&rsquo;s powerful regular expressions</li>
</ul>
<p>Needless to say, it&rsquo;s <a href="http://betterthangrep.com/">better than grep</a>.</p>
<p>To install <code>ack</code> from repos:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Debian</span>
</span></span><span class="line"><span class="cl">$ aptitude install ack-grep
</span></span><span class="line"><span class="cl">$ ln -s /usr/bin/ack-grep /usr/bin/ack
</span></span><span class="line"><span class="cl"><span class="c1"># Arch</span>
</span></span><span class="line"><span class="cl">$ pacman -S ack
</span></span><span class="line"><span class="cl"><span class="c1"># OS X</span>
</span></span><span class="line"><span class="cl">$ brew install ack
</span></span></code></pre></div><h2 id="tree">tree</h2>
<p><code>tree</code> is a great way to wrap your head around a directory structure instead of <code>cd</code>ing and <code>ls</code>ing all over the place.</p>
<p><code>tree</code>&rsquo;s output looks like this:</p>
<p><img src="http://jerodsanto.net/drop/tree-20120614-161039.png" alt="tree"></p>
<p>The output can be a bit overwhelming for directories with many files and subdirectories, but it can be easily piped to <code>less</code> so you can page and navigate it.</p>
<p>To install <code>tree</code> from repos:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Debian</span>
</span></span><span class="line"><span class="cl">$ aptitude install tree
</span></span><span class="line"><span class="cl"><span class="c1"># Arch</span>
</span></span><span class="line"><span class="cl">$ pacman -S tree
</span></span><span class="line"><span class="cl"><span class="c1"># OS X</span>
</span></span><span class="line"><span class="cl">$ brew install tree
</span></span></code></pre></div><h2 id="quid-pro-quo">quid pro quo</h2>
<p>What about you?</p>
<p>Do you have any tools that don&rsquo;t ship with your server operating system of choice, but you just can&rsquo;t live without?</p>
<p>If so, I probably can&rsquo;t live without them either and just don&rsquo;t know it yet. Please share :)</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>A Brief Message to Web Designers</title>
      <link>https://jerodsanto.net/2012/06/a-brief-message-to-web-designers/</link>
      <pubDate>Thu, 07 Jun 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/06/a-brief-message-to-web-designers/</guid>
      
      
      <description><![CDATA[<p>Dear web designers,</p>
<p>First, I want you to know that I admire your work. Really, I do.</p>
<p>I&rsquo;m also a little jealous of your skills. There&rsquo;s nothing more frustrating than having design taste, but lacking what the French call a certain&hellip; I-don&rsquo;t-know-what to turn taste into tangible work product. That&rsquo;s where I live every day. But enough about me. This message is for you.</p>
<p>Every time I check out a newly launched website I do the following:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Dear web designers,</p>
<p>First, I want you to know that I admire your work. Really, I do.</p>
<p>I&rsquo;m also a little jealous of your skills. There&rsquo;s nothing more frustrating than having design taste, but lacking what the French call a certain&hellip; I-don&rsquo;t-know-what to turn taste into tangible work product. That&rsquo;s where I live every day. But enough about me. This message is for you.</p>
<p>Every time I check out a newly launched website I do the following:</p>
<ol>
<li>Open it in Chrome</li>
<li>See how it looks &amp; works</li>
<li>Drag my Chrome window to as skinny as possible</li>
<li>See how it looks &amp; works</li>
<li>Judge the site</li>
</ol>
<p>Sadly, more often than not I see an otherwise great website that totally fails its mobile users.</p>
<p>Guys and gals. It&rsquo;s 2012 and the mobile web is here in a big way. Just look at this pie chart from <a href="http://allthingsd.com/20120525/mobile-devices-now-make-up-about-20-percent-of-u-s-web-traffic/">All Things D</a> back in May:</p>
<p><a href="http://allthingsd.com/20120525/mobile-devices-now-make-up-about-20-percent-of-u-s-web-traffic/"><img src="http://jerodsanto.net/drop/mobile-share-of-web-traffic-20120607-112145.png" alt="Mobile Web Chart"></a></p>
<p>That&rsquo;s 20%, y&rsquo;all. A big chunk. And it&rsquo;s just going to keep getting bigger every day.</p>
<p><strong>If you&rsquo;re not designing websites that adapt to mobile browser sizes, you need to be.</strong></p>
<p>If you don&rsquo;t have the skills, <a href="http://www.abookapart.com/products/responsive-web-design">get them</a>. If you need tools, they&rsquo;re <a href="http://foundation.zurb.com/">out</a> <a href="http://gumbyframework.com">there</a>. If your clients/bosses need convincing to invest a little extra, convince them.</p>
<p>Users will benefit. Site owners will benefit. You will benefit.</p>
<p>I don&rsquo;t know about you, but to me that sounds like a <a href="http://www.nbc.com/the-office/video/conflict-resolution/116196">win-win-win</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>the birth, life, and death of sant0sk1</title>
      <link>https://jerodsanto.net/2012/06/the-birth-life-and-death-of-sant0sk1/</link>
      <pubDate>Tue, 05 Jun 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/06/the-birth-life-and-death-of-sant0sk1/</guid>
      
      
      <description><![CDATA[<p>There was a brief period in high school when I was BIG in to the WWF.</p>
<p>It was a great time to be a wrestling fan. Stone Cold Steve Austin was chugging beers and delivering <a href="http://www.youtube.com/watch?v=oUqDCKzHTsI">Stunners</a>, Degeneration X was regularly insisting that their opponents <a href="http://www.youtube.com/watch?v=snW-5AHxYTA">Suck It</a>, and The Rock was the <a href="http://www.youtube.com/watch?v=zeTLK2DsBDc">People&rsquo;s Champ</a> (can you smell it?).</p>
<p><img src="http://jerodsanto.net/drop/peoples-champ-20120605-170058.png" alt="The People&rsquo;s Champ"></p>
<p>During this less-discerning-phase of my life, a Classy wrestler by the name of Val Venis burst onto the scene. The ladies loved him. Well, at least that&rsquo;s what he kept repeating every time he got his paws on the mic.</p>]]></description>
      
      <content:encoded><![CDATA[<p>There was a brief period in high school when I was BIG in to the WWF.</p>
<p>It was a great time to be a wrestling fan. Stone Cold Steve Austin was chugging beers and delivering <a href="http://www.youtube.com/watch?v=oUqDCKzHTsI">Stunners</a>, Degeneration X was regularly insisting that their opponents <a href="http://www.youtube.com/watch?v=snW-5AHxYTA">Suck It</a>, and The Rock was the <a href="http://www.youtube.com/watch?v=zeTLK2DsBDc">People&rsquo;s Champ</a> (can you smell it?).</p>
<p><img src="http://jerodsanto.net/drop/peoples-champ-20120605-170058.png" alt="The People&rsquo;s Champ"></p>
<p>During this less-discerning-phase of my life, a Classy wrestler by the name of Val Venis burst onto the scene. The ladies loved him. Well, at least that&rsquo;s what he kept repeating every time he got his paws on the mic.</p>
<p><img src="http://jerodsanto.net/drop/venis-20120605-140149.png" alt="Val Venis"></p>
<p>Val Venis&rsquo;s shtick cracked me up, so I would often quote him (&ldquo;Hello Ladies&rdquo;) at football practices, beer die games, or pretty much anywhere that I had a male audience.</p>
<p>Val Venis often referred to himself as the Big Valboski, and I imitated the guy often enough that a few of my friends began calling me the Big Santoski. That name was either too verbose or I wasn&rsquo;t big enough, because it was soon shortened to Santoski, Toski, or even just Tosk by those into the whole brevity thing.</p>
<p>Somehow the name stuck. Nicknames work like that. You don&rsquo;t pick a nickname. It picks you.</p>
<p>When I finally needed a handle on the Internet — I didn&rsquo;t own a computer until I was 17 and even then I rarely used the Internet —  I couldn&rsquo;t think of anything awesome so I just went with what people already called me. I threw a few 1337 vowel replacements in there for good measure and have been sant0sk1 ever since.</p>
<p>Until today.</p>
<p>Today I&rsquo;m putting sant0sk1 to death. Murderous? I suppose. Tragic? Nah. But you may be wondering why.</p>
<p>I&rsquo;ll tell you why!</p>
<ul>
<li>
<p><strong>It&rsquo;s not objectively awesome</strong> — some handles are so cool that they should never be abandoned. <a href="https://twitter.com/notch">Notch</a>, <a href="https://twitter.com/mechanical_fish">mechanical_fish</a>, and <a href="https://twitter.com/tenderlove">tenderlove</a> come to mind. sant0sk1 isn&rsquo;t <em>bad</em>, but it&rsquo;s nowhere near an untouchable level of cool</p>
</li>
<li>
<p><strong>People can&rsquo;t pronounce it</strong> — Some people who have only ever read my handle online think it&rsquo;s pronounced sant-zero-s-k-one. Weird, I know, but it happens more than you&rsquo;d think. This makes for first IRL encounters of the awkward kind.</p>
</li>
<li>
<p><strong>I&rsquo;m not Polish</strong> — I didn&rsquo;t even know this was a thing, but it turns out that the &ldquo;ski&rdquo; suffix is common amongst Polish names. There&rsquo;s only two things I hate in this world. People who are intolerant of other people&rsquo;s cultures and the Polish.</p>
</li>
<li>
<p><strong>My name is my name</strong> — As a contract software developer, I travel on my name. Anything that disconnects my work from my real name could be detrimental to that purpose.</p>
</li>
</ul>
<p>From now on I&rsquo;m just going to be boring like so many others and use my full name. I&rsquo;ve already changed my username on my most frequented accounts: <a href="https://twitter.com/jerodsanto">Twitter</a> and <a href="https://github.com/jerodsanto">GitHub</a>. The others will follow as time permits.</p>
<p>Good bye, sant0sk1. It was good when it was good.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Back That Gmail Up</title>
      <link>https://jerodsanto.net/2012/05/back-that-gmail-up/</link>
      <pubDate>Sat, 26 May 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/05/back-that-gmail-up/</guid>
      
      
      <description><![CDATA[<p>I&rsquo;m not leery of Google shuttering its Gmail service, but I can imagine a scenario where they cut off <em>my</em> access to Gmail and I have no way to plead my case to the search behemoth. So I went looking for a Gmail backup solution to put my mind at ease.</p>
<p>There are many options out there, but I ended up using <a href="http://bagoma.sourceforge.net/">BaGoMa</a> with much success. Things that turned me on to BaGoMa:</p>]]></description>
      
      <content:encoded><![CDATA[<p>I&rsquo;m not leery of Google shuttering its Gmail service, but I can imagine a scenario where they cut off <em>my</em> access to Gmail and I have no way to plead my case to the search behemoth. So I went looking for a Gmail backup solution to put my mind at ease.</p>
<p>There are many options out there, but I ended up using <a href="http://bagoma.sourceforge.net/">BaGoMa</a> with much success. Things that turned me on to BaGoMa:</p>
<ol>
<li>People were raving about it on a forum</li>
<li>Python is already on all of my machines</li>
<li>I can read Python pretty well and it looked well written</li>
<li>Its flags are plentiful and documented</li>
<li>It works with Google Apps for Domains</li>
<li>It works with multiple accounts</li>
<li>It respects Gmail&rsquo;s labels</li>
<li>It uses IMAP smartly</li>
<li>It can run headless and be scheduled with cron</li>
<li>It&rsquo;s hosted on SourceForge, just kidding that was weird and scary</li>
</ol>
<p>After installing BaGoMa on my backup server — a.k.a. The iMac in our bedroom — I wrote a little shell script to set the options and run the backup on all of our Gmail accounts:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/sh
</span></span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Backing up Jerod&#39;s email&#34;</span>
</span></span><span class="line"><span class="cl">/usr/bin/python /Users/santo/src/bagoma.py <span class="se">\
</span></span></span><span class="line"><span class="cl">  --file<span class="o">=</span>off <span class="se">\
</span></span></span><span class="line"><span class="cl">  --email<span class="o">=</span>jerod.santo@gmail.com <span class="se">\
</span></span></span><span class="line"><span class="cl">  --pwd<span class="o">=</span>ohnoididnt <span class="se">\
</span></span></span><span class="line"><span class="cl">  --dir<span class="o">=</span>/Users/santo/Documents/Email/Jerod <span class="se">\
</span></span></span><span class="line"><span class="cl">  --action<span class="o">=</span>backup
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Backing up RSDi&#39;s email&#34;</span>
</span></span><span class="line"><span class="cl">/usr/bin/python /Users/santo/src/bagoma.py <span class="se">\
</span></span></span><span class="line"><span class="cl">  --file<span class="o">=</span>off <span class="se">\
</span></span></span><span class="line"><span class="cl">  --email<span class="o">=</span>jerod@rsdcompany.com <span class="se">\
</span></span></span><span class="line"><span class="cl">  --pwd<span class="o">=</span>againwiththepassword <span class="se">\
</span></span></span><span class="line"><span class="cl">  --dir<span class="o">=</span>/Users/santo/Documents/Email/RSDi <span class="se">\
</span></span></span><span class="line"><span class="cl">  --action<span class="o">=</span>backup
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># other accounts follow...</span>
</span></span></code></pre></div><p>The <code>--file</code> option disables logging to a file so all output is sent to STDOUT. The rest should be self-explanatory. After running the script manually a few times (the first run takes a LONG time if you have a lot of email), I set up a cron job to back up our email every night at 3AM.</p>
<p>I executed <code>crontab -e</code> and added the following line:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">0 3 * * * /Users/santo/bin/backup_email &gt;&gt;/var/log/backup_email.log 2&gt;&amp;1
</span></span></span></code></pre></div><p>BaGoMa&rsquo;s output is pretty informative so I send it to log a file in case I ever want to check up on it and make sure the backups are still working.</p>
<p>That&rsquo;s it. The thing has been running like clockwork ever since. If you&rsquo;re similarily concerned about continued access to your email, I highly recommend <a href="http://bagoma.sourceforge.net/">BaGoMa</a>.</p>
<p>But whatever you do, don&rsquo;t <a href="http://twitter.com/jerodsanto">follow me on Twitter</a>. That would be absolutely ridiculous.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Enumerable&#39;s *ect Methods Reimplemented Using `each_with_object`</title>
      <link>https://jerodsanto.net/2012/05/enumerables-ect-methods-reimplemented-using-each_with_object/</link>
      <pubDate>Sun, 20 May 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/05/enumerables-ect-methods-reimplemented-using-each_with_object/</guid>
      
      
      <description><![CDATA[<p>On episode <a href="http://rubyrogues.com/054-rr-coding-exercises-quizzes-and-katas/">54</a> of the Ruby Rogues podcast, <a href="http://twitter.com/joshsusser">Josh Susser</a> mentioned an interview question he used to use where he would ask interviewees to reimplement <a href="http://ruby-doc.org/core-1.9.3/Enumerable.html">Enumerable&rsquo;s</a> *ect methods (<code>collect</code>, <code>select</code>, <code>reject</code>, and <code>detect</code>) using <code>inject</code>.</p>
<p>On the same show, <a href="http://twitter.com/jeg2">James Edward Gray</a> aptly pointed out that Ruby 1.9 has a new method that works a lot like <code>inject</code>, but has a more meaningul name and removes the need to expliticly return the passed object during each pass: <a href="http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-each_with_object"><code>each_with_object</code></a>.</p>]]></description>
      
      <content:encoded><![CDATA[<p>On episode <a href="http://rubyrogues.com/054-rr-coding-exercises-quizzes-and-katas/">54</a> of the Ruby Rogues podcast, <a href="http://twitter.com/joshsusser">Josh Susser</a> mentioned an interview question he used to use where he would ask interviewees to reimplement <a href="http://ruby-doc.org/core-1.9.3/Enumerable.html">Enumerable&rsquo;s</a> *ect methods (<code>collect</code>, <code>select</code>, <code>reject</code>, and <code>detect</code>) using <code>inject</code>.</p>
<p>On the same show, <a href="http://twitter.com/jeg2">James Edward Gray</a> aptly pointed out that Ruby 1.9 has a new method that works a lot like <code>inject</code>, but has a more meaningul name and removes the need to expliticly return the passed object during each pass: <a href="http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-each_with_object"><code>each_with_object</code></a>.</p>
<p>I thought it&rsquo;d be a fun exercise to give Josh&rsquo;s interview question a go, but using <code>each_with_object</code> instead of <code>inject</code>.</p>
<h2 id="tests-first">Tests First</h2>
<p>We&rsquo;ll be monkey patching the Enumerable module, but first let&rsquo;s get some tests in place so we know if our overrides actually work. Here&rsquo;s a quick test suite, which could definitely be more thorough, but oh well this is just for fun anyhow:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;minitest/autorun&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">StarEctTest</span> <span class="o">&lt;</span> <span class="no">MiniTest</span><span class="o">::</span><span class="no">Unit</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@array</span> <span class="o">=</span> <span class="o">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">test_collect</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">10</span><span class="o">]</span><span class="p">,</span> <span class="vi">@array</span><span class="o">.</span><span class="n">collect</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="n">i</span> <span class="o">*</span> <span class="mi">2</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">test_detect</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="mi">3</span><span class="p">,</span> <span class="vi">@array</span><span class="o">.</span><span class="n">detect</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="mi">2</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_nil</span> <span class="vi">@array</span><span class="o">.</span><span class="n">detect</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">test_select</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="o">]</span><span class="p">,</span> <span class="vi">@array</span><span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">3</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[]</span><span class="p">,</span>  <span class="vi">@array</span><span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">test_reject</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="o">]</span><span class="p">,</span> <span class="vi">@array</span><span class="o">.</span><span class="n">reject</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">3</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[]</span><span class="p">,</span> <span class="vi">@array</span><span class="o">.</span><span class="n">reject</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>At this point we&rsquo;re just testing Ruby&rsquo;s imlpementation so it&rsquo;s no surprise that they&rsquo;re all green. Now we&rsquo;ll re-open the Enumerable module and define our methods inside.</p>
<p>My full source code is <a href="https://gist.github.com/2759931">here</a>, in case you want to follow along at home.</p>
<h2 id="collect">Collect</h2>
<p><a href="http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-collect"><code>collect</code></a> invokes the given block on each item and returns a new array with the results. If you&rsquo;ve never heard of <code>collect</code>, you may know it as <code>map</code>. Implementing it using <code>each_with_object</code> is pretty trivial. You just pass in an array and push processed items on to it.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">collect</span><span class="p">(</span><span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">each_with_object</span><span class="p">(</span><span class="o">[]</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="p">,</span> <span class="n">obj</span><span class="o">|</span> <span class="n">obj</span> <span class="o">&lt;&lt;</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="select">Select</h2>
<p><a href="http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-select"><code>select</code></a> returns a new array holding just the items for which the given block returned <code>true</code>. This one is also pretty easy.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">select</span><span class="p">(</span><span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">each_with_object</span><span class="p">(</span><span class="o">[]</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="p">,</span> <span class="n">obj</span><span class="o">|</span> <span class="n">obj</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="k">if</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="reject">Reject</h2>
<p><a href="http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-reject"><code>reject</code></a> is just like <code>select</code>, only the opposite: it returns a new array holding only the items for which the given block returned <code>false</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">reject</span><span class="p">(</span><span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">each_with_object</span><span class="p">(</span><span class="o">[]</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="p">,</span> <span class="n">obj</span><span class="o">|</span> <span class="n">obj</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="k">unless</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="detect">Detect</h2>
<p>Here&rsquo;s where it gets tricky. <a href="http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-detect"><code>detect</code></a> (a.k.a <code>find</code>) returns the first item for which the given block returns true. If no items fit the bill, it returns <code>nil</code>. Easy, right? Just pass <code>nil</code> into <code>each_with_object</code> and set it to an item when applicable:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">detect</span><span class="p">(</span><span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">each_with_object</span><span class="p">(</span><span class="kp">nil</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="p">,</span> <span class="n">obj</span><span class="o">|</span> <span class="n">obj</span> <span class="o">||=</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="p">?</span> <span class="n">i</span> <span class="p">:</span> <span class="kp">nil</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>I expected that to work, but sadly it does not. It turns out that you can&rsquo;t mutate <code>nil</code> using <code>each_with_object</code>. That actually makes sense, because you can&rsquo;t normally assign to <code>nil</code>, but it sure isn&rsquo;t very useful. No matter what you do inside the block, it just returns <code>nil</code>.</p>
<p>So for this particular method, I reverted to <code>inject</code>, which does allow you to pass <code>nil</code> into it and come out with something else:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">detect</span><span class="p">(</span><span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">inject</span><span class="p">(</span><span class="kp">nil</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">obj</span><span class="p">,</span> <span class="n">i</span><span class="o">|</span> <span class="n">obj</span> <span class="o">||=</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="p">?</span> <span class="n">i</span> <span class="p">:</span> <span class="kp">nil</span><span class="p">;</span> <span class="n">obj</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>So that works, but it feels a bit like a fail. If you can think of a way to implement <code>detect</code> using <code>each_with_object</code>, please do let me know.</p>
<h2 id="takeaways">Takeaways</h2>
<p>Putting yourself through these little challenges — no matter how silly they seem — is a great way to level up your skills. This was fun and I learned a little something along the way.</p>
<p>Plus, if I ever want to work for/with Josh, I&rsquo;ll be ready for at least one of his questions ;)</p>
<p>(<a href="https://gist.github.com/2759931">source code</a>)</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Tee for Two</title>
      <link>https://jerodsanto.net/2012/05/tee-for-two/</link>
      <pubDate>Wed, 16 May 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/05/tee-for-two/</guid>
      
      
      <description><![CDATA[<p><a href="http://en.wikipedia.org/wiki/Tee_(command)">Tee</a> is one of those commands so simple, so basic that you&rsquo;ve either been using it for years (and turn your nose up at me for writing about something so obvious) or you&rsquo;ll wish you had been (and sing my praises for writing about something so useful).</p>
<p>Sometimes you want to send a program&rsquo;s output to two locations. The most obvious case (and really the only one that I use) is when you want to display logging/debugging information in your shell and also capture it on disk for later analysis. Tee makes it dead simple. For example, an uploader Ruby script that logs to STDOUT:</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="http://en.wikipedia.org/wiki/Tee_(command)">Tee</a> is one of those commands so simple, so basic that you&rsquo;ve either been using it for years (and turn your nose up at me for writing about something so obvious) or you&rsquo;ll wish you had been (and sing my praises for writing about something so useful).</p>
<p>Sometimes you want to send a program&rsquo;s output to two locations. The most obvious case (and really the only one that I use) is when you want to display logging/debugging information in your shell and also capture it on disk for later analysis. Tee makes it dead simple. For example, an uploader Ruby script that logs to STDOUT:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">ruby uploader.rb | tee uploader.log
</span></span></span></code></pre></div><p>You can also use it as a middle man between between programs. For example, finding pdf files in a directory, writing the results to a new file while also using a pager on them:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">find ~/Documents -name *.pdf | tee pdfs.log | less
</span></span></span></code></pre></div><p>You&rsquo;ll find the <code>tee</code> command preinstalled on most unixes and in the package repositories of those that don&rsquo;t ship with it. I&rsquo;m pretty sure Windows PowerShell has it too, if you&rsquo;re in to that kind of thing.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Confessions of a Meteor Newb</title>
      <link>https://jerodsanto.net/2012/04/confessions-of-a-meteor-newb/</link>
      <pubDate>Mon, 23 Apr 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/04/confessions-of-a-meteor-newb/</guid>
      
      
      <description><![CDATA[<p>I recently took part in the first (annual, I hope) <a href="http://hackomaha.com">Hack Omaha</a> competition. I teamed up with the ridiculously talented <a href="http://www.zachleat.com/web/">Zach Leatherman</a> to create <a href="http://www.omahabountyhunter.com">Omaha Bounty Hunter</a>, a web-based The-Price-is-Right-style game using the provided <a href="https://hackomaha.socrata.com/Government/CRIME_property/3nfn-ev8i">Property Crime</a> data set.</p>
<p><a href="http://www.omahabountyhunter.com"><img src="http://www.omahabountyhunter.com/obh-logo.png" alt="Omaha Bounty Hunter" style="background: black"></a></p>
<p>As Zach and I were getting started, we were lamenting how timed coding competitions rarely provide opportunity to learn new tricks or try new tech. That&rsquo;s when one of us (I can&rsquo;t recall who) had a crazy idea&hellip; let&rsquo;s build this thing with the just-released <a href="http://meteor.com">Meteor</a> JavaScript framework!</p>]]></description>
      
      <content:encoded><![CDATA[<p>I recently took part in the first (annual, I hope) <a href="http://hackomaha.com">Hack Omaha</a> competition. I teamed up with the ridiculously talented <a href="http://www.zachleat.com/web/">Zach Leatherman</a> to create <a href="http://www.omahabountyhunter.com">Omaha Bounty Hunter</a>, a web-based The-Price-is-Right-style game using the provided <a href="https://hackomaha.socrata.com/Government/CRIME_property/3nfn-ev8i">Property Crime</a> data set.</p>
<p><a href="http://www.omahabountyhunter.com"><img src="http://www.omahabountyhunter.com/obh-logo.png" alt="Omaha Bounty Hunter" style="background: black"></a></p>
<p>As Zach and I were getting started, we were lamenting how timed coding competitions rarely provide opportunity to learn new tricks or try new tech. That&rsquo;s when one of us (I can&rsquo;t recall who) had a crazy idea&hellip; let&rsquo;s build this thing with the just-released <a href="http://meteor.com">Meteor</a> JavaScript framework!</p>
<p>And that&rsquo;s exactly what we did. Well, kind of&hellip;</p>
<p>Here&rsquo;s how it went down, from a total Meteor Newb&rsquo;s perspective.</p>
<h2 id="getting-started">Getting Started</h2>
<p>Congrats to the Meteor team for making getting started a breeze. You literally just run:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">curl install.meteor.com | /bin/sh
</span></span></span></code></pre></div><p>and you can start coding a Meteor app. I figured that I&rsquo;d at least have to have <a href="http://www.mongodb.org/">MongoDB</a> installed on my machine (this is the default — and currently only — persistence mechanism) before I could start. Nope. They bundle it up and manage the MongoDB server for you seamlessly. Very nice.</p>
<div class="notice">Newb tip: You can hop right into the Mongo shell by typing <code>meteor mongo</code> inside your app&rsquo;s root directory.</div>
<p>The provided <code>meteor</code> command has many features right out of the gate, most importantly a <code>--help</code> flag for general help and a <code>meteor help &lt;command&gt;</code> to learn more about each specific command.</p>
<p>I was excited to see <code>meteor update</code> which brings your Meteor install up to the latest release. A young project having an easy update feature baked right in says to me that the devs really thought this through.</p>
<p>I think it&rsquo;s safe to say that Zach and I were both up and running with Meteor in about 15 minutes.</p>
<h2 id="packages">Packages</h2>
<p>One big piece of Meteor is the package system. The good news is that for us the package system just worked. The bad news is that there aren&rsquo;t very many packages in the directory yet. This wouldn&rsquo;t be a probem if Meteor just directly integrated <a href="http://npmjs.org/">npm</a>, therefore inheriting its large package index, but I&rsquo;m sure they had their reasons.</p>
<p>The packages employed by any given Meteor app are listed in <code>.meteor/packages</code> and can be installed via:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">meteor add &lt;package_name&gt;
</span></span></span></code></pre></div><p>We used the <em>less</em> and <em>underscore</em> packages, but I couldn&rsquo;t talk Zach into using the <em>coffeescript</em> package. Oh well, there&rsquo;s always next year&hellip;</p>
<p>(<a href="http://docs.meteor.com/#packages">More info on packages</a>)</p>
<h2 id="getting-help">Getting Help</h2>
<p>The <a href="http://docs.meteor.com/">Meteor docs</a> are surprisingly good. They definitely didn&rsquo;t answer all of our questions and they definitely need <em>more</em> examples, but what is there is easy to read and organized really well. I can&rsquo;t even complain about the lack of search, because everything is on a single page. Chrome&rsquo;s inline search was sufficient to find anything we needed.</p>
<p>But what happens when the docs aren&rsquo;t cutting it and you have like 30 hours to get this dog hunting? <a href="http://meteor.com/irc">IRC</a>, that&rsquo;s what. I had two critical questions answered in #meteor AND they were answered swiftly AND it was a Saturday morning. That&rsquo;s pretty rad.</p>
<h2 id="autopublish-is-the-devil">Autopublish is the Devil</h2>
<p>It&rsquo;s a good thing that getting help was so painless, because boy did we need it! Everything was going peechy until I got the <a href="https://github.com/jerodsanto/obh/blob/master/import/import.rb">import script</a> (Yes, Ruby) working and imported slightly over 30,000 records into our <code>Bounties</code> collection.</p>
<p>This is where the S really hit the F.</p>
<p><a href="http://www.amazon.com/dp/B002AV8UH2/?tag=shoes4all-20"><img src="http://ecx.images-amazon.com/images/I/418FkFqBrPL.jpg" alt="You gotta be kidding me"></a></p>
<p>Meteor dutifully refreshed my browser for me and suddenly Chrome was crushing my CPU. I tried quitting Chrome. Nothing. I tried force quitting Chrome. Nothing. Tried again. Finally, it quit.</p>
<p>What the what?</p>
<p>After restarting Chrome and seeing my CPU spike again, I knew this was not a fluke. I managed to get the Dev Tools open this time to see a 7.8MB XHR request flying down the wire. Yup, the server was sending all 30k+ records down to my client.</p>
<p>You see, one of the reasons that Meteor demos so well is that you can execute arbitrary Mongo queries right in the client. What they don&rsquo;t tell you in the demo (but they will tell you on SO or IRC) is that this works out of the box because of a feature called <code>autopublish</code> which does pretty much what it sounds like. If you have more than a few documents in your database you need to disable <code>autopublish</code> and you need to disable it right now.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">meteor remove autopublish
</span></span></span></code></pre></div><p>After disabling autopublish I could once again load our app without crashing my browser, but now then there were no items available in the client&rsquo;s representation of the Bounties collection.</p>
<h2 id="i-dont-think-that-word-means-what-you-think-it-means">I don&rsquo;t think that word means what you think it means</h2>
<p>Once autopublish is out of the picture, you have to manually publish and subscribe in order to get data down to your clients. The way this works in Meteor is, to me, non-intuitive.</p>
<p>First you have to set up a publish on a channel from your server and then you have to subscribe to that channel from your clients. Notice the order there: first you publish, then you subscribe.</p>
<p>This was super weird for us because we wanted to have 2 clients join the same &ldquo;room&rdquo; and then have the server publish 5 random bounties down to the clients (we never got this far and thus our game is single player). The way I tried to get this working — but eventually abandoned — was to define methods that clients could call to initiate a battle, get a UUID from the server, and then join the channel with that UUID. It looked something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">Meteor</span><span class="p">.</span><span class="nx">startup</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// code to run on server at startup
</span></span></span><span class="line"><span class="cl">  <span class="nx">WAITING</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">  <span class="nx">BOUNTY_COUNT</span> <span class="o">=</span> <span class="nx">Bounties</span><span class="p">.</span><span class="nx">find</span><span class="p">().</span><span class="nx">count</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getRando</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="p">(</span><span class="nx">BOUNTY_COUNT</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Meteor</span><span class="p">.</span><span class="nx">methods</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">startSingle</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">Meteor</span><span class="p">.</span><span class="nx">uuid</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">startBattle</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">WAITING</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kd">var</span> <span class="nx">uuid</span> <span class="o">=</span> <span class="nx">WAITING</span><span class="p">.</span><span class="nx">pop</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kd">var</span> <span class="nx">uuid</span> <span class="o">=</span> <span class="nx">Meteor</span><span class="p">.</span><span class="nx">uuid</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">      <span class="nx">WAITING</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">uuid</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">uuid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">join</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">uuid</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">randos</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">    <span class="nx">_</span><span class="p">.</span><span class="nx">times</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">randos</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">getRando</span><span class="p">());</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Meteor</span><span class="p">.</span><span class="nx">publish</span><span class="p">(</span><span class="s2">&#34;bounties-&#34;</span> <span class="o">+</span> <span class="nx">uuid</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nx">Bounties</span><span class="p">.</span><span class="nx">find</span><span class="p">({</span><span class="nx">rando</span><span class="o">:</span> <span class="p">{</span><span class="nx">$in</span><span class="o">:</span> <span class="nx">randos</span><span class="p">}});</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>The idea here is that when a user clicked the &ldquo;Battle Mode&rdquo; link then they would call <code>startBattle</code>, get and store a UUID, and then call <code>join</code> with the given UUID. This would subscribe them to a channel and they&rsquo;d be waiting for an opponent to join with the same UUID.</p>
<p>The two clients would play the game with their 5 bounties and then somehow trigger the server to republish 5 different bounties to play with. The problem with that is what I said before, you have to publish before you subscribe (which, again, doesn&rsquo;t make much sense to me) and you can&rsquo;t republish to the same channel. The server would just need to change the Bounties collection on the channel and both clients would get the new bounties, but how does the server get the handle to the unique channel id?</p>
<p>Now, I may have just been thinking too client-server-y, but I never did figure out how to accomplish this pretty trivial work flow. If any Meteor non-newbs are reading, please post answers in the comments!</p>
<p>We ran out of time to figure out a good way to periodically fetch random bounties, so the end result of the server code is when somebody starts the game it just hands out 50 random bounties. If a user is wicked smart and gets all 50 bounties right, it creates a rift in the space-time continuum and a very naked Arnold Schwarzenegger shows up at their house looking for clothes, boots, and a motorcycle.</p>
<p><a href="http://www.imdb.com/title/tt0103064/"><img src="http://jerodsanto.net/drop/terminator_528_poster-20120423-161807.png" alt="T2"></a></p>
<h2 id="javascript-javascript-everywhere">JavaScript, JavaScript Everywhere</h2>
<p>One of the claimed advantages of using entirely JavaScript-based app stacks like Meteor is that you don&rsquo;t have to context switch between a server-side language and a client-side language. I don&rsquo;t think this is a much of a win for a couple of reasons:</p>
<ol>
<li>I find switching between Ruby and JavaScript (esp. CoffeeScript) pretty easy</li>
<li>You still have HTML, CSS, and some templating system in play so contexts still need switching quite often</li>
</ol>
<p>Now, I could be totally wrong and never having to stop to think about which language your code is running in could be a huge win, but with Meteor I experienced a completely different disorienting effect: I kept forgetting <em>where</em> my code was running. Is this the client? Is this the server? WHERE AM I?!</p>
<p>It helped to split the implementations up into separate files, but even then there are some Meteor APIs exposed only in the client&rsquo;s context, some exposed only in the server&rsquo;s context, and some exposed in both contexts. Figuring out what stuff was available where was&hellip;odd.</p>
<div class="notice">Newb tip: the docs says Client, Server, or Anywhere next to each method so it&rsquo;s not too much work to figure out</div>
<p>I have a feeling that this is something I&rsquo;d get used to pretty quickly, but it was frustrating nonetheless.</p>
<h2 id="meteor-and-mongo-sitting-in-a-tree">Meteor and Mongo, sitting in a tree</h2>
<p>MongoDB is tons of fun and I can&rsquo;t wait to use it again. One frustrating thing about using MongoDB from within Meteor is that only a subset of Mongo&rsquo;s JavaScript API is available from inside a Meteor collection. That said, I&rsquo;m no MongoDB pro so I&rsquo;m not sure how significant of a subset it is.</p>
<p>(<a href="http://docs.meteor.com/#meteor_collection">More on Meteor Collections</a>)</p>
<h2 id="deploying">Deploying</h2>
<p>Meteor provides free hosting on one of their subdomains (which is really awesome, btw), but we wanted to use our own domain so we deployed to Heroku instead. This was made relatively painless thanks to <a href="https://github.com/jordansissel/heroku-buildpack-meteor">this build pack</a>. I had to make a few adjustments to deploy from a subdirectory instead of the root directory (so as to ignore our import script), but other than that it just worked. I&rsquo;ve been using Heroku for years and it still impresses me more every time I use it.</p>
<h2 id="conclusions-faq-style">Conclusions, FAQ-style</h2>
<p><em>Did our Meteor app win the contest?</em></p>
<p>Nope. <a href="http://www.siliconprairienews.com/2012/04/food-fight-gamifies-restaurant-ratings-wins-hack-omaha-video">This awesome app</a> did.</p>
<p><em>Would our app have won if we built it on our bread-and-butter tools instead of Meteor?</em></p>
<p>Nope. <a href="http://www.siliconprairienews.com/2012/04/food-fight-gamifies-restaurant-ratings-wins-hack-omaha-video">This awesome app</a> would have.</p>
<p><em>Would I use Meteor for a production application?</em></p>
<p>Not yet. I&rsquo;d wait until they have more security features and more example code is available. I&rsquo;m also concerned about how well Meteor will scale (code-wise) to a very complex application. Hopefully there will be an aggressive, complex, open source Meteor app soon.</p>
<p><em>Would I use Meteor for fun, hacks, tech demos, etc?</em></p>
<p>Absolutely, but it might be worth checking out <a href="http://derbyjs.com/">Derby</a> too.</p>
<p><em>Is everything in this blog post absolute truth?</em></p>
<p>Absolutely not. When it comes to Meteor I&rsquo;m a total newb, remember!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Dead Simple Rails Config</title>
      <link>https://jerodsanto.net/2012/04/dead-simple-rails-config/</link>
      <pubDate>Wed, 04 Apr 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/04/dead-simple-rails-config/</guid>
      
      
      <description><![CDATA[<p>Your Rails app needs custom configuration so you run off to GitHub to find a gem for that. Stop.</p>
<p>That would&rsquo;ve been prudent in the bad ole&rsquo; days, but Rails 3 makes adding custom configuration dead simple. You <strike>don&rsquo;t have to</strike> shouldn&rsquo;t add a dependency. Here&rsquo;s how easy it is:</p>
<div class="notice">This method provides custom configuration without checking it in to version control. It&rsquo;s even easier if your configuration settings do not need to be private.</div>
<h2 id="easy-access">Easy Access</h2>
<p>There are two ways to access our application&rsquo;s configuration object in Rails 3.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Your Rails app needs custom configuration so you run off to GitHub to find a gem for that. Stop.</p>
<p>That would&rsquo;ve been prudent in the bad ole&rsquo; days, but Rails 3 makes adding custom configuration dead simple. You <strike>don&rsquo;t have to</strike> shouldn&rsquo;t add a dependency. Here&rsquo;s how easy it is:</p>
<div class="notice">This method provides custom configuration without checking it in to version control. It&rsquo;s even easier if your configuration settings do not need to be private.</div>
<h2 id="easy-access">Easy Access</h2>
<p>There are two ways to access our application&rsquo;s configuration object in Rails 3.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">MyApp</span><span class="o">::</span><span class="no">Application</span><span class="o">.</span><span class="n">config</span>
</span></span><span class="line"><span class="cl"><span class="c1"># or</span>
</span></span><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">config</span>
</span></span></code></pre></div><p>In my opinion, both of these are too verbose, so I add a convenience method to <code>config/application.rb</code> just inside my application&rsquo;s top-level module:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">MyApp</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">config</span>
</span></span><span class="line"><span class="cl">    <span class="no">Application</span><span class="o">.</span><span class="n">config</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Now we have easy access to the configuration object:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">MyApp</span><span class="o">.</span><span class="n">config</span>
</span></span></code></pre></div><p>Right now that object is a bucket full of Rails configurations. How do we get our own settings in there?</p>
<h2 id="just-add-yaml">Just add Yaml</h2>
<p>Add a <code>config.yml</code> file to our app&rsquo;s <code>config</code> directory. Put some settings in there.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">site_name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;My Application&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">contact_email</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;email@myapp.com&#34;</span><span class="w">
</span></span></span></code></pre></div><p>Copy the file to a new one called <code>config.yml.example</code>. Gitignore the real one. Check the example in to version control.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">cp config/config.yml config/config.yml.example
</span></span></span><span class="line"><span class="cl"><span class="go">echo &#34;config/config.yml&#34; &gt;&gt; .gitignore
</span></span></span></code></pre></div><h2 id="load-it">Load it</h2>
<p>We can add custom settings to Rails&rsquo; configuration manually like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">MyApp</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Application</span> <span class="o">&lt;</span> <span class="no">Rails</span><span class="o">::</span><span class="no">Application</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</span><span class="o">.</span><span class="n">site_name</span> <span class="o">=</span> <span class="s2">&#34;My Application&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>But we want to load in all the settings from <code>config.yml</code>, so manual ain&rsquo;t gonna cut it. Instead, load the Yaml file and send setter methods to the <code>Application</code> object for each setting, like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># inside MyApp::Application class</span>
</span></span><span class="line"><span class="cl"><span class="no">YAML</span><span class="o">.</span><span class="n">load_file</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="no">Rails</span><span class="o">.</span><span class="n">root</span><span class="si">}</span><span class="s2">/config/config.yml&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">k</span><span class="p">,</span><span class="n">v</span><span class="o">|</span> <span class="n">config</span><span class="o">.</span><span class="n">send</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">k</span><span class="si">}</span><span class="s2">=&#34;</span><span class="p">,</span> <span class="n">v</span> <span class="p">}</span>
</span></span></code></pre></div><p>It&rsquo;s as simple as that.</p>
<h2 id="go-crazy">Go Crazy</h2>
<p>This probably solves only the most basic of needs, but at just one LOC it is quite easy to extend ;). A few things that might be worth adding:</p>
<ol>
<li>Environment separation</li>
<li>Shared settings</li>
<li>Nested settings</li>
</ol>
<p>I&rsquo;ll leave those as very easy exercises for the reader.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Get FireHOL off of RSyslog&#39;s lawn</title>
      <link>https://jerodsanto.net/2012/03/get-firehol-off-of-rsyslogs-lawn/</link>
      <pubDate>Tue, 27 Mar 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/03/get-firehol-off-of-rsyslogs-lawn/</guid>
      
      
      <description><![CDATA[<p><a href="http://firehol.sourceforge.net/">FireHOL</a> is the best tool I&rsquo;ve used for configuring Linux firewalls. But it can really spew its logs all over your <code>kern.log</code> and <code>syslog</code>. Getting it to stop this is non-obvious so hopefully this saves you some time.</p>
<div class="notice">This tutorial is for Debian 6 running rsyslog. As always, YMMV.</div>
<h2 id="install-firehol">Install FireHOL</h2>
<p>This is the easy part.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">aptitude install firehol
</span></span></span></code></pre></div><h2 id="make-sure-it-can-start">Make sure it can start</h2>
<p>Debian&rsquo;s FireHOL package has it disabled by default. Edit <code>/etc/default/firehol</code> and set:</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="http://firehol.sourceforge.net/">FireHOL</a> is the best tool I&rsquo;ve used for configuring Linux firewalls. But it can really spew its logs all over your <code>kern.log</code> and <code>syslog</code>. Getting it to stop this is non-obvious so hopefully this saves you some time.</p>
<div class="notice">This tutorial is for Debian 6 running rsyslog. As always, YMMV.</div>
<h2 id="install-firehol">Install FireHOL</h2>
<p>This is the easy part.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">aptitude install firehol
</span></span></span></code></pre></div><h2 id="make-sure-it-can-start">Make sure it can start</h2>
<p>Debian&rsquo;s FireHOL package has it disabled by default. Edit <code>/etc/default/firehol</code> and set:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">START_FIREHOL=YES
</span></span></span></code></pre></div><h2 id="set-a-custom-log-prefix">Set a custom log prefix</h2>
<p>Edit <code>/etc/firehol/firehol.conf</code> and add the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">FIREHOL_LOG_PREFIX=&#34;firehol: &#34;
</span></span></span></code></pre></div><p>This ensures that all FireHOL-generated log messages contain this string. While you&rsquo;re here, you might want to configure the firewall itself :)</p>
<h2 id="create-a-special-rule-in-rsyslog">Create a special rule in rsyslog</h2>
<p>Add a file at <code>/etc/rsyslog.d/30-firehol.conf</code> and make it have the following content:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">:msg, contains, &#34;&#39;firehol: &#34; -/var/log/firehol.log
</span></span></span><span class="line"><span class="cl"><span class="go">&amp; ~
</span></span></span></code></pre></div><p>This will make all log messages that contain the &ldquo;&lsquo;firehol: &quot; string log to their own file. It then skips the rest of the rules so they don&rsquo;t also go to <code>kern.log</code> and <code>syslog</code>. The &ldquo;30&rdquo; in the filename is just there to ensure that this file is evaluated before the others. I&rsquo;m not sure if it&rsquo;s actually necessary or not.</p>
<h2 id="restart-stuff">Restart stuff</h2>
<p>That should be all you need to do. Now restart FireHOL and Rsyslog like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">/etc/init.d/firehol stop
</span></span></span><span class="line"><span class="cl"><span class="go">/etc/init.d/rsyslog stop
</span></span></span><span class="line"><span class="cl"><span class="go">/etc/init.d/firehol start
</span></span></span><span class="line"><span class="cl"><span class="go">/etc/init.d/rsyslog start
</span></span></span></code></pre></div><p>Then check <code>/var/log/firehol.log</code> to make sure FireHOL is logging there.</p>
<h2 id="bonus">Bonus</h2>
<p>You may also be getting sick of seeing this warning every time you restart FireHOL:</p>
<p>File &lsquo;/etc/firehol/RESERVED_IPS&rsquo; is more than 90 days old</p>
<p>The worst part is that the script to update the reserved IPs list (as of right now) 404s so you can&rsquo;t fix it. What you can do is this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">touch /etc/firehol/RESERVED_IPS
</span></span></span></code></pre></div><p>That&rsquo;ll shut it up, if for only another 3 months.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Naming Schemes</title>
      <link>https://jerodsanto.net/2012/03/naming-schemes/</link>
      <pubDate>Thu, 22 Mar 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/03/naming-schemes/</guid>
      
      
      <description><![CDATA[<p>We all know that naming things is one of <a href="http://martinfowler.com/bliki/TwoHardThings.html">the hard things</a> in Computer Science. It turns out that this is also a problem in network/system administration. So many servers. So hard to come up with names for them all.</p>
<p>Naming schemes help, but they are also difficult to come up with. Thankfully, <a href="http://namingschemes.com/">there&rsquo;s a wiki for that</a>.</p>
<p>A few characteristics of a good machine name:</p>
<ol>
<li>One word, if possible — avoid the CamelCase v. under_score war</li>
<li>Memorable — If you need to look it up, you picked a bad name</li>
<li>Easy to spell — Help you help yourself</li>
<li>Not embarrasing — I once named a server &ldquo;Woody&rdquo; (whoops)</li>
</ol>
<p>Any naming scheme chosen must provide a multitude of names that fit the chriteria above. Some of my favorite schemes from this list:</p>]]></description>
      
      <content:encoded><![CDATA[<p>We all know that naming things is one of <a href="http://martinfowler.com/bliki/TwoHardThings.html">the hard things</a> in Computer Science. It turns out that this is also a problem in network/system administration. So many servers. So hard to come up with names for them all.</p>
<p>Naming schemes help, but they are also difficult to come up with. Thankfully, <a href="http://namingschemes.com/">there&rsquo;s a wiki for that</a>.</p>
<p>A few characteristics of a good machine name:</p>
<ol>
<li>One word, if possible — avoid the CamelCase v. under_score war</li>
<li>Memorable — If you need to look it up, you picked a bad name</li>
<li>Easy to spell — Help you help yourself</li>
<li>Not embarrasing — I once named a server &ldquo;Woody&rdquo; (whoops)</li>
</ol>
<p>Any naming scheme chosen must provide a multitude of names that fit the chriteria above. Some of my favorite schemes from this list:</p>
<ul>
<li><a href="http://namingschemes.com/Bible_Names">Bible Names</a></li>
<li><a href="http://namingschemes.com/Big_Lebowski_Characters">Big Lebowski Characters</a></li>
<li><a href="http://namingschemes.com/Shakespeare_Characters">Shakespeare Characters</a></li>
<li><a href="http://namingschemes.com/Elements">Elements</a></li>
<li><a href="http://namingschemes.com/Fraggle_Rock_Characters">Fraggle Rock Characters</a></li>
<li><a href="http://namingschemes.com/James_Bond_Villains">James Bond Villans</a></li>
<li><a href="http://namingschemes.com/Mexican_Foods">Mexican Foods</a></li>
<li><a href="http://namingschemes.com/Simpsons">Simpsons</a></li>
<li><a href="http://namingschemes.com/Wrestlers">Wrestlers</a></li>
</ul>
<p>So far I&rsquo;ve usually stuck to TV show characters because they are memorable and plentiful. Series I&rsquo;ve used as naming schemes include <a href="http://en.wikipedia.org/wiki/List_of_recurring_Cheers_characters">Cheers</a> (hence: Woody), <a href="http://en.wikipedia.org/wiki/M*A*S*H_(TV_series)#Characters">M*A*S*H</a>, and most recently <a href="http://en.wikipedia.org/wiki/Arrested_Development_Characters">Arrested Development</a>.</p>
<p><img src="http://jerodsanto.net/drop/arrested_development_gob_magic-20120322-164935.png" alt="Gob the Illusionist"></p>
<p>Come on, who wouldn&rsquo;t want a server names after that guy?!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Best Buy Considered Harmful</title>
      <link>https://jerodsanto.net/2012/03/best-buy-considered-harmful/</link>
      <pubDate>Mon, 05 Mar 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/03/best-buy-considered-harmful/</guid>
      
      
      <description><![CDATA[<p>Once in a blue moon I have a valid reason to visit <a href="http://www.bestbuy.com/">Best Buy</a>. In this particular case, an upcoming snowboarding trip had me wanting some cheap headphones that wrap around your ears. I found a pair from <a href="http://www.amazon.com/Sony-MDR-J10-Headphones-Non-Slip-Design/dp/B000092YR6/ref=sr_1_1?s=electronics&amp;ie=UTF8&amp;qid=1330799507&amp;sr=1-1">Sony</a> that looked like they&rsquo;d do the trick, but they weren&rsquo;t on Amazon Prime so I couldn&rsquo;t get them delivered on time.</p>
<p><a href="http://www.amazon.com/Sony-MDR-J10-Headphones-Non-Slip-Design/dp/B000092YR6/ref=sr_1_1?s=electronics&amp;ie=UTF8&amp;qid=1330799507&amp;sr=1-1"><img src="http://jerodsanto.net/drop/sony-clip-on-ears-20120304-080505.png" alt="Sony MDR-J10"></a></p>
<p>Best Buy&rsquo;s website showed the same model for $9.99 in stock at my local store. It was a bright and breezy Saturday morning, so I made my first trip to Best Buy in recent memory.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Once in a blue moon I have a valid reason to visit <a href="http://www.bestbuy.com/">Best Buy</a>. In this particular case, an upcoming snowboarding trip had me wanting some cheap headphones that wrap around your ears. I found a pair from <a href="http://www.amazon.com/Sony-MDR-J10-Headphones-Non-Slip-Design/dp/B000092YR6/ref=sr_1_1?s=electronics&amp;ie=UTF8&amp;qid=1330799507&amp;sr=1-1">Sony</a> that looked like they&rsquo;d do the trick, but they weren&rsquo;t on Amazon Prime so I couldn&rsquo;t get them delivered on time.</p>
<p><a href="http://www.amazon.com/Sony-MDR-J10-Headphones-Non-Slip-Design/dp/B000092YR6/ref=sr_1_1?s=electronics&amp;ie=UTF8&amp;qid=1330799507&amp;sr=1-1"><img src="http://jerodsanto.net/drop/sony-clip-on-ears-20120304-080505.png" alt="Sony MDR-J10"></a></p>
<p>Best Buy&rsquo;s website showed the same model for $9.99 in stock at my local store. It was a bright and breezy Saturday morning, so I made my first trip to Best Buy in recent memory.</p>
<p>Oh man it sucked for so many reasons…</p>
<h2 id="the-hunt">The Hunt</h2>
<p>I could write a whole seperate rant on the search inadequacies of brick-and-morter retail stores, but let&rsquo;s just focus on the particular item I was at Best Buy to acquire.</p>
<p>I walked in and saw two distinct sections on separate sides of the store:</p>
<ol>
<li>Music</li>
<li>iPods/MP3 Players</li>
</ol>
<p>Which of these two sections would you expect to find the headphones? That&rsquo;s a trick question. There are headphones in both sections. Except each section has different models.</p>
<p><img src="http://jerodsanto.net/drop/double-facepalm-20120303-144151.png" alt="Double Facepalm"></p>
<p>I first glanced at the <em>Music</em> section and saw a rack of headphones. These particular headphones had &ldquo;sports&rdquo; and &ldquo;fitness&rdquo; keywords on their packaging so I figured I was in the right place. They were specialized headphones which are designed to stay on your ears while doing physical activity.</p>
<p>Bingo, right?! Wrong.</p>
<p>My $10 Sony&rsquo;s were nowhere to be found. The cheapest alternative on this rack was $29. If I didn&rsquo;t already know that the Sony&rsquo;s existed <em>somewhere</em> in the store I probably would have walked out paying 3x for a similar product.</p>
<p>It turns out the <em>right</em> place to look for $10 stay-on-your-ear Sony headphones is in the <em>iPods/MP3 Player</em> section with all of the noise cancelling and professional-grade headphones. Naturally.</p>
<h2 id="the-irl-disadvantage">The IRL (dis)Advantage</h2>
<p>Once I found the pair that I came for, it was time to evaluate the product to see if they were a good fit for the job. At this point I&rsquo;d expect Best Buy to cream the online stores where you&rsquo;re left to look at a few low resolution images and read other people&rsquo;s opinions.</p>
<p>Nope, not in the least.</p>
<p>Most of the headphones are wrapped in their standard plastic packaging, my Sony&rsquo;s included. There is no way to easily open the package to inspect them closer. There are no demo units. You literally can&rsquo;t put them on or hear how they sound. Wha?</p>
<p>Come on, Best Buy! This is your one chance — your only chance — to put Amazon&rsquo;s shopping experience to shame. Have a demo pair of each headphone and an iPod there loaded with music. Are you ascared of theft? Fine, latch the freaking iPod to a table or something. Just do something! You&rsquo;re doing nothing to improve on what the Internet offers. NOTHING.</p>
<p>They forced me to be that guy who pries open the packaging of a potential purchase and squeezes the item out just enough to get his fingers on it… just enough to hold the whole package up to my ear and see how it fits. We all know that guy. We don&rsquo;t want to be that guy, but I was without choice.</p>
<p><img src="http://jerodsanto.net/drop/thatguy-20120303-144346.png" alt="That guy"></p>
<h2 id="the-crap-canal">The Crap Canal</h2>
<p>I don&rsquo;t know when it happened, but at some point Best Buy decided it&rsquo;d be cool to force all of their paying customers through a crap canal on the way out the door (they may have cribbed this idea from Ikea, which is infamous for it).</p>
<p><img src="http://jerodsanto.net/drop/crap-canal-20120304-085210.png" alt="The Crap Canal"></p>
<p>Typical items you might find in the crap canal: Funyuns, Tamagotchi, a Justin Beiber bracelet, or the <a href="http://www.imdb.com/title/tt0270846/">Superbabies</a> Director&rsquo;s Cut on DVD.</p>
<p>You know, the <em>good</em> stuff. The stuff we all came to an <strong>electronics</strong> store for in the first place.</p>
<p>What&rsquo;s worse is that once you reach the end of the canal — where the crappiest crap resides — you have to stand there and wait for the next available cashier. They won&rsquo;t let you queue like normal behind each cashier. You have to queue right there next to the Barbie Pez Dispensers and the Zune case/charger combo packs.</p>
<h2 id="the-tipping-point">The Tipping Point</h2>
<p>Where my Best Buy experience went from Just Bad® to The Worst Ever® was at checkout. After politely declining a Best Buy Rewards account, I was prompted with a question so ridiculous that the cashier felt compelled to preface it with &ldquo;I have to ask you this&rdquo;:</p>
<p>Would I like to buy a $5 warranty for my $10 purchase?</p>
<p>Would I what? Like to buy… a $5 warranty… for my $10 purchase?</p>
<p><img src="http://jerodsanto.net/drop/mindblown.gif" alt="mind blow in a bad way"></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>ip2loc lives</title>
      <link>https://jerodsanto.net/2012/02/ip2loc-lives/</link>
      <pubDate>Sat, 25 Feb 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/02/ip2loc-lives/</guid>
      
      
      <description><![CDATA[<p>If you follow me on <a href="http://twitter.com/jerodsanto">Twitter</a>, then you know that I was <a href="http://twitter.com/jerodsanto/status/171266417906290689">a little upset</a> last week with regard to one of my little side projects, <a href="http://ip2loc.jerodsanto.net">ip2loc</a>, which provides simple IP address to geographic location translation via a Google Map.</p>
<p>The full story isn&rsquo;t very interesting, but let&rsquo;s just say that a few unrighteous souls took advantage of the free service that I offered and ruined the fun for everyone.</p>
<p>In my frustration, I replaced the lookup form with a &ldquo;<em>this is why we can&rsquo;t have nice things</em>&rdquo; message and announced the service&rsquo;s retirement on Twitter.</p>]]></description>
      
      <content:encoded><![CDATA[<p>If you follow me on <a href="http://twitter.com/jerodsanto">Twitter</a>, then you know that I was <a href="http://twitter.com/jerodsanto/status/171266417906290689">a little upset</a> last week with regard to one of my little side projects, <a href="http://ip2loc.jerodsanto.net">ip2loc</a>, which provides simple IP address to geographic location translation via a Google Map.</p>
<p>The full story isn&rsquo;t very interesting, but let&rsquo;s just say that a few unrighteous souls took advantage of the free service that I offered and ruined the fun for everyone.</p>
<p>In my frustration, I replaced the lookup form with a &ldquo;<em>this is why we can&rsquo;t have nice things</em>&rdquo; message and announced the service&rsquo;s retirement on Twitter.</p>
<p>(I just looked and my ip2loc announcement blog post was in July of 2008. That&rsquo;s a long time ago!)</p>
<h2 id="but">But</h2>
<p>So many folks reached out to say thanks, offer assistance, and tell me how often they use the tool that I decided to bring it back to life.</p>
<h2 id="compromises">Compromises</h2>
<p>In order to resurrect the service, I had to switch away from the paid <a href="http://www.maxmind.com/">MaxMind</a> web service that was powering it. This is worse in a few ways:</p>
<ol>
<li>
<p>Accuracy suffers because the database is updated monthly instead of in realtime</p>
</li>
<li>
<p>It no longer displays ISP information for each IP</p>
</li>
<li>
<p>There is now a PayPal donate button for those who <em>really</em> appreciate the tool</p>
</li>
</ol>
<p>All in all I think these deficincies are worth suffering to keep the service online. Rarely a week goes by that I don&rsquo;t run a lookup myself, so I am happy to be able to bring it back.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>A Simple Pattern to Namespace and Selectively Execute Certain Bits of JavaScript Depending on Which Rails Controller and Action are Active</title>
      <link>https://jerodsanto.net/2012/02/a-simple-pattern-to-namespace-and-selectively-execute-certain-bits-of-javascript-depending-on-which-rails-controller-and-action-are-active/</link>
      <pubDate>Wed, 08 Feb 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/02/a-simple-pattern-to-namespace-and-selectively-execute-certain-bits-of-javascript-depending-on-which-rails-controller-and-action-are-active/</guid>
      
      
      <description><![CDATA[<p>Rails 3.1&rsquo;s asset pipeline compiles all of our JavaScript into a single file which is executed on all pages which include the <code>javascript_include_tag</code> method. Most of the time that method is in the layout, which means evey page gets the same bundle of JavaScript. This is great for caching, but not so great for selective code execution.</p>
<p>I developed a pretty simple pattern to namespace and selectively execute certain bits of JavaScript depending on which Rails controller action is active. Just do the following:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Rails 3.1&rsquo;s asset pipeline compiles all of our JavaScript into a single file which is executed on all pages which include the <code>javascript_include_tag</code> method. Most of the time that method is in the layout, which means evey page gets the same bundle of JavaScript. This is great for caching, but not so great for selective code execution.</p>
<p>I developed a pretty simple pattern to namespace and selectively execute certain bits of JavaScript depending on which Rails controller action is active. Just do the following:</p>
<div class="notice">I talk about JavaScript, but my code samples are all in CoffeeScript. That&rsquo;s because they&rsquo;re really the same thing. Aren&rsquo;t they?</div>
<h2 id="1-create-an-application-object">1) Create an application object</h2>
<p>Let&rsquo;s write a Rails app for accurately predicting weather patterns. We&rsquo;ll call it <em>Elijah</em>. The first step is to create a top-level JavaScript object named after our application.</p>
<p>Create <code>app/assets/javascripts/elijah.js.coffee</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="k">this</span><span class="p">.</span><span class="nx">Elijah</span> <span class="o">?=</span> <span class="p">{}</span>
</span></span></code></pre></div><p>This is also a nice place to define common methods that will be used all across the application.</p>
<p>All controller-level objects will be namescaped inside this object, so it must be specified first in the application manifest. Edit <code>app/assets/javascripts/application.js</code> like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">//= require jquery
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require elijah
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require_tree .
</span></span></span></code></pre></div><h2 id="2-add-a-javascript-controller-for-each-rails-controller">2) Add a JavaScript controller for each Rails controller</h2>
<p>Each Rails controller will have a matching JavaScript controller to manage code executed on the Rails controller&rsquo;s actions.</p>
<p><em>Elijah</em> has a <code>TemperaturesController</code>, so we need our JavaScript to follow suit. Create <code>app/assets/javascripts/temperatures.js.coffee</code>. In it, define a class and instantiate an object of the class.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-coffeescript" data-lang="coffeescript"><span class="line"><span class="cl"><span class="k">class</span> <span class="nx">TemperaturesController</span>
</span></span><span class="line"><span class="cl">    <span class="nv">init: </span><span class="nf">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span> <span class="s">&#34;temps init!&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nv">index: </span><span class="nf">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span> <span class="s">&#34;temps index!&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">this</span><span class="p">.</span><span class="nv">Elijah.temperatures = </span><span class="k">new</span> <span class="nx">TemperaturesController</span>
</span></span></code></pre></div><p>The <code>init</code> method is where to put any setup code that will be executed on before all actions for a given controller.</p>
<p>Each action can optionally have its own method which will be executed on it and no other actions owned by the controller.</p>
<p>This example will log &ldquo;temps init!&rdquo; followed by &ldquo;temps index!&rdquo; when a user visits the temperatures index.</p>
<h2 id="3-embed-the-current-controller-and-action-in-the-html">3) Embed the current controller and action in the HTML</h2>
<p>Somehow we have to let our <em>Elijah</em> JavaScript object know which controller/action pair are active for a given page request. I do this by adding <code>data-</code> attributes to the <code>body</code> element of the layout, like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span> <span class="na">data-controller</span><span class="o">=</span><span class="s">&#34;&lt;%= controller.controller_path %&gt;&#34;</span> <span class="na">data-action</span><span class="o">=</span><span class="s">&#34;&lt;%= controller.action_name %&gt;&#34;</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>It&rsquo;s important to use <code>controller_path</code> instead of <code>controller_name</code> if you ever want to namespace your Rails controllers.</p>
<h2 id="4-auto-execute-the-matching-controlleraction-javascript">4) Auto-execute the matching controller/action JavaScript</h2>
<p>The last thing to do is make sure that the active Rails controller/action have their matching JavaScript controller/method executed when the page loads.</p>
<p>This can be set up right inside the application manifest file because code added there is executed <em>after</em> all other compiled code. This way we&rsquo;re sure to have our JavaScript controller objects in place.</p>
<p>It&rsquo;ll look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// - snipped -
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require_tree .
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">$</span><span class="p">,</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">$</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">$body</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;body&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">controller</span> <span class="o">=</span> <span class="nx">$body</span><span class="p">.</span><span class="nx">data</span><span class="p">(</span><span class="s2">&#34;controller&#34;</span><span class="p">).</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\//g</span><span class="p">,</span> <span class="s2">&#34;_&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">action</span> <span class="o">=</span> <span class="nx">$body</span><span class="p">.</span><span class="nx">data</span><span class="p">(</span><span class="s2">&#34;action&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">activeController</span> <span class="o">=</span> <span class="nx">Elijah</span><span class="p">[</span><span class="nx">controller</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">activeController</span> <span class="o">!==</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">$</span><span class="p">.</span><span class="nx">isFunction</span><span class="p">(</span><span class="nx">activeController</span><span class="p">.</span><span class="nx">init</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">activeController</span><span class="p">.</span><span class="nx">init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">$</span><span class="p">.</span><span class="nx">isFunction</span><span class="p">(</span><span class="nx">activeController</span><span class="p">[</span><span class="nx">action</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">activeController</span><span class="p">[</span><span class="nx">action</span><span class="p">]();</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">})(</span><span class="nx">jQuery</span><span class="p">);</span>
</span></span></code></pre></div><p>Nothing too crazy going on here. It just extracts the embedded controller/action combo and executes the matching JavaScript controller&rsquo;s <code>init</code> method followed by the action method.</p>
<p>Ruby controller namespaces need to be replaced by underscores in JavaScript. For example, an <code>Admin::UsersController</code> will require a JavaScript object called <code>Elijah.admin_users</code>.</p>
<h2 id="5-there-is-no-step-5">5) There is no step 5</h2>
<p>That&rsquo;s all there is to it. So far, this has worked pretty well to keep my JavaScript organized.</p>
<p>Are there better paths to the same goal? How do you go about it?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Sublime Text 2 Icon Replacement Roundup</title>
      <link>https://jerodsanto.net/2012/01/sublime-text-2-icon-replacement-roundup/</link>
      <pubDate>Thu, 12 Jan 2012 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2012/01/sublime-text-2-icon-replacement-roundup/</guid>
      
      
      <description><![CDATA[<p>Sublime Text 2 may be the best text editor on the planet, but its icon (as of build 2165) is an abomination.</p>
<p><img src="http://jerodsanto.net/drop/st2-20120112-085400.jpg" alt="Sublime Text 2 Icon"></p>
<p>Replacements abound, and I&rsquo;ve rounded them all up for convenient comparison and selection.</p>
<h2 id="by-adam-kiss-on-forrst"><a href="https://forrst.com/posts/Unofficial_Sublime_Text_2_Icon_Replacement_F-6hb">By Adam Kiss on Forrst</a></h2>
<p><a href="https://forrst.com/posts/Unofficial_Sublime_Text_2_Icon_Replacement_F-6hb"><img src="http://jerodsanto.net/drop/akst2-20120112-083432.jpg" alt="Adam Kiss Sublime Text 2 Icon"></a></p>
<h2 id="by-daniel-matarazzo-on-dribbble"><a href="http://dribbble.com/shots/337996-Sublime-Text-2-Icon">By Daniel Matarazzo on Dribbble</a></h2>
<p><a href="http://dribbble.com/shots/337996-Sublime-Text-2-Icon"><img src="http://jerodsanto.net/drop/dmst2-20120112-083255.jpg" alt="Daniel Matarazzo Sublime Text 2 Icon"></a></p>
<h2 id="by-kreg-wallace"><a href="http://www.designkode.com/blog/sublime-text-icon">By Kreg Wallace</a></h2>
<p><a href="http://www.designkode.com/blog/sublime-text-icon"><img src="http://jerodsanto.net/drop/kwst2-20120112-083802.jpg" alt="Kreg Wallace Sublime Text 2 Icon"></a></p>]]></description>
      
      <content:encoded><![CDATA[<p>Sublime Text 2 may be the best text editor on the planet, but its icon (as of build 2165) is an abomination.</p>
<p><img src="http://jerodsanto.net/drop/st2-20120112-085400.jpg" alt="Sublime Text 2 Icon"></p>
<p>Replacements abound, and I&rsquo;ve rounded them all up for convenient comparison and selection.</p>
<h2 id="by-adam-kiss-on-forrst"><a href="https://forrst.com/posts/Unofficial_Sublime_Text_2_Icon_Replacement_F-6hb">By Adam Kiss on Forrst</a></h2>
<p><a href="https://forrst.com/posts/Unofficial_Sublime_Text_2_Icon_Replacement_F-6hb"><img src="http://jerodsanto.net/drop/akst2-20120112-083432.jpg" alt="Adam Kiss Sublime Text 2 Icon"></a></p>
<h2 id="by-daniel-matarazzo-on-dribbble"><a href="http://dribbble.com/shots/337996-Sublime-Text-2-Icon">By Daniel Matarazzo on Dribbble</a></h2>
<p><a href="http://dribbble.com/shots/337996-Sublime-Text-2-Icon"><img src="http://jerodsanto.net/drop/dmst2-20120112-083255.jpg" alt="Daniel Matarazzo Sublime Text 2 Icon"></a></p>
<h2 id="by-kreg-wallace"><a href="http://www.designkode.com/blog/sublime-text-icon">By Kreg Wallace</a></h2>
<p><a href="http://www.designkode.com/blog/sublime-text-icon"><img src="http://jerodsanto.net/drop/kwst2-20120112-083802.jpg" alt="Kreg Wallace Sublime Text 2 Icon"></a></p>
<h2 id="by-nate-beaty"><a href="http://www.sublimetext.com/forum/viewtopic.php?f=2&amp;t=1558&amp;hilit=icon&amp;start=40#p9365">By Nate Beaty</a></h2>
<p><a href="http://www.sublimetext.com/forum/viewtopic.php?f=2&amp;t=1558&amp;hilit=icon&amp;start=40#p9365"><img src="http://jerodsanto.net/drop/nbst2-20120112-084416.jpg" alt="Nate Beaty Sublime Text 2 Icon"></a></p>
<h2 id="by-nate-beaty-1"><a href="http://www.sublimetext.com/forum/viewtopic.php?f=2&amp;t=1558&amp;hilit=icon&amp;start=40#p9365">By Nate Beaty</a></h2>
<p><a href="http://www.sublimetext.com/forum/viewtopic.php?f=2&amp;t=1558&amp;hilit=icon&amp;start=40#p9365"><img src="http://jerodsanto.net/drop/nbst2-2-20120112-084602.jpg" alt="Nate Beaty 2 Sublime Text 2 Icon"></a></p>
<h2 id="by-jon-paul-lunney-on-dribbble"><a href="http://dribbble.com/shots/357612-Sublime-Text-2-Replacement-Icon">By Jon-Paul Lunney on Dribbble</a></h2>
<p><a href="http://dribbble.com/shots/357612-Sublime-Text-2-Replacement-Icon"><img src="http://jerodsanto.net/drop/jplst2-20120112-085107.jpg" alt="Jon-Paul Lunney Sublime Text 2 Icon"></a></p>
<h2 id="by-dan-perrera-on-dribbble"><a href="http://dribbble.com/shots/311515-A-Sublime-Text-2-Icon-that-is-less-horrible">By Dan Perrera on Dribbble</a></h2>
<p><a href="http://dribbble.com/shots/311515-A-Sublime-Text-2-Icon-that-is-less-horrible"><img src="http://jerodsanto.net/drop/dpst2-20120112-085302.jpg" alt="Dan Perrera Sublime Text 2 Icon"></a></p>
<p>If I&rsquo;m missing any <em>good</em> ones or new ones are released, let me know in the comments and I&rsquo;ll add them.</p>
<p>Hopefully the official icon will get updated before the app is out of beta. Who knows, maybe one of these alternatives will be chosen.</p>
<p>Enjoy!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Beloved Bits, 2011</title>
      <link>https://jerodsanto.net/2011/12/beloved-bits-2011/</link>
      <pubDate>Sat, 31 Dec 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/12/beloved-bits-2011/</guid>
      
      
      <description><![CDATA[<p>&lsquo;Tis the season for retrospectives and &ldquo;Best of&rdquo; lists. I used to fart in the general direction of such mawkishness, but age brings with it a fondness for the nostalgiac and a sentimentality not to be suppressed.</p>
<p>I suppose I&rsquo;ve reached the aforementioned age, because this is my first ever list-of-things-that-I-deem-notable-in-the-year-which-is-about-to-conclude.</p>
<h2 id="house-rules">House Rules</h2>
<p>A list without rules is like a flock of sheep without a shephard; meandering, lawless, fetid.</p>
<p><a href="http://www.flickr.com/photos/14229642@N07/3111026977/" title="Deputation by Petereck, on Flickr"><img src="http://farm4.staticflickr.com/3229/3111026977_5e78e5ddd5.jpg" width="500" height="356" alt="Deputation"></a></p>]]></description>
      
      <content:encoded><![CDATA[<p>&lsquo;Tis the season for retrospectives and &ldquo;Best of&rdquo; lists. I used to fart in the general direction of such mawkishness, but age brings with it a fondness for the nostalgiac and a sentimentality not to be suppressed.</p>
<p>I suppose I&rsquo;ve reached the aforementioned age, because this is my first ever list-of-things-that-I-deem-notable-in-the-year-which-is-about-to-conclude.</p>
<h2 id="house-rules">House Rules</h2>
<p>A list without rules is like a flock of sheep without a shephard; meandering, lawless, fetid.</p>
<p><a href="http://www.flickr.com/photos/14229642@N07/3111026977/" title="Deputation by Petereck, on Flickr"><img src="http://farm4.staticflickr.com/3229/3111026977_5e78e5ddd5.jpg" width="500" height="356" alt="Deputation"></a></p>
<p>My self-imposed rules for this list are as follows:</p>
<ol>
<li>List items must be anatomically digital</li>
<li>List items must have won <em>me</em> over in 2011</li>
<li>List items may or may not have been released in 2011</li>
</ol>
<p>Them&rsquo;s the rules. Let&rsquo;s get started!</p>
<h2 id="ios-apps">iOS Apps</h2>
<p>I&rsquo;ve been a huge iOS fan since day two (I jumped on board with the iPhone 3G). My iOS use is decidedly recreational, so my favorite apps on the platform lean in that direction.</p>
<p>Sure, I can Get Things Done® on my iPhone or iPad, but by the time I&rsquo;ve finished working all day on my laptop, I rarely want to.</p>
<p>I suspect that might change as the operating system advances, but for now I&rsquo;m completely content with this usage pattern. Well, enough blathering on. To the apps!</p>
<h3 id="instacast">Instacast</h3>
<p>There are apps, there are good apps, and there are &ldquo;Man, I wish I wrote that&rdquo; apps. <a href="http://vemedio.com/products/instacast">Instacast</a> falls in the third category. Yes, it&rsquo;s <em>just</em> a podcast client. Yes, iOS has built-in podcast support. Yes, I&rsquo;m writing a lot of sentences that start with Yes. What&rsquo;s your point? Check out their video:</p>
<iframe src="http://player.vimeo.com/video/22829416?title=0&amp;byline=0&amp;portrait=0" width="500" height="281" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe><p><a href="http://vimeo.com/22829416">Introduction to Instacast</a> from <a href="http://vimeo.com/vemedio">Vemedio</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
<p>Instacast is so exquisitely crafted that the app actually makes me want to listen to more podcasts. I could enumerate all the little details that make Instacast a joy to use, but you should really just go buy it and try it for yourself.</p>
<h3 id="band-of-the-day">Band of the Day</h3>
<p>I&rsquo;ve always been one of those people who scour the Internet for new music to listen to, but there are often too few hours in the day to keep up. <a href="http://itunes.apple.com/us/app/band-of-the-day/id459664402?mt=8">Band of the Day</a> is like having your own personal new music concierge.</p>
<p>As the name implies, each day the app features a new band. The feature includes a biography, review, a few songs to stream for free, discography, et cetera.</p>
<p>Not every band featured strikes my personal chords, but that is to be expected. That being said, even the bands that I don&rsquo;t like are still talented and I think the BoD editors do a great job.</p>
<p><img src="http://jerodsanto.net/drop/band-of-the-day-20111226-145321.jpg" alt="Band of the Day"></p>
<p>The app has a distinct interface that I don&rsquo;t personally dig, but it has been heralded for that exact reason, so what do I know?</p>
<h3 id="flipboard">Flipboard</h3>
<p>I didn&rsquo;t give <a href="http://flipboard.com/">Flipboard</a> a fair chance the first time around, but somehow it reeled me back in and landed a huge fan. It is hands down my favorite way to just relax and surf the net. Better than any magazine that I could imagine.</p>
<p>They recently released an iPhone version — which I was skeptical of — and it is great as well.</p>
<h3 id="camera">Camera+</h3>
<p>This app was just kinda cool but I never used it. Then iOS 5 hit and with it Photo Stream. Photo Stream is a step in the right direction, <a href="https://jerodsanto.net/2011/12/a-photostream-problem-workaround-bonus/">but has a major flaw which Camera+ helps work around</a>. For that it&rsquo;s pretty great.</p>
<h2 id="os-x-apps">OS X Apps</h2>
<p>I make a living writing software on (and sometimes for) OS X, so the apps below are mostly writing tools.</p>
<h3 id="sublime-text-2">Sublime Text 2</h3>
<p>I&rsquo;m an old command-line Vim junkie, but when I first switched to a Mac I was wooed by the sexiness and intrigue of TextMate. It delivered on many of its promises, but its lack of updates and mythical version 2.0 (I know, now in Alpha) gave me a bit of wanderlust.</p>
<p>Many competitors came and went, but none of them lived up to — let alone surpassed — TextMate until <a href="http://www.sublimetext.com/2">Sublime Text 2</a>.</p>
<p><img src="http://jerodsanto.net/drop/sublimetext-20111229-204821.jpg" alt="Sublime Text 2 Screenshot"></p>
<p>ST2 looks great, is compatible with TextMate&rsquo;s bundles, has a great package manager, and best of all: the developer releases almost-monthly builds that keep on improving the editor.</p>
<p>I couldn&rsquo;t be more impressed and excited about this piece of software. It has become my default code editor of choice.</p>
<h3 id="ia-writer">iA Writer</h3>
<p>When I&rsquo;m not coding, I&rsquo;m often writing notes, blog posts, or documentation. In these cases I prefer to use <a href="http://daringfireball.net/projects/markdown/">Markdown</a> because it looks great in both plaintext and HTML format.</p>
<p>I&rsquo;m not unique in that regard, and many Markdown-specific editors have been cropping up in the Mac software scene these past few years. Of those, <a href="http://www.iawriter.com/">iA Writer</a> really stands out.</p>
<p>Much like <a href="http://vemedio.com/products/instacast">Instacast</a> makes me want to listen to more podcasts, <a href="http://www.iawriter.com/">iA Writer</a> makes me want to write more! It&rsquo;s so simple, and yet so elegant and thoughtful. One example of its thoughtfulness is that it provides not only word and character counts for the current document, but an estimated <em>reading</em> time. Love it.</p>
<h3 id="sparrow">Sparrow</h3>
<p>At first glance, <a href="http://sparrowmailapp.com/">Sparrow</a> looks like a Tweetie (remember Tweetie?) rip-off for email. Okay so it kind of is, but it&rsquo;s a really stinkin&rsquo; good one!</p>
<p>If you have multiple Gmail accounts and like getting your email out of a browser tab, then you should really give this app a try. It has a million and one little surprises and even supports Gmail-style keyboard shortcuts.</p>
<p>I can&rsquo;t wait for the iOS version&hellip;</p>
<h3 id="soulver">Soulver</h3>
<p>Nary a day goes by that I don&rsquo;t reach for a <a href="http://www.acqualia.com/soulver/">Soulver</a> window to perform some calculations. It&rsquo;s like a calculator on steroids. Really, really strong steroids.</p>
<p><img src="http://jerodsanto.net/drop/soulver-example-20111229-211421.jpg" alt="Soulver Screenshot"></p>
<p><a href="http://www.acqualia.com/soulver/">Soulver</a> is not a new app and I had heard people singing its praises for awhile, but I finally &ldquo;got it&rdquo; this year and it has become an essential tool in my toolbox.</p>
<h2 id="e-books">(e) Books</h2>
<p>I just got my first Kindle, so I suspect 2012&rsquo;s books section to have a little more meat on its bones than this year&rsquo;s. That being said, one book wowed me in 2011:</p>
<h3 id="designed-for-use">Designed for Use</h3>
<p><a href="http://ignorethecode.net/blog/">Ignore the Code</a> is one of the most insightful design-related blogs on the Internet, so when I heard that Lukas had published a book I snatched it up immiedately.</p>
<p>He did not disappoint.</p>
<p><a href="http://pragprog.com/book/lmuse/designed-for-use">Designed for Use</a> is eye-opening, practical, and well organized. It breaks out into three sections: research, design, and implementation. Of these, the section on design really hit home, but the entire book is highly recommended.</p>
<h2 id="podcasts">Podcasts</h2>
<p>I subscribe to 20 podcasts and am always on the lookout for new stuff. For me, 2011 has been the year of <a href="http://5by5.tv">5by5</a>, but I&rsquo;ll only include my favorite <a href="http://5by5.tv">5by5</a> so I don&rsquo;t come off as a total fan<strike>boy</strike>man.</p>
<h3 id="hypercritical">Hypercritical</h3>
<p><a href="http://5by5.tv/hypercritical">Hypercritical</a> is co-hosted by <a href="http://twitter.com/danbenjamin">Dan Benjamin</a> and <a href="http://twitter.com/siracusa">John Siracusa</a> (You may know John from his <a href="http://arstechnica.com/author/john-siracusa/">epicly long Mac OS X reviews</a>).</p>
<p>The show is basically a venue for John to rail on stuff. That doesn&rsquo;t sound too awesome, but he is so particular, angry, and mostly correct that it&rsquo;s actually quite entertaining.</p>
<p><a href="http://5by5.tv/hypercritical"><img src="http://jerodsanto.net/drop/hypercritical-large-20111231-112442.jpg" alt="Hypercritical logo"></a></p>
<p><a href="http://5by5.tv/hypercritical">Hypercritical</a> focuses mostly around Apple and related topics, but John does not discriminate against what or whom he will tear in to. TiVO, George Lucas, and even toaster ovens have all been subject to his wrath.</p>
<p>If you&rsquo;re at all interested, listen to <a href="http://5by5.tv/hypercritical/42">The Wrong Guy</a>, where John rips Walter Isaacson&rsquo;s Steve Jobs biography to shreds. It is hilarious.</p>
<p>Also, <a href="http://www.siracusasaidso.com/">this</a>.</p>
<h3 id="the-moth-podcast">The Moth Podcast</h3>
<p><a href="http://themoth.org/">The Moth</a> is a great little podcast featuring stories told by great story tellers. It&rsquo;s a lot like <a href="http://www.thisamericanlife.org/">This American Life</a>, but in 1-15 minute chunks.</p>
<p>Stand out stories include <a href="http://wehearus.com/podcasts/episode/34016">Rare Romance, Well-Done Marriage</a> by Adam Gopnik and <a href="http://www.nateloucks.com/?p=684">Best of Times, Worst of Times</a> by Anthony Griffith (links go to 3rd parties because the originals seem to have been purged).</p>
<h3 id="ruby-rogues">Ruby Rogues</h3>
<p>The <a href="http://rubyrogues.com/">Ruby Rogues</a> is a round table discussion show. They aren&rsquo;t afraid to dive head first into deep technical discussions, and I&rsquo;ve been able to glean quite a few programming tips from them.</p>
<p>Each episode focuses on a single topic and concludes with each rogue picking 1 or more things they&rsquo;re in to, whether programming related or otherwise.</p>
<p>Pro tip for this show: read the picks in the show notes before listening so you can skip the last 20+ minutes of the show.</p>
<h2 id="honorable-mentions">Honorable Mentions</h2>
<p>Stuff that didn&rsquo;t quite make the list, or didn&rsquo;t fit smoothly into a category:</p>
<ul>
<li><a href="http://www.theheadandtheheart.com/">The Head and the Heart</a></li>
<li><a href="http://kishibashi.com/">Kishi Bashi</a></li>
<li><a href="http://www.deadmau5.com/">Deadmau5</a></li>
<li><a href="http://python.org/">Python</a></li>
<li><a href="http://coffeescript.org/">CoffeeScript</a></li>
<li><a href="http://redis.io">Redis</a></li>
<li><a href="http://www.postgresql.org/">PostgreSQL</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>A Photo Stream Problem, Workaround, &amp;amp; Bonus</title>
      <link>https://jerodsanto.net/2011/12/a-photo-stream-problem-workaround-amp-bonus/</link>
      <pubDate>Sun, 11 Dec 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/12/a-photo-stream-problem-workaround-amp-bonus/</guid>
      
      
      <description><![CDATA[<p>iOS 5&rsquo;s <a href="http://www.apple.com/icloud/features/photo-stream.html">Photo Stream</a> feature — in which your photos are synced across devices via iCloud — is pretty cool, but like most new software it has its limitations.</p>
<h2 id="a-problem">A Problem</h2>
<p>One major problem with Photo Stream is that it uploads <em>everything</em> that hits your device&rsquo;s Camera Roll. That&rsquo;d be great if all we ever took were amazing photographs. But alas, many of the images captured by our phones just aren&rsquo;t worth the effort, like this one taken by my three year old daughter (which was then Photo Streamed to all my machines):</p>]]></description>
      
      <content:encoded><![CDATA[<p>iOS 5&rsquo;s <a href="http://www.apple.com/icloud/features/photo-stream.html">Photo Stream</a> feature — in which your photos are synced across devices via iCloud — is pretty cool, but like most new software it has its limitations.</p>
<h2 id="a-problem">A Problem</h2>
<p>One major problem with Photo Stream is that it uploads <em>everything</em> that hits your device&rsquo;s Camera Roll. That&rsquo;d be great if all we ever took were amazing photographs. But alas, many of the images captured by our phones just aren&rsquo;t worth the effort, like this one taken by my three year old daughter (which was then Photo Streamed to all my machines):</p>
<p><img src="http://jerodsanto.net/drop/IMG_1077-20111211-152020.jpg" alt="Lyla shot"></p>
<p>Not. Ideal.</p>
<h2 id="a-workaround">A Workaround</h2>
<p>For awhile now, I&rsquo;ve been using a simple workaround for this inadequacy. It&rsquo;s not revolutionary or anything, but everybody I tell about it seems to have one of those &ldquo;Aha!&rdquo; moments, so I figured it&rsquo;d be worth sharing here.</p>
<p>As far as I know, you can&rsquo;t configure Apple&rsquo;s Camera app to dump some pictures in Camera Roll and other pictures elsewhere. So the only way to Photo Stream some of your photos and not others is to introduce a middle man.</p>
<p>For this, I now use the massively popular <a href="http://campl.us/">Camera+</a> for <em>all</em> my picture taking. The great thing about Camera+ (and other 3rd party camera apps) is that it has its own image storage outside of the Camera Roll.</p>
<p>After I&rsquo;ve captured a particularly good picture and want to have it streamed to my Mac and iPad, I save it from Camera+ to the Camera Roll. Off it goes.</p>
<p>Unfortunately, this solution doesn&rsquo;t work in every scenario. For instance, you can&rsquo;t route your screen captures, saved SMS messages, or Mobile Safari&rsquo;s downloaded images to Camera+. They get Photo Streamed immediately.</p>
<p>If you know of a workaround for those scenarios, please post it in the comments.</p>
<h2 id="a-bonus">A Bonus</h2>
<p>I don&rsquo;t know about you, but I have a recurring problem with the Camera app where it&rsquo;s always in <em>video mode</em> when I want to take a picture and in <em>picture mode</em> when I want to take a video. Gah!</p>
<p>However, now that I&rsquo;ve completely replaced Camera&rsquo;s picture taking function with Camera+, I can leave Camera in video mode <strong>all the time</strong>.</p>
<p><img src="http://jerodsanto.net/drop/IMG_1076-20111211-151253.jpg" alt="side by side"></p>
<p>The two apps live side-by-side on my homescreen and I simply launch the one I want depending on whether I want to shoot still images or video. No more frustrating missed opportunities.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>A Kindle Cloud Reader Bookmarklet to Enable Text Selection, Copy/Paste, Et Cetera</title>
      <link>https://jerodsanto.net/2011/08/a-kindle-cloud-reader-bookmarklet-to-enable-text-selection-copy/paste-et-cetera/</link>
      <pubDate>Wed, 10 Aug 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/08/a-kindle-cloud-reader-bookmarklet-to-enable-text-selection-copy/paste-et-cetera/</guid>
      
      
      <description><![CDATA[<p>Amazon&rsquo;s new <a href="https://read.amazon.com">Kindle Cloud Reader</a> is a really cool web app and yet another step toward the web playing a huge role in mobile like it does on the desktop.</p>
<p>But.</p>
<p>Amazon has crippled a few features that we web denizens come to expect and rely upon. No copy/paste? No highlighting text as I read it? No context menus to look up a word in a dictionary, clip to <a href="http://evernote.com/">Evernote</a> or whathaveyou?</p>]]></description>
      
      <content:encoded><![CDATA[<p>Amazon&rsquo;s new <a href="https://read.amazon.com">Kindle Cloud Reader</a> is a really cool web app and yet another step toward the web playing a huge role in mobile like it does on the desktop.</p>
<p>But.</p>
<p>Amazon has crippled a few features that we web denizens come to expect and rely upon. No copy/paste? No highlighting text as I read it? No context menus to look up a word in a dictionary, clip to <a href="http://evernote.com/">Evernote</a> or whathaveyou?</p>
<p><a href="http://www.imdb.com/title/tt0118715/quotes?qt=qt0464827">This will not stand, ya know, this aggression will not stand, man.</a></p>
<p>So I hacked together a bookmarklet which gives your browser those features back. Drag the link below to your bookmarks bar and click it after you load a book in Cloud Reader:</p>
<div class="bookmarklet">
  <a href="javascript:(function(){function c(){var a=this.contentWindow.document.getElementsByTagName("body")[0];this.contentWindow.onclick=null;a._frame=this;a.onmousemove=function(){this._frame.contentWindow.onclick=null;this.setAttribute("style","-webkit-user-select: auto;");this.oncontextmenu=a.onselectstart=null};a.onmousemove()}for(var b=document.getElementsByTagName("iframe")[0].contentWindow.document.getElementsByTagName("iframe"),a=0;a<b.length;a++)c.call(b[a]),b[a].onload=c})();">Uncripple Kindle</a>
</div>
<p>It&rsquo;s quite possible that the bookmarklet will break as Amazon makes changes to the app, so I&rsquo;ve put the <a href="https://gist.github.com/1137337">source on GitHub</a> for all to participate in its maintenance.</p>
<div class="alert">Amazon has moved too fast for me to keep up. This bookmarklet is mostly likely broken and I won&rsquo;t be working on it further. Sorry! :(</div>
<p>Oh, and I only tested it on Chrome 14 (Mac) and Safari 5.1. I have confirmed that it is NOT working on dev Chrome (currently 16). YMMV.</p>
<p>Happy reading!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Brew Install Vim</title>
      <link>https://jerodsanto.net/2011/08/brew-install-vim/</link>
      <pubDate>Thu, 04 Aug 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/08/brew-install-vim/</guid>
      
      
      <description><![CDATA[<p>OS X users who prefer command-line Vim over MacVim (I&rsquo;m sure we&rsquo;re few and far between) may become frustrated with the lack of features enabled in Apple&rsquo;s stock <code>/usr/bin/vim</code>. For me, seeing <code>-clipboard</code> and <code>-ruby</code> in my <code>vim --version</code> just wasn&rsquo;t gonna cut it.</p>
<p>Turning to <a href="http://mxcl.github.com/homebrew/">Homebrew &ldquo;proper&rdquo;</a> for a solution would only further that frustration:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">brew search vim
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="go">If you meant `vim&#39; precisely:
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="go">Apple distributes vim with OS X, you can find it in /usr/bin.
</span></span></span></code></pre></div><p>Thankfully, there&rsquo;s a great <a href="https://github.com/adamv/homebrew-alt">unofficial repository</a> of alternative Homebrew formulae to fill the void!</p>]]></description>
      
      <content:encoded><![CDATA[<p>OS X users who prefer command-line Vim over MacVim (I&rsquo;m sure we&rsquo;re few and far between) may become frustrated with the lack of features enabled in Apple&rsquo;s stock <code>/usr/bin/vim</code>. For me, seeing <code>-clipboard</code> and <code>-ruby</code> in my <code>vim --version</code> just wasn&rsquo;t gonna cut it.</p>
<p>Turning to <a href="http://mxcl.github.com/homebrew/">Homebrew &ldquo;proper&rdquo;</a> for a solution would only further that frustration:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">brew search vim
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="go">If you meant `vim&#39; precisely:
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="go">Apple distributes vim with OS X, you can find it in /usr/bin.
</span></span></span></code></pre></div><p>Thankfully, there&rsquo;s a great <a href="https://github.com/adamv/homebrew-alt">unofficial repository</a> of alternative Homebrew formulae to fill the void!</p>
<p>In it is a formula that will compile a feature-rich version of Vim. You can install it by cloning the repo and pointing to the formula on your local file system or by simply pointing <code>brew install</code> at the raw URL, like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">brew install https://raw.github.com/adamv/homebrew-alt/master/duplicates/vim.rb
</span></span></span></code></pre></div><p>Seriously, do it. Do it. DO IT.</p>
<div style="width: 560px; margin: 0 auto;">
<iframe width="560" height="349" src="http://www.youtube.com/embed/JoqDYcCDOTg" frameborder="0" align="center"></iframe>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>A Better Way to Get Recency-based Google Searches in Chrome</title>
      <link>https://jerodsanto.net/2011/08/a-better-way-to-get-recency-based-google-searches-in-chrome/</link>
      <pubDate>Tue, 02 Aug 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/08/a-better-way-to-get-recency-based-google-searches-in-chrome/</guid>
      
      
      <description><![CDATA[<p>Last week I shared <a href="https://jerodsanto.net/2011/07/my-dotjs-hack-to-default-google-searches-to-past-year/">my Dotjs hack</a> which forces Chrome&rsquo;s Omnibar searches to use Google&rsquo;s &ldquo;past year&rdquo; parameter. Since then I&rsquo;ve learned a few things:</p>
<ol>
<li>My JavaScript-based solution creates a blip effect because the page first loads a regular search and then immediately updates to load the &ldquo;past year&rdquo; search. That blip is annoying.</li>
<li>Even though I quite often want a recency-based search, there are plenty of times that I don&rsquo;t. I&rsquo;d rather opt-in than opt-out.</li>
<li>There is <a href="https://jerodsanto.net/2011/07/my-dotjs-hack-to-default-google-searches-to-past-year/#comment-266238490">probably a better way</a>.</li>
</ol>
<p>I Combined those new bits of knowledge and came up with a better way to get recency-based Google searches in Chrome: <em>add a custom search engine</em>.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Last week I shared <a href="https://jerodsanto.net/2011/07/my-dotjs-hack-to-default-google-searches-to-past-year/">my Dotjs hack</a> which forces Chrome&rsquo;s Omnibar searches to use Google&rsquo;s &ldquo;past year&rdquo; parameter. Since then I&rsquo;ve learned a few things:</p>
<ol>
<li>My JavaScript-based solution creates a blip effect because the page first loads a regular search and then immediately updates to load the &ldquo;past year&rdquo; search. That blip is annoying.</li>
<li>Even though I quite often want a recency-based search, there are plenty of times that I don&rsquo;t. I&rsquo;d rather opt-in than opt-out.</li>
<li>There is <a href="https://jerodsanto.net/2011/07/my-dotjs-hack-to-default-google-searches-to-past-year/#comment-266238490">probably a better way</a>.</li>
</ol>
<p>I Combined those new bits of knowledge and came up with a better way to get recency-based Google searches in Chrome: <em>add a custom search engine</em>.</p>
<p>Here&rsquo;s how.</p>
<ol>
<li>Open Chrome&rsquo;s preferences and navigate to Basics =&gt; Search =&gt; Manage Search Engines</li>
<li>Add a new search engine under the list of &ldquo;Other search engines&rdquo; with the following values.</li>
</ol>
<p><strong>Name:</strong> &ldquo;Google (recent)&rdquo; or whatever makes sense to you</p>
<p><strong>Keyword:</strong> &ldquo;recent&rdquo; or something else short and easily typed</p>
<p><strong>URL:</strong> <a href="http://www.google.com/search?q=%25s&amp;tbs=qdr:y&amp;tbo=1">http://www.google.com/search?q=%s&amp;tbs=qdr:y&amp;tbo=1</a></p>
<p>See my list of other search engines below:</p>
<p><img src="http://jerodsanto.net/drop/other-search-engines-20110802-084545.jpg" alt="other search engines"></p>
<p>When you want to use it, start typing the search engine&rsquo;s keyword into the Omnibar and hit the &ldquo;tab&rdquo; key. Chrome will select the appropriate search engine.</p>
<p><img src="http://jerodsanto.net/drop/google-recent-search-20110802-083728.jpg" alt="recent search example"></p>
<p>That&rsquo;s all there is to it. Better, no?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>My Dotjs Hack to Default Google Searches to Past Year</title>
      <link>https://jerodsanto.net/2011/07/my-dotjs-hack-to-default-google-searches-to-past-year/</link>
      <pubDate>Tue, 26 Jul 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/07/my-dotjs-hack-to-default-google-searches-to-past-year/</guid>
      
      
      <description><![CDATA[<div class="alert">UPDATE: I&rsquo;ve since moved to <a href="https://jerodsanto.net/2011/08/a-better-way-to-get-recency-based-google-searches-in-chrome/">a better way</a> of doing this. Please read that first/instead.</div>
<p>Sick of clicking on &ldquo;More tools&rdquo; and &ldquo;Past year&rdquo; after almost all of your Google searches? I was, so I hacked together a <a href="http://defunkt.io/dotjs/">dotjs</a> quickie which adds the necessary params if they aren&rsquo;t already provided.</p>
<div class="notice">Disclaimer: this has only been tested on searches performed from the omnibar. I can&rsquo;t remember the last time I visited google.com.</div>
<p>First, use Chrome as your browser. Next, install dotjs. Finally, drop this snippet into <code>~/.js/google.com.js</code>:</p>]]></description>
      
      <content:encoded><![CDATA[<div class="alert">UPDATE: I&rsquo;ve since moved to <a href="https://jerodsanto.net/2011/08/a-better-way-to-get-recency-based-google-searches-in-chrome/">a better way</a> of doing this. Please read that first/instead.</div>
<p>Sick of clicking on &ldquo;More tools&rdquo; and &ldquo;Past year&rdquo; after almost all of your Google searches? I was, so I hacked together a <a href="http://defunkt.io/dotjs/">dotjs</a> quickie which adds the necessary params if they aren&rsquo;t already provided.</p>
<div class="notice">Disclaimer: this has only been tested on searches performed from the omnibar. I can&rsquo;t remember the last time I visited google.com.</div>
<p>First, use Chrome as your browser. Next, install dotjs. Finally, drop this snippet into <code>~/.js/google.com.js</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">pathname</span> <span class="o">==</span> <span class="s2">&#34;/search&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">href</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="s2">&#34;&amp;tbs=&#34;</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">href</span> <span class="o">+=</span> <span class="s2">&#34;&amp;tbs=qdr:y&amp;tbo=1&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>If you prefer your timely searches to be constrained differently, change the <code>tbs</code> param as follows:</p>
<ul>
<li>month = qdr:m</li>
<li>week  = qdr:w</li>
<li>day   = qdr:d</li>
</ul>
<p>Recency is a big deal. One of these days Google will allow this as a setting in our search preferences. Until then, JavaScript FTW!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>The Coldplay Effect</title>
      <link>https://jerodsanto.net/2011/07/the-coldplay-effect/</link>
      <pubDate>Mon, 25 Jul 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/07/the-coldplay-effect/</guid>
      
      
      <description><![CDATA[<p>I thought I&rsquo;d share an interesting term I picked up last week from <a href="http://twitter.com/ashenden">John Ashenden</a> of Grooveshark: The Coldplay Effect</p>
<p>As an Internet radio listener, your like/dislike history and recently played music are used to determine which songs to play next (it&rsquo;s obviously much more complicated than that, but I&rsquo;m speaking in generalities here, so back off!). Your most recently played songs are weighted the heaviest. That makes sense. It is of little consequence that you like The Beach Boys when you&rsquo;ve been listening to Deadmau5 and the Aphex Twins for the last two hours.</p>]]></description>
      
      <content:encoded><![CDATA[<p>I thought I&rsquo;d share an interesting term I picked up last week from <a href="http://twitter.com/ashenden">John Ashenden</a> of Grooveshark: The Coldplay Effect</p>
<p>As an Internet radio listener, your like/dislike history and recently played music are used to determine which songs to play next (it&rsquo;s obviously much more complicated than that, but I&rsquo;m speaking in generalities here, so back off!). Your most recently played songs are weighted the heaviest. That makes sense. It is of little consequence that you like The Beach Boys when you&rsquo;ve been listening to Deadmau5 and the Aphex Twins for the last two hours.</p>
<p>This data is compared to songs in the provider&rsquo;s catalog and (again, using a complex algorithm) songs are picked based on their level of &ldquo;likeness&rdquo; to what you currently deem play-worthy. I&rsquo;m not sure exactly how song likeness is determined, but it&rsquo;s probably a combination of genre, beats per minute, and many other characteristics that can be expressed mathematically.</p>
<p>The Coldplay Effect is an unfortunate phenomenon that arises when certain artists&rsquo; music (like Coldplay&rsquo;s) ranks high for likeness across diverse genres. It doesn&rsquo;t matter that you are on an electronic trance binge, because some Coldplay song is <em>just enough</em> like the third track on the Fight Club soundtrack to bring it into your queue.</p>
<p>Once the Coldplay track plays, it&rsquo;s all over. Your previously specialized stream has been normalized and pretty much anything can play next.</p>
<p>This seems like a Hard Problem™, and I don&rsquo;t think anybody has solved it just yet. Perhaps this — along with other problems faced by programmatic music recommendation engines — plays into the <a href="http://gadgetwise.blogs.nytimes.com/2011/07/20/spotify-is-great-but-turntable-fm-is-amazing/">recent praise</a> of <a href="http://turntable.fm/">TurnTable.fm</a>, which uses humans to pick which songs play next.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Building Multi-tenant Rails Apps with PostgreSQL Schemas</title>
      <link>https://jerodsanto.net/2011/07/building-multi-tenant-rails-apps-with-postgresql-schemas/</link>
      <pubDate>Mon, 18 Jul 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/07/building-multi-tenant-rails-apps-with-postgresql-schemas/</guid>
      
      
      <description><![CDATA[<p>Many Rails apps need to accommodate multiple tenants. There are a few different ways to go about this, each with their set of pros and cons. Guy Naor did a great job of diving into the pros and cons of each strategy in his <a href="http://confreaks.net/videos/111-aac2009-writing-multi-tenant-applications-in-rails">2009 Acts As Conference talk</a> (a must-watch).</p>
<p>One of the multi-tenant strategies he presented takes advantage of a feature specific to <a href="http://www.postgresql.org">PostgreSQL</a> called &ldquo;Schemas&rdquo;. His talk was technical, but didn&rsquo;t go in-depth into implementation details. There are a few blog posts, <a href="http://stackoverflow.com/questions/2782758/creating-a-multi-tenant-application-using-postgresqls-schemas-and-rails">Stack Overflow</a> threads, and other semi-related flotsam around the tubes on how to actually accomplish a multi-tenant app using this strategy, but I still had to figure out many things on my own so I figured I&rsquo;d document the setup.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Many Rails apps need to accommodate multiple tenants. There are a few different ways to go about this, each with their set of pros and cons. Guy Naor did a great job of diving into the pros and cons of each strategy in his <a href="http://confreaks.net/videos/111-aac2009-writing-multi-tenant-applications-in-rails">2009 Acts As Conference talk</a> (a must-watch).</p>
<p>One of the multi-tenant strategies he presented takes advantage of a feature specific to <a href="http://www.postgresql.org">PostgreSQL</a> called &ldquo;Schemas&rdquo;. His talk was technical, but didn&rsquo;t go in-depth into implementation details. There are a few blog posts, <a href="http://stackoverflow.com/questions/2782758/creating-a-multi-tenant-application-using-postgresqls-schemas-and-rails">Stack Overflow</a> threads, and other semi-related flotsam around the tubes on how to actually accomplish a multi-tenant app using this strategy, but I still had to figure out many things on my own so I figured I&rsquo;d document the setup.</p>
<h2 id="why-postgresql-schemas">Why PostgreSQL Schemas</h2>
<p>Guy laid out three basic strategies for multi-tenant Rails apps.</p>
<ol>
<li>Separate databases for each tenant</li>
<li>One database with records scoped through a &ldquo;tenant&rdquo; relationship</li>
<li>One database with separate schemas for each tenant (PostgreSQL only)</li>
</ol>
<p>I won&rsquo;t lay out all the factors in choosing a multi-tenant strategy, but I&rsquo;ll tell you when you might want to choose strategy #3. If your app has the following characteristics:</p>
<ol>
<li>Each tenant&rsquo;s data is private and should not be leaked across tenants</li>
<li>You have little or no need to run queries that aggregate across all tenants</li>
<li>You may have many tenants and can&rsquo;t handle high administration overhead</li>
</ol>
<p>There are, of course, nuances to every application so, seriously, <a href="http://confreaks.net/videos/111-aac2009-writing-multi-tenant-applications-in-rails">go watch his talk</a> and make the decision on your own. If you decide you&rsquo;d like to go the PostgreSQL Schema route, come back and finish reading this post.</p>
<h2 id="how-postgresql-schemas-work">How PostgreSQL Schemas Work</h2>
<p>&ldquo;Schema&rdquo; is such a terrible name for this feature. When most people hear the term &ldquo;schema&rdquo; they think of a data definition of some sort. This is not what <a href="http://www.postgresql.org/docs/9.0/static/ddl-schemas.html">PostgreSQL schemas</a> are. I&rsquo;m sure the PostgreSQL devs had their reasons, but I really wish they would have named it more appropriately. &ldquo;Namespaces&rdquo; would have been apropos.</p>
<p>Anywho, the easiest way for me to describe PostgreSQL schemas (besides telling you that they are, indeed, namespaces for tables) is to relate them to the UNIX execution path. When you run a UNIX command without specifying its absolute path, your shell will work its way down the <code>$PATH</code> until it finds an executable of the same name.</p>
<p>Given that your <code>$PATH</code> looks like this:</p>
<p><code>/usr/local/bin:/usr/bin</code></p>
<p>When you type <code>vim</code> your shell will look for <code>vim</code> in <code>/usr/local/bin</code> and then in <code>/usr/bin</code> before giving up.</p>
<p>PostgreSQL schemas work pretty much the same way. Every table in a PostgreSQL database belongs to a schema. By default tables go in the <code>public</code> schema. You can see the current schema search path in psql by executing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SHOW</span><span class="w"> </span><span class="n">search_path</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>If you haven&rsquo;t done any schema-related stuff yet, you&rsquo;ll see <code>&quot;$user&quot;,public</code> as the search path. This means when you query a table without explicitly specifying the table&rsquo;s <strike>namespace</strike> schema it will first look in your user&rsquo;s schema (every user gets one) and then in the public schema before giving up. That means that:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="k">count</span><span class="p">(</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">users</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>Is functionally equivalent to:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="k">count</span><span class="p">(</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="k">public</span><span class="p">.</span><span class="n">users</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>What does all this mean? It means you can have the same table many times in one database as long as they each live in their own schema and you can query those tables without having to explicitly state which schema they&rsquo;re in. You just have to set the schema search path appropriately and PostgreSQL will handle the rest.</p>
<p>In other words, you get data separation across tenants by modifying very little of your application logic!</p>
<h2 id="setting-the-search-path">Setting the Search Path</h2>
<p>So how do you do all that in Rails?</p>
<div class="notice">NOTE: This implementation targets Rails 3.0.9 and PostgreSQL 9.0.4. YMMV</div>
<p>First, let&rsquo;s assume you have a <code>Tenant</code> model which holds a unique subdomain string. When an HTTP request comes in with a subdomain in it, you find the appropriate tenant and use its primary key (<code>id</code>) to set the search path (you could use the subdomain itself, but you may want to allow your users to change their subdomain).</p>
<p>This logic should happen on every request so put it in a <code>before_filter</code> in your <code>ApplicationController</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_filter</span> <span class="ss">:handle_subdomain</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_subdomain</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@tenant</span> <span class="o">=</span> <span class="no">Tenant</span><span class="o">.</span><span class="n">find_by_subdomain</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">subdomain</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="no">PgTools</span><span class="o">.</span><span class="n">set_search_path</span> <span class="vi">@tenant</span><span class="o">.</span><span class="n">id</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="no">PgTools</span><span class="o">.</span><span class="n">restore_default_search_path</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>The logic isn&rsquo;t difficult to follow. You set the search path to the matched tenant if one is found. Otherwise you restore the default search path. The database bits are tucked away inside <code>PgTools</code>, which is a very small module you can drop into <code>lib/</code>. Here are the relevant methods:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">PgTools</span>
</span></span><span class="line"><span class="cl">  <span class="kp">extend</span> <span class="nb">self</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">search_path</span>
</span></span><span class="line"><span class="cl">    <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">connection</span><span class="o">.</span><span class="n">schema_search_path</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">default_search_path</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@default_search_path</span> <span class="o">||=</span> <span class="sx">%{&#34;$user&#34;, public}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">set_search_path</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="n">include_public</span> <span class="o">=</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">path_parts</span> <span class="o">=</span> <span class="o">[</span><span class="nb">name</span><span class="o">.</span><span class="n">to_s</span><span class="p">,</span> <span class="p">(</span><span class="s2">&#34;public&#34;</span> <span class="k">if</span> <span class="n">include_public</span><span class="p">)</span><span class="o">].</span><span class="n">compact</span>
</span></span><span class="line"><span class="cl">    <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">connection</span><span class="o">.</span><span class="n">schema_search_path</span> <span class="o">=</span> <span class="n">path_parts</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;,&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">restore_default_search_path</span>
</span></span><span class="line"><span class="cl">    <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">connection</span><span class="o">.</span><span class="n">schema_search_path</span> <span class="o">=</span> <span class="n">default_search_path</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>These methods are pretty self-explanatory, but I will point out that the <code>set_search_path</code> method will include the public search path by default, but it can be disabled by passing <code>false</code> as a second parameter to the method. This will come into play a little further down.</p>
<p>In the case of a tenant with id of &ldquo;4&rdquo;, at the end of your <code>handle_subdomain</code> method the PostgreSQL schema search path will look like: <code>4, public</code></p>
<p>For the remainder of the request all of your queries will be sent through the &ldquo;4&rdquo; schema first as long as you have tables in it to be used. So how do you get all the tables in each tenant&rsquo;s schema?</p>
<h2 id="adding-new-tenants">Adding New Tenants</h2>
<p>The current database table definitions need to be loaded into the private schema of each new tenant of the system. You can perform this task in a callback after the tenant record has been created. Something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Tenant</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">after_create</span> <span class="ss">:prepare_tenant</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">prepare_tenant</span>
</span></span><span class="line"><span class="cl">    <span class="n">create_schema</span>
</span></span><span class="line"><span class="cl">    <span class="n">load_tables</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create_schema</span>
</span></span><span class="line"><span class="cl">    <span class="no">PgTools</span><span class="o">.</span><span class="n">create_schema</span> <span class="nb">id</span> <span class="k">unless</span> <span class="no">PgTools</span><span class="o">.</span><span class="n">schemas</span><span class="o">.</span><span class="n">include?</span> <span class="nb">id</span><span class="o">.</span><span class="n">to_s</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">load_tables</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">if</span> <span class="no">Rails</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">test?</span>
</span></span><span class="line"><span class="cl">    <span class="no">PgTools</span><span class="o">.</span><span class="n">set_search_path</span> <span class="nb">id</span><span class="p">,</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl">    <span class="nb">load</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">Rails</span><span class="o">.</span><span class="n">root</span><span class="si">}</span><span class="s2">/db/schema.rb&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="no">PgTools</span><span class="o">.</span><span class="n">restore_default_search_path</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>After the <code>create_schema</code> method ensures that the new tenant has their own schema, the <code>load_tables</code> method sets the search path to its schema and loads the &ldquo;schema.rb&rdquo; file. You may notice that this time <code>false</code> is being sent to the <code>set_search_path</code> method. That is because you only want the loaded &ldquo;schema.rb&rdquo; file to affect the tenant&rsquo;s schema, NOT the public schema.</p>
<div class="notice">NOTE: This code may take awhile to execute and should be run in a background process.</div>
<p>Here you&rsquo;re using two more methods from the <code>PgTools</code> module. Here are their implementations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">create_schema</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">sql</span> <span class="o">=</span> <span class="sx">%{CREATE SCHEMA &#34;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="sx">&#34;}</span>
</span></span><span class="line"><span class="cl">  <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">connection</span><span class="o">.</span><span class="n">execute</span> <span class="n">sql</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">schemas</span>
</span></span><span class="line"><span class="cl">  <span class="n">sql</span> <span class="o">=</span> <span class="s2">&#34;SELECT nspname FROM pg_namespace WHERE nspname !~ &#39;^pg_.*&#39;&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">connection</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">sql</span><span class="p">)</span><span class="o">.</span><span class="n">flatten</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>At this point you&rsquo;ve accomplished the bulk of the logic that goes into a multi-tenant Rails app with PostgreSQL schemas, but there are a few other things that you&rsquo;ll want to be aware of.</p>
<h2 id="migrating-tenants">Migrating Tenants</h2>
<p>Since every tenant has their own set of tables, it is no longer good enough to just run <code>rake db:migrate</code> to make database changes. Instead, each tenant must have its schema&rsquo;s tables migrated.</p>
<p>This isn&rsquo;t too bad, you just need a custom rake task which loops over the <code>tenants</code> table, setting the schema search path and migrating the database. Add this to <code>lib/tasks/tenants.rake</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">namespace</span> <span class="ss">:tenants</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">namespace</span> <span class="ss">:db</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">desc</span> <span class="s2">&#34;runs db:migrate on each tenant&#39;s private schema&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">task</span> <span class="ss">migrate</span><span class="p">:</span> <span class="ss">:environment</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">verbose</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s2">&#34;VERBOSE&#34;</span><span class="o">]</span> <span class="p">?</span> <span class="no">ENV</span><span class="o">[</span><span class="s2">&#34;VERBOSE&#34;</span><span class="o">]</span> <span class="o">==</span> <span class="s2">&#34;true&#34;</span> <span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">      <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span><span class="o">.</span><span class="n">verbose</span> <span class="o">=</span> <span class="n">verbose</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="no">Tenant</span><span class="o">.</span><span class="n">all</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">tenant</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="nb">puts</span> <span class="s2">&#34;migrating tenant </span><span class="si">#{</span><span class="n">tenant</span><span class="o">.</span><span class="n">id</span><span class="si">}</span><span class="s2"> (</span><span class="si">#{</span><span class="n">tenant</span><span class="o">.</span><span class="n">subdomain</span><span class="si">}</span><span class="s2">)&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="no">PgTools</span><span class="o">.</span><span class="n">set_search_path</span> <span class="n">tenant</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl">        <span class="n">version</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s2">&#34;VERSION&#34;</span><span class="o">]</span> <span class="p">?</span> <span class="no">ENV</span><span class="o">[</span><span class="s2">&#34;VERSION&#34;</span><span class="o">].</span><span class="n">to_i</span> <span class="p">:</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">        <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migrator</span><span class="o">.</span><span class="n">migrate</span><span class="p">(</span><span class="s2">&#34;db/migrate/&#34;</span><span class="p">,</span> <span class="n">version</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Be sure to run this in addition to <code>rake db:migrate</code>. You may want to hook it in to the <code>db:migrate</code> task somehow. If you do, please add your solution to the comments as I have not done this yet.</p>
<p>This should also be hooked into your deploy process. If you&rsquo;re using capistrano to deploy, you can add it like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">namespace</span> <span class="ss">:db</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">desc</span> <span class="s2">&#34;Runs rake task which migrates database tables for all tenants&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">task</span> <span class="ss">:migrate_tenants</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">env</span> <span class="o">=</span> <span class="s2">&#34;RAILS_ENV=production&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">run</span> <span class="s2">&#34;cd </span><span class="si">#{</span><span class="n">release_path</span><span class="si">}</span><span class="s2"> &amp;&amp; bundle exec rake </span><span class="si">#{</span><span class="n">env</span><span class="si">}</span><span class="s2"> tenants:db:migrate&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">after</span> <span class="s2">&#34;deploy:migrate&#34;</span><span class="p">,</span> <span class="s2">&#34;db:migrate_tenants&#34;</span>
</span></span></code></pre></div><p>That should do it!</p>
<h2 id="shared-tables">Shared Tables</h2>
<p>So far this post has just compiled and distilled information available from various sources, but one thing that nobody else seems to be talking about is that not <em>all</em> of your application&rsquo;s tables will be private to each tenant.</p>
<p>The <code>tenants</code> table itself, for example, needs to be accessible to all tenants (how else will they edit their account settings?). Also, what if you want users to be able to log in to multiple tenants across the system (single sign-on)?</p>
<p>One way to accomplish this is to have shared tables live in the public schema and private tables live in the tenant-specific schemas. Technically, all tables must exist in the public schema for Rails to boot properly. This is not a problem. Since the search path is being set to look in the private schemas first it will find the tables there and use the right table. However, if a private schema has a <code>tenants</code> table and the public schema has the <code>tenants</code> table with data in it, the wrong one will be used.</p>
<p>One solution is to delete the shared tables from the private schemas. This will ensure that the search path won&rsquo;t find them in the private schemas and will find them in the public schema.</p>
<p>To accomplish this, I maintain a list of shared tables (this is kind of hacky, but the list is short) and modify the <code>Tenant#load_tables</code> method to look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">load_tables</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="k">if</span> <span class="no">Rails</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">test?</span>
</span></span><span class="line"><span class="cl">  <span class="no">PgTools</span><span class="o">.</span><span class="n">set_search_path</span> <span class="nb">id</span><span class="p">,</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl">  <span class="nb">load</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">Rails</span><span class="o">.</span><span class="n">root</span><span class="si">}</span><span class="s2">/db/schema.rb&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="no">MyApp</span><span class="o">::</span><span class="no">SHARED_TABLES</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="nb">name</span><span class="o">|</span> <span class="n">connection</span><span class="o">.</span><span class="n">execute</span> <span class="sx">%{drop table &#34;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="sx">&#34;}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="no">PgTools</span><span class="o">.</span><span class="n">restore_default_search_path</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Imagine an application that has a shared <code>users</code> table and private <code>posts</code> and <code>comments</code> tables. With this setup the table list will looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">public.posts
</span></span></span><span class="line"><span class="cl"><span class="go">public.comments
</span></span></span><span class="line"><span class="cl"><span class="go">public.users
</span></span></span><span class="line"><span class="cl"><span class="go">1.posts
</span></span></span><span class="line"><span class="cl"><span class="go">1.comments
</span></span></span></code></pre></div><p>When the search path is set to <code>1, public</code> the comments and posts will be fetched from the &ldquo;1&rdquo; schema and the users will be fetched from the public schema. That&rsquo;s pretty cool, if you ask me!</p>
<p>This does throw a wrench in one more area of development: migrating shared tables. The private schemas will not have the shared tables in them, so you will encounter errors when looping through them and running migrations. The fix to this is simple enough, but needs to be communicated to all developers on the project.</p>
<p>Any migration that operates on shared tables should be short-circuited if the current schema search path is private. Add this method to <code>PgTools</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">private_search_path?</span>
</span></span><span class="line"><span class="cl">  <span class="o">!</span><span class="n">search_path</span><span class="o">.</span><span class="n">match</span> <span class="sr">/public/</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Consider adding a boolean admin to the aforementioned shared <code>users</code> table. The migration should looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">AddAdminToUsers</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">up</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">if</span> <span class="no">PgTools</span><span class="o">.</span><span class="n">private_search_path?</span>
</span></span><span class="line"><span class="cl">    <span class="n">add_column</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:admin</span><span class="p">,</span> <span class="ss">:boolean</span><span class="p">,</span> <span class="ss">default</span><span class="p">:</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">down</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">if</span> <span class="no">PgTools</span><span class="o">.</span><span class="n">private_search_path?</span>
</span></span><span class="line"><span class="cl">    <span class="n">remove_column</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:admin</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>This migration will run as normal during <code>rake db:migrate</code> and get safely skipped during <code>rake tenants:db:migrate</code>.</p>
<h2 id="good-luck">Good luck!</h2>
<p>I hope this post serves as a guide for your own adventure into multi-tenant Rails apps on PostgreSQL. I&rsquo;ve been using it for some time now and while there is a lot to wrap your head around and set up at the outset, it pays off in spades when you can ignore the entire problem for much of your application logic and have the peace of mind that a coding mistake won&rsquo;t accidentally expose your customers&rsquo; sensitive data.</p>
<p>Let me know how you get on!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>cap db:pull</title>
      <link>https://jerodsanto.net/2011/06/cap-dbpull/</link>
      <pubDate>Wed, 29 Jun 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/06/cap-dbpull/</guid>
      
      
      <description><![CDATA[<p>If there&rsquo;s one thing I&rsquo;ve learned from <a href="http://heroku.com">Heroku</a>, it&rsquo;s that grabbing a snapshot of your production database is incredibly handy for troubleshooting.</p>
<p><code>heroku db:pull</code> is so rad that I wanted to have it on other projects not hosted on their platform, so I wrote a <a href="https://github.com/capistrano/capistrano/wiki">Capistrano</a> task which accomplishes the same goal.</p>
<p>This task is <a href="http://www.postgresql.org">PostgreSQL</a> specific, but can be easily adapted to work with other datastores. Just replace the <code>pg_dump</code> and <code>pg_restore</code> related commands with ones that your datastore provides. The process is still the same.</p>]]></description>
      
      <content:encoded><![CDATA[<p>If there&rsquo;s one thing I&rsquo;ve learned from <a href="http://heroku.com">Heroku</a>, it&rsquo;s that grabbing a snapshot of your production database is incredibly handy for troubleshooting.</p>
<p><code>heroku db:pull</code> is so rad that I wanted to have it on other projects not hosted on their platform, so I wrote a <a href="https://github.com/capistrano/capistrano/wiki">Capistrano</a> task which accomplishes the same goal.</p>
<p>This task is <a href="http://www.postgresql.org">PostgreSQL</a> specific, but can be easily adapted to work with other datastores. Just replace the <code>pg_dump</code> and <code>pg_restore</code> related commands with ones that your datastore provides. The process is still the same.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">namespace</span> <span class="ss">:db</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">desc</span> <span class="s2">&#34;Snapshots production db and dumps into local development db&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">task</span> <span class="ss">:pull</span><span class="p">,</span> <span class="ss">roles</span><span class="p">:</span> <span class="ss">:db</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="p">{</span> <span class="ss">primary</span><span class="p">:</span> <span class="kp">true</span> <span class="p">}</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># adjust prod_config to point to your database.yml</span>
</span></span><span class="line"><span class="cl">    <span class="n">prod_config</span> <span class="o">=</span> <span class="n">capture</span> <span class="s2">&#34;cat </span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">/config/database.yml&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">prod</span> <span class="o">=</span> <span class="no">YAML</span><span class="o">::</span><span class="nb">load</span><span class="p">(</span><span class="n">prod_config</span><span class="p">)</span><span class="o">[</span><span class="s2">&#34;production&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">dev</span>  <span class="o">=</span> <span class="no">YAML</span><span class="o">::</span><span class="n">load_file</span><span class="p">(</span><span class="s2">&#34;config/database.yml&#34;</span><span class="p">)</span><span class="o">[</span><span class="s2">&#34;development&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">dump</span> <span class="o">=</span> <span class="s2">&#34;/tmp/</span><span class="si">#{</span><span class="no">Time</span><span class="o">.</span><span class="n">now</span><span class="o">.</span><span class="n">to_i</span><span class="si">}</span><span class="s2">-</span><span class="si">#{</span><span class="n">application</span><span class="si">}</span><span class="s2">.psql&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">run</span> <span class="sx">%{pg_dump -x -Fc </span><span class="si">#{</span><span class="n">prod</span><span class="o">[</span><span class="s2">&#34;database&#34;</span><span class="o">]</span><span class="si">}</span><span class="sx"> -f </span><span class="si">#{</span><span class="n">dump</span><span class="si">}</span><span class="sx">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">dump</span><span class="p">,</span> <span class="n">dump</span>
</span></span><span class="line"><span class="cl">    <span class="n">run</span> <span class="s2">&#34;rm </span><span class="si">#{</span><span class="n">dump</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nb">system</span> <span class="sx">%{dropdb </span><span class="si">#{</span><span class="n">dev</span><span class="o">[</span><span class="s2">&#34;database&#34;</span><span class="o">]</span><span class="si">}</span><span class="sx">}</span>
</span></span><span class="line"><span class="cl">    <span class="nb">system</span> <span class="sx">%{createdb </span><span class="si">#{</span><span class="n">dev</span><span class="o">[</span><span class="s2">&#34;database&#34;</span><span class="o">]</span><span class="si">}</span><span class="sx"> -O </span><span class="si">#{</span><span class="n">dev</span><span class="o">[</span><span class="s2">&#34;username&#34;</span><span class="o">]</span><span class="si">}</span><span class="sx">}</span>
</span></span><span class="line"><span class="cl">    <span class="nb">system</span> <span class="sx">%{pg_restore -O -U </span><span class="si">#{</span><span class="n">dev</span><span class="o">[</span><span class="s2">&#34;username&#34;</span><span class="o">]</span><span class="si">}</span><span class="sx"> -d </span><span class="si">#{</span><span class="n">dev</span><span class="o">[</span><span class="s2">&#34;database&#34;</span><span class="o">]</span><span class="si">}</span><span class="sx"> </span><span class="si">#{</span><span class="n">dump</span><span class="si">}</span><span class="sx">}</span>
</span></span><span class="line"><span class="cl">    <span class="nb">system</span> <span class="s2">&#34;rm </span><span class="si">#{</span><span class="n">dump</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>The reason that I&rsquo;m dropping and recreating the development database before running the restore is that I could not find an easier way to restore a database that has a different user and name in production than it does in development. If you know of a better way to accomplish, please let me know.</p>
<p>Now just run <code>cap db:pull</code> and you&rsquo;re on your way!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Connecting Node.js to Redis To Go on Heroku</title>
      <link>https://jerodsanto.net/2011/06/connecting-node.js-to-redis-to-go-on-heroku/</link>
      <pubDate>Fri, 17 Jun 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/06/connecting-node.js-to-redis-to-go-on-heroku/</guid>
      
      
      <description><![CDATA[<p>Heroku&rsquo;s new <a href="http://devcenter.heroku.com/articles/cedar">Cedar stack</a> is awesome for many reasons, one of which is first-class <a href="http://nodejs.org">Node.js</a> hosting.</p>
<p>I&rsquo;m giving it a test run with an app that uses <a href="http://redis.io">Redis</a> for data caching and I couldn&rsquo;t find any documentation on how to connect a Node app to the <a href="http://redistogo.com">Redis To Go</a> add-on. Here&rsquo;s how I did it:</p>
<div class="notice">This assumes that you&rsquo;ve already created the Heroku app or don&rsquo;t need any help in doing so. Heroku has a <a href="http://devcenter.heroku.com/articles/node-js">great tutorial</a> on how to deploy a Node app on Cedar.</div>
<p>First, add the Redis To Go add-on via the <code>heroku</code> command:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Heroku&rsquo;s new <a href="http://devcenter.heroku.com/articles/cedar">Cedar stack</a> is awesome for many reasons, one of which is first-class <a href="http://nodejs.org">Node.js</a> hosting.</p>
<p>I&rsquo;m giving it a test run with an app that uses <a href="http://redis.io">Redis</a> for data caching and I couldn&rsquo;t find any documentation on how to connect a Node app to the <a href="http://redistogo.com">Redis To Go</a> add-on. Here&rsquo;s how I did it:</p>
<div class="notice">This assumes that you&rsquo;ve already created the Heroku app or don&rsquo;t need any help in doing so. Heroku has a <a href="http://devcenter.heroku.com/articles/node-js">great tutorial</a> on how to deploy a Node app on Cedar.</div>
<p>First, add the Redis To Go add-on via the <code>heroku</code> command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">heroku addons:add redistogo
</span></span></span></code></pre></div><p>Installing the add-on will automatically add a <code>REDISTOGO_URL</code> environment variable to your app. You can see its value with the <code>heroku config</code> command.</p>
<p>The Redis client for Node that I use is <a href="https://github.com/mranney/node_redis">node_redis</a>, so the following is specific it for it. If you use another client, adjust as necessary.</p>
<p>When node_redis connects to a Redis instance on your local machine it assumes the default port and host information, so instantiating the client is as simple as this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">redis</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;redis&#34;</span><span class="p">).</span><span class="nx">createClient</span><span class="p">();</span>
</span></span></code></pre></div><p>That works just fine in development, but we need it to authenticate to Redis To Go in production. To handle both cases I just check for the existence of the <code>REDISTOGO_URL</code>, like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">REDISTOGO_URL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// TODO: redistogo connection
</span></span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">redis</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;redis&#34;</span><span class="p">).</span><span class="nx">createClient</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Everything should still work fine in development, but we still need to implement the Redis To Go connection. To do this we need to extract the port, hostname, and authentication string from <code>REDISTOGO_URL</code> using Node&rsquo;s built-in <code>url</code> lib:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// inside if statement
</span></span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">rtg</span>   <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;url&#34;</span><span class="p">).</span><span class="nx">parse</span><span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">REDISTOGO_URL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">redis</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;redis&#34;</span><span class="p">).</span><span class="nx">createClient</span><span class="p">(</span><span class="nx">rtg</span><span class="p">.</span><span class="nx">port</span><span class="p">,</span> <span class="nx">rtg</span><span class="p">.</span><span class="nx">hostname</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">redis</span><span class="p">.</span><span class="nx">auth</span><span class="p">(</span><span class="nx">rtg</span><span class="p">.</span><span class="nx">auth</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s2">&#34;:&#34;</span><span class="p">)[</span><span class="mi">1</span><span class="p">]);</span>
</span></span></code></pre></div><p>For some reason Node&rsquo;s <code>url</code> lib won&rsquo;t split up the auth section&rsquo;s username and password, so that&rsquo;s what is going on in the <code>rtg.auth.split(&quot;:&quot;)[1]</code> that is passed to the auth command.</p>
<p>That&rsquo;s all it took to get my app connected and running smoothly. I hope this helps you do the same!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Reasons Your Dev Team Might Choose Talker Over Campfire for Group Chat</title>
      <link>https://jerodsanto.net/2011/06/reasons-your-dev-team-might-choose-talker-over-campfire-for-group-chat/</link>
      <pubDate>Tue, 14 Jun 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/06/reasons-your-dev-team-might-choose-talker-over-campfire-for-group-chat/</guid>
      
      
      <description><![CDATA[<p>Many development teams use 37Signals&rsquo; <a href="http://campfirenow.com">Campfire</a> for group chat. I&rsquo;ve used it just enough to complain about it.</p>
<p>Our team has been trying <a href="http://talkerapp.com">Talker</a> instead. The two tools are similar in many ways, but there are a few things about Talker that stand out to make it, in my opinion, better than Campfire for software development teams. I&rsquo;ll enumerate three of them for you.</p>
<h2 id="1-code-snippets-are-etherpad-sessions">1) Code snippets are EtherPad sessions</h2>
<p>Yes, you read that correctly, and it is awesome. When Talker detects a paste it will format it so all can see like so:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Many development teams use 37Signals&rsquo; <a href="http://campfirenow.com">Campfire</a> for group chat. I&rsquo;ve used it just enough to complain about it.</p>
<p>Our team has been trying <a href="http://talkerapp.com">Talker</a> instead. The two tools are similar in many ways, but there are a few things about Talker that stand out to make it, in my opinion, better than Campfire for software development teams. I&rsquo;ll enumerate three of them for you.</p>
<h2 id="1-code-snippets-are-etherpad-sessions">1) Code snippets are EtherPad sessions</h2>
<p>Yes, you read that correctly, and it is awesome. When Talker detects a paste it will format it so all can see like so:</p>
<p><img src="http://jerodsanto.net/drop/talker-paste-20110614-084721.png" alt="Talker code paste"></p>
<p>Notice the link in the upper left which says <strong>&ldquo;View / Edit paste&rdquo;</strong>? Clicking it will open a new tab/window and join you into an EtherPad session with the snippet&rsquo;s contents pre-populated. Anybody in the chat room can collaboratively live edit the snippet. Brilliant!</p>
<h2 id="2-javascript-plugin-api">2) JavaScript Plugin API</h2>
<p>As devs we often have thoughts like:</p>
<blockquote>
<p>&ldquo;This is pretty cool, but it&rsquo;d be even cooler if _______.&rdquo;</p>
</blockquote>
<p>With Talker you can easily fill in that blank.</p>
<p>Most rich web applications&rsquo; interactive features are implemented in JavaScript. Talker is not unique in this sense. What is unique, however, is that Talker&rsquo;s interactive features are built on top of a <em>public</em> plugin API that you can also build upon.</p>
<p><img src="http://jerodsanto.net/drop/talker-plugins-20110614-091155.png" alt="Talker plugins"></p>
<p>In fact, if you <a href="https://github.com/search?type=Repositories&amp;language=&amp;q=talker+plugins&amp;repo=&amp;langOverride=&amp;x=0&amp;y=0&amp;start_value=1">search GitHub</a> for &ldquo;talker plugins&rdquo; you&rsquo;ll find many plugins have already been written!</p>
<p><a href="https://github.com/cloudhead/talker-plugins/blob/master/talker-substitute.js">Here&rsquo;s</a> a handy plugin that provides live substitutions of the previous message.</p>
<h2 id="3-bring-your-own-server">3) Bring Your Own Server</h2>
<p>Talker has a REST API, but that&rsquo;s pretty much required these days. What makes Talker even better is that the <em>entire</em> codebase is open sourced (GPL). <a href="https://github.com/teambox/teambox-talker">Here</a>.</p>
<p>Detailed installation instructions are included. The paranoid or cheap — Talker is free, but private rooms &amp; SSL come at a cost of $12 a month — among us can run their own instance on their own hardware. How is that for sustainability?</p>
<p>This also allows for deeper integration into your company&rsquo;s custom workflows if the flexibility provided by the JavaScript and REST APIs isn&rsquo;t sufficient.</p>
<hr>
<p>I could go on, but these are the major points that make me want to use Talker and tell others of this great alternative to <a href="http://campfirenow.com">The Incumbent</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Benchmarketing</title>
      <link>https://jerodsanto.net/2011/04/benchmarketing/</link>
      <pubDate>Sat, 16 Apr 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/04/benchmarketing/</guid>
      
      
      <description><![CDATA[<hr>
<p><strong>bench•mar•ket•ing  |&lsquo;bench,märkiti NG|</strong></p>
<p><strong>noun</strong></p>
<ol>
<li>(<em>pejorative</em>) The misleading use of unrepresentative benchmark software results in marketing a computer system.</li>
</ol>
<hr>
<p>I fear we are quite often subject to such misleading tactics. It is difficult to discern between a benchmark and a benchmarket. What a great term.</p>
<p>(via <a href="http://en.wiktionary.org/wiki/benchmarketing">Wiktionary</a> and a <a href="http://news.ycombinator.com/item?id=2449125">great comment</a> by <em>jread</em> on HN)</p>]]></description>
      
      <content:encoded><![CDATA[<hr>
<p><strong>bench•mar•ket•ing  |&lsquo;bench,märkiti NG|</strong></p>
<p><strong>noun</strong></p>
<ol>
<li>(<em>pejorative</em>) The misleading use of unrepresentative benchmark software results in marketing a computer system.</li>
</ol>
<hr>
<p>I fear we are quite often subject to such misleading tactics. It is difficult to discern between a benchmark and a benchmarket. What a great term.</p>
<p>(via <a href="http://en.wiktionary.org/wiki/benchmarketing">Wiktionary</a> and a <a href="http://news.ycombinator.com/item?id=2449125">great comment</a> by <em>jread</em> on HN)</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Pow and Apache Side-by-Side</title>
      <link>https://jerodsanto.net/2011/04/pow-and-apache-side-by-side/</link>
      <pubDate>Tue, 12 Apr 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/04/pow-and-apache-side-by-side/</guid>
      
      
      <description><![CDATA[<p>You&rsquo;ve probably heard of <a href="http://pow.cx/">Pow</a>. It is awesome. However, it <a href="https://github.com/37signals/pow/issues/48#issuecomment-973701">doesn&rsquo;t play nice</a> with Apache running on the same box. Pow redirects all localhost traffic on port 80 to its own application server listening on a different port. This sucks for anybody — myself included — who also develops non-Ruby web apps using Apache virtual hosts.</p>
<p>Thankfully there&rsquo;s an elegant workaround to this problem and it comes from the future: <a href="http://en.wikipedia.org/wiki/IPv6">IPv6</a>.</p>
<div class="notice">I&rsquo;m sure you know that IPv6 is not actually from the future, but its wide-spread adoption certainly is!</div>
<p>By configuring Apache to use IPv6 and Pow to use IPv4 they can both live in harmony and bind to port 80 to their heart&rsquo;s content. Pow is on IPv4 already, so we just have to get Apache on IPv6. Here&rsquo;s how:</p>]]></description>
      
      <content:encoded><![CDATA[<p>You&rsquo;ve probably heard of <a href="http://pow.cx/">Pow</a>. It is awesome. However, it <a href="https://github.com/37signals/pow/issues/48#issuecomment-973701">doesn&rsquo;t play nice</a> with Apache running on the same box. Pow redirects all localhost traffic on port 80 to its own application server listening on a different port. This sucks for anybody — myself included — who also develops non-Ruby web apps using Apache virtual hosts.</p>
<p>Thankfully there&rsquo;s an elegant workaround to this problem and it comes from the future: <a href="http://en.wikipedia.org/wiki/IPv6">IPv6</a>.</p>
<div class="notice">I&rsquo;m sure you know that IPv6 is not actually from the future, but its wide-spread adoption certainly is!</div>
<p>By configuring Apache to use IPv6 and Pow to use IPv4 they can both live in harmony and bind to port 80 to their heart&rsquo;s content. Pow is on IPv4 already, so we just have to get Apache on IPv6. Here&rsquo;s how:</p>
<h2 id="1-make-it-listen">1) Make it Listen</h2>
<p>Set (or change) the <code>Listen</code> directive in your Apache configuration (probably <code>/etc/apache2/httpd.conf</code>) to use IPv6 address syntax:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-apache" data-lang="apache"><span class="line"><span class="cl"><span class="nb">Listen</span> [::]:80
</span></span></code></pre></div><p>The <code>::</code> means to bind on all available IPv6 addresses (akin to IPv4&rsquo;s 0.0.0.0). We have to wrap it in square brackets to distinguish the address portion from the colon preceding the port assignment. If you have your <code>NameVirtualHost</code> and <code>VirtualHost</code> directives set to use <code>*:80</code> then you shouldn&rsquo;t need any other changes. Restart Apache and it should be all good.</p>
<h2 id="2-add-hosts">2) Add Hosts</h2>
<p>You have to add the hosts you want Apache to serve to <code>/etc/hosts</code> with IPv6 addresses so the browser gets routed properly. <code>::1</code> is the default loopback address, so add them like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">::1 localhost
</span></span></span><span class="line"><span class="cl"><span class="go">::1 wp30.local
</span></span></span><span class="line"><span class="cl"><span class="go">::1 wp31.local
</span></span></span></code></pre></div><p>One thing to note is that it is required to put them on separate lines like that. With IPv4 addresses you could stack them on a single line, but for some reason that doesn&rsquo;t work with IPv6.</p>
<p>To make sure the routing is working properly, ping the hosts using the IPv6 version of ping:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ ping6 wp30.local
</span></span></span><span class="line"><span class="cl"><span class="go">PING6(56=40+8+8 bytes) ::1 --&gt; ::1
</span></span></span><span class="line"><span class="cl"><span class="go">16 bytes from ::1, icmp_seq=0 hlim=64 time=0.076 ms
</span></span></span></code></pre></div><h2 id="3-browse-it-up">3) Browse It Up</h2>
<p>At this point you <strong>should</strong> be able to access both Apache virtual hosts and Pow virtual hosts in your browser. Boom shakalaka</p>
<h2 id="notes-and-drawbacks">Notes and Drawbacks</h2>
<p>A few things to note about this solution:</p>
<p>First, Chrome doesn&rsquo;t seem to resolve the IPv6 hosts properly, which is strange because both Safari 5 and Firefox 4 do. I&rsquo;m on the dev channel, so maybe that has something to do with it.</p>
<p>Next, you must add the hosts to <code>/etc/hosts</code>. Tools like my <a href="http://detoursapp.com">Detours</a> application do not properly register the IPv6 addresses for lookup (I&rsquo;m going to see if I can fix that, more to come).</p>
<p>Finally, if IPv6 ever does get deployed into the mainstream, Pow may adopt it and we&rsquo;ll be back where we started. I don&rsquo;t think we have to worry about that for awhile.</p>
<p>As always, YMMV. Enjoy.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>ActiveRecord Association Caching Gotcha</title>
      <link>https://jerodsanto.net/2011/04/activerecord-association-caching-gotcha/</link>
      <pubDate>Thu, 07 Apr 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/04/activerecord-association-caching-gotcha/</guid>
      
      
      <description><![CDATA[<p>Here&rsquo;s a quick bit of info that will hopefully save somebody some time. ActiveRecord&rsquo;s association methods are built around caching. This caching is disabled by default in <strong>development</strong> mode, but enabled in <strong>test</strong> and <strong>production</strong> modes. If you&rsquo;re doing any kind of association updating in an ActiveRecord callback you may run into this caching and slam your head against your desk a few times.</p>
<p>Say we have a <code>Pledge</code> class that belongs to a <code>Person</code> class. People also have <code>Job</code>s and we want to (re)set their <code>job_id</code> when a <code>Pledge</code> is saved. Enter a virtual attribute and a callback:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Here&rsquo;s a quick bit of info that will hopefully save somebody some time. ActiveRecord&rsquo;s association methods are built around caching. This caching is disabled by default in <strong>development</strong> mode, but enabled in <strong>test</strong> and <strong>production</strong> modes. If you&rsquo;re doing any kind of association updating in an ActiveRecord callback you may run into this caching and slam your head against your desk a few times.</p>
<p>Say we have a <code>Pledge</code> class that belongs to a <code>Person</code> class. People also have <code>Job</code>s and we want to (re)set their <code>job_id</code> when a <code>Pledge</code> is saved. Enter a virtual attribute and a callback:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Person</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_many</span> <span class="ss">:pledges</span>
</span></span><span class="line"><span class="cl">  <span class="n">belongs_to</span> <span class="ss">:job</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Pledge</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">belongs_to</span> <span class="ss">:person</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_writer</span> <span class="ss">:job_id</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">after_save</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">person</span><span class="o">.</span><span class="n">update_attribute</span><span class="p">(</span><span class="ss">:job_id</span><span class="p">,</span> <span class="vi">@job_id</span><span class="p">)</span> <span class="k">if</span> <span class="vi">@job_id</span><span class="o">.</span><span class="n">present?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>This looks pretty straight forward. However, this simple test case will fail:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">should</span> <span class="s2">&#34;assign virtual job_id attribute to its person after save&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@pledge</span><span class="o">.</span><span class="n">job_id</span> <span class="o">=</span> <span class="mi">4</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@pledge</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">  <span class="n">assert_equal</span> <span class="mi">4</span><span class="p">,</span> <span class="vi">@pledge</span><span class="o">.</span><span class="n">person</span><span class="o">.</span><span class="n">job_id</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>The associated person&rsquo;s <code>job_id</code> attribute never gets updated. After some experimentation, I realized that this code runs as expected in development mode. So, what gives? Caching gives, as I explained above.</p>
<p>Let&rsquo;s fix this up to work regardless of ActiveRecord caching by forcing a trip to the database:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">after_save</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">person</span><span class="p">(</span><span class="kp">true</span><span class="p">)</span><span class="o">.</span><span class="n">update_attribute</span><span class="p">(</span><span class="ss">:job_id</span><span class="p">,</span> <span class="vi">@job_id</span><span class="p">)</span> <span class="k">if</span> <span class="vi">@job_id</span><span class="o">.</span><span class="n">present?</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Ahh, that&rsquo;s better. Read more on controlling ActiveRecord caching <a href="http://guides.rubyonrails.org/association_basics.html#controlling-caching">here</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Install Firefox 4 and Firefox 3 Side by Side on Mac OS X</title>
      <link>https://jerodsanto.net/2011/03/install-firefox-4-and-firefox-3-side-by-side-on-mac-os-x/</link>
      <pubDate>Wed, 23 Mar 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/03/install-firefox-4-and-firefox-3-side-by-side-on-mac-os-x/</guid>
      
      
      <description><![CDATA[<p>Want to upgrade to the newly released Firefox 4 while maintaining your Firefox 3 install for browser testing purposes? So did I. Here&rsquo;s how to get &rsquo;er done:</p>
<h2 id="1-rename-firefox-3s-app-bundle">1) Rename Firefox 3&rsquo;s app bundle</h2>
<p>You don&rsquo;t want the Firefox 4 install to clobber the Firefox 3 install which it will if you don&rsquo;t rename it first. Use Finder to rename it or execute this from the terminal:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">mv /Applications/Firefox.app /Applications/Firefox3.app
</span></span></span></code></pre></div><h2 id="2-create-version-specific-profiles">2) Create version-specific profiles</h2>
<p>Skip this step if you don&rsquo;t care about your current Firefox 3 profile.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Want to upgrade to the newly released Firefox 4 while maintaining your Firefox 3 install for browser testing purposes? So did I. Here&rsquo;s how to get &rsquo;er done:</p>
<h2 id="1-rename-firefox-3s-app-bundle">1) Rename Firefox 3&rsquo;s app bundle</h2>
<p>You don&rsquo;t want the Firefox 4 install to clobber the Firefox 3 install which it will if you don&rsquo;t rename it first. Use Finder to rename it or execute this from the terminal:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">mv /Applications/Firefox.app /Applications/Firefox3.app
</span></span></span></code></pre></div><h2 id="2-create-version-specific-profiles">2) Create version-specific profiles</h2>
<p>Skip this step if you don&rsquo;t care about your current Firefox 3 profile.</p>
<p>You need to rename your current profile (probably <strong>default</strong>) and create a new profile for Firefox 4 to use. Use the <a href="http://support.mozilla.com/en-US/kb/Managing%20profiles">Profile Manager</a> to accomplish this. Quit Firefox if it&rsquo;s running and run this command from your terminal:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">/Applications/Firefox3.app/Contents/MacOS/firefox-bin -ProfileManager
</span></span></span></code></pre></div><p>Rename the the default profile to &ldquo;firefox3&rdquo; (or similar) and create a new profile called &ldquo;firefox4&rdquo; (or similar). If you&rsquo;re cool with selecting the profile to use every time you launch one of the browsers, just uncheck &ldquo;Don&rsquo;t ask at startup&rdquo; and jump down to step 5. If you&rsquo;re <strong>not</strong> cool with that, make sure it is checked and read on.</p>
<h2 id="3-force-firefox-3-to-use-its-own-profile-when-launched">3) Force Firefox 3 to use its own profile when launched</h2>
<p>This is more work than I anticipated, but <a href="http://nxsy.org/firefox-30b4-and-multiple-firefox-versions-on-os-x">this blog post</a> explains it pretty well. Create a script called &ldquo;firefox.sh&rdquo; (or similar) in <code>/Applications/Firefox3.app/Contents/MacOS</code> and paste the following into it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/sh
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">MYDIR</span><span class="o">=</span><span class="sb">`</span>dirname <span class="s2">&#34;</span><span class="nv">$0</span><span class="s2">&#34;</span><span class="sb">`</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">MYDIR</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">./firefox-bin -P <span class="s2">&#34;firefox3&#34;</span> <span class="s2">&#34;</span><span class="nv">$@</span><span class="s2">&#34;</span>
</span></span></code></pre></div><p>Then edit <code>/Applications/Firefox3.app/Contents/Info.plist</code> and change the <code>Root/CFBundleExecutable</code> string to &ldquo;firefox.sh&rdquo;.</p>
<p>Finally, rebuild your launch services database so it picks up the changes by executing this command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -kill -r -domain local -domain system -domain user
</span></span></span></code></pre></div><h2 id="4-install-firefox-4-like-normal">4) Install Firefox 4 like normal</h2>
<p>Yup, just <a href="http://getfirefox.com">Get Firefox 4</a> and install it. You should now be able to run them side by side with their unique profiles!</p>
<p><img src="http://jerodsanto.net/drop/firefox-3-and-4-20110323-093818.png" alt="side by side" title="Firefox 3 and Firefox 4 side by side"></p>
<p>Bingo. Bango. Bongo.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>The Two Character Config Hack That Changed my Bashing Life</title>
      <link>https://jerodsanto.net/2011/03/the-two-character-config-hack-that-changed-my-bashing-life/</link>
      <pubDate>Sun, 20 Mar 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/03/the-two-character-config-hack-that-changed-my-bashing-life/</guid>
      
      
      <description><![CDATA[<p>Code isn&rsquo;t the only thing we should endeavor to keep <a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">DRY</a>. Let&rsquo;s see if you can detect a pattern in this very common — albeit contrived for sake of example — Bash session:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ cd src/erlang
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src/erlang$ ls
</span></span></span><span class="line"><span class="cl"><span class="go">[snip]
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src/erlang$ cd ..
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src$ ls
</span></span></span><span class="line"><span class="cl"><span class="go">[snip]
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src$ cd
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ ls
</span></span></span><span class="line"><span class="cl"><span class="go">[snip]
</span></span></span></code></pre></div><p>In my experience, the <code>cd</code> command is <em>almost always</em> followed by the <code>ls</code> command. Why have I been typing it in all these years? Not anymore, baby!</p>]]></description>
      
      <content:encoded><![CDATA[<p>Code isn&rsquo;t the only thing we should endeavor to keep <a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">DRY</a>. Let&rsquo;s see if you can detect a pattern in this very common — albeit contrived for sake of example — Bash session:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ cd src/erlang
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src/erlang$ ls
</span></span></span><span class="line"><span class="cl"><span class="go">[snip]
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src/erlang$ cd ..
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src$ ls
</span></span></span><span class="line"><span class="cl"><span class="go">[snip]
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src$ cd
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ ls
</span></span></span><span class="line"><span class="cl"><span class="go">[snip]
</span></span></span></code></pre></div><p>In my experience, the <code>cd</code> command is <em>almost always</em> followed by the <code>ls</code> command. Why have I been typing it in all these years? Not anymore, baby!</p>
<p>I already have a custom <code>cd</code> function which provides <code>cd ...</code> type directory traversals (<a href="https://jerodsanto.net/2009/09/cd-up-up-up/">more on that here</a>), so I recently added two characters to it.</p>
<h2 id="before">Before</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">function</span> <span class="nb">cd</span> <span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="o">[[</span> <span class="nv">$#</span> &gt; <span class="m">0</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> <span class="si">${</span><span class="nv">1</span><span class="p">:</span><span class="nv">0</span><span class="p">:</span><span class="nv">2</span><span class="si">}</span> <span class="o">==</span> <span class="s2">&#34;..&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">      <span class="nv">rest</span><span class="o">=</span><span class="si">${</span><span class="nv">1</span><span class="p">:</span><span class="nv">2</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">      <span class="nv">rest</span><span class="o">=</span><span class="si">${</span><span class="nv">rest</span><span class="p">//./../</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">      <span class="nb">builtin</span> <span class="nb">cd</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">1</span><span class="p">:</span><span class="nv">0</span><span class="p">:</span><span class="nv">2</span><span class="si">}</span><span class="s2">/</span><span class="si">${</span><span class="nv">rest</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="nb">builtin</span> <span class="nb">cd</span> <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="nb">builtin</span> <span class="nb">cd</span>
</span></span><span class="line"><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><h2 id="after">After</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">function</span> <span class="nb">cd</span> <span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="o">[[</span> <span class="nv">$#</span> &gt; <span class="m">0</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> <span class="si">${</span><span class="nv">1</span><span class="p">:</span><span class="nv">0</span><span class="p">:</span><span class="nv">2</span><span class="si">}</span> <span class="o">==</span> <span class="s2">&#34;..&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">      <span class="nv">rest</span><span class="o">=</span><span class="si">${</span><span class="nv">1</span><span class="p">:</span><span class="nv">2</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">      <span class="nv">rest</span><span class="o">=</span><span class="si">${</span><span class="nv">rest</span><span class="p">//./../</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">      <span class="nb">builtin</span> <span class="nb">cd</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">1</span><span class="p">:</span><span class="nv">0</span><span class="p">:</span><span class="nv">2</span><span class="si">}</span><span class="s2">/</span><span class="si">${</span><span class="nv">rest</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="nb">builtin</span> <span class="nb">cd</span> <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="nb">builtin</span> <span class="nb">cd</span>
</span></span><span class="line"><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="cl">  ls
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>Did you spot the change? Yup, I just added the <code>ls</code> at the end of every <code>cd</code>. Little change, huge payoff. Give it a try and after a few days you&rsquo;ll be wondering why you didn&rsquo;t do this years ago (I know I am).</p>
<p>Oh, and if you don&rsquo;t want the directory traversal bit, you can get away with a much more simple function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">function</span> cd<span class="o">()</span> <span class="o">{</span> <span class="nb">builtin</span> <span class="nb">cd</span> <span class="nv">$@</span> <span class="o">&amp;&amp;</span> ls<span class="p">;</span> <span class="o">}</span>
</span></span></code></pre></div><p>Enjoy!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>The Perfect Setup: Rails on Screen</title>
      <link>https://jerodsanto.net/2011/02/the-perfect-setup-rails-on-screen/</link>
      <pubDate>Fri, 25 Feb 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/02/the-perfect-setup-rails-on-screen/</guid>
      
      
      <description><![CDATA[<p>If, like me, you prefer staying light on your feet, you probably use a powerful text editor coupled with multiple shell sessions to drive your <a href="http://rubyonrails.org">Rails</a> development. Unfortunately, managing said shells <em>can</em> get a bit unwieldy, but thankfully UNIX-based systems have a tool just for the job: <a href="http://www.gnu.org/software/screen/">GNU Screen</a>.</p>
<p>This post isn&rsquo;t trying to sell you on <code>screen</code>. There are umpteen sites out there which do that. Instead, I&rsquo;d like to share with you how I&rsquo;ve engineered my setup such that it&rsquo;s a joy to use.</p>]]></description>
      
      <content:encoded><![CDATA[<p>If, like me, you prefer staying light on your feet, you probably use a powerful text editor coupled with multiple shell sessions to drive your <a href="http://rubyonrails.org">Rails</a> development. Unfortunately, managing said shells <em>can</em> get a bit unwieldy, but thankfully UNIX-based systems have a tool just for the job: <a href="http://www.gnu.org/software/screen/">GNU Screen</a>.</p>
<p>This post isn&rsquo;t trying to sell you on <code>screen</code>. There are umpteen sites out there which do that. Instead, I&rsquo;d like to share with you how I&rsquo;ve engineered my setup such that it&rsquo;s a joy to use.</p>
<p><img src="http://jerodsanto.net/drop/rails-screen-20110225-204305.png" alt="rails screen" title="Rails on Screen"></p>
<h2 id="the-premise">The Premise</h2>
<p>Each Rails project I work on has the same basic needs. I want each of those needs facilitated by a <code>screen</code> window, and I don&rsquo;t want to manually bootstrap the environment each time I return to a project. The things I want in each Rails app (currently) are:</p>
<ol>
<li>TextMate</li>
<li>Autotest</li>
<li>Development server</li>
<li>Development Console</li>
<li>GitX</li>
<li>A spare Bash session</li>
</ol>
<p>Ideally, I would be able to <code>cd</code> to my application&rsquo;s root directory, execute a single command, and be up and running. But&hellip;</p>
<h2 id="the-problem">The Problem</h2>
<p>There is no straight forward way (that I know of) to configure screen for multiple unrelated sessions. In other words, you can configure it to <em>always</em> do certain things when it is invoked, but you can&rsquo;t configure it to <em>sometimes</em> do certain things when it is invoked. WANT.</p>
<h2 id="my-solution">My Solution</h2>
<p>Good news! Passing the <code>-c</code> flag to <code>screen</code> specifies a configuration file to use. I leverage this feature to (sometimes) load in a custom, Rails-specific configuration. That plus a wrapper function does the job perfectly. Here&rsquo;s how:</p>
<h2 id="1-create-a-custom-screenrc-just-for-rails">1) Create a custom screenrc just for rails</h2>
<p>In it I put everything required to get the session all set up. Mine looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">source $HOME/.screenrc
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="go">screen 0 bash
</span></span></span><span class="line"><span class="cl"><span class="go">title &#34;tests&#34;
</span></span></span><span class="line"><span class="cl"><span class="go">stuff &#34;autotest\015&#34;
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="go">screen 1 bash
</span></span></span><span class="line"><span class="cl"><span class="go">title &#34;server&#34;
</span></span></span><span class="line"><span class="cl"><span class="go">stuff &#34;rails server\015&#34;
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="go">screen 2 bash
</span></span></span><span class="line"><span class="cl"><span class="go">title &#34;console&#34;
</span></span></span><span class="line"><span class="cl"><span class="go">stuff &#34;rails console\015&#34;
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="go">screen 3 bash
</span></span></span><span class="line"><span class="cl"><span class="go">stuff &#34;gitx &amp;&amp; mate .\015&#34;
</span></span></span></code></pre></div><p>The first thing to note here is that this file will source my main <code>.screenrc</code> file. That means I don&rsquo;t have to repeat any of those settings. Gotta love that!</p>
<p>Next, notice that for each separate window I have 3 lines. The first line indicates which <code>screen</code> window it will be in and starts a bash shell, the second line is obviously the title of the window, and the third line is the actual command to &ldquo;stuff&rdquo; into the window.</p>
<div class="notice">I <em>could</em> bypass the whole &ldquo;stuff&rdquo; thing by passing the command directly on the &ldquo;screen&rdquo; line instead of passing in &ldquo;bash&rdquo;, but that will exit the window when the process terminates, which is annoying.</div>
<p>Finally, I should explain the <code>\015</code> at the end of each &ldquo;stuff&rdquo; line. That is telling <code>screen</code> to enter a line feed (LF) character which will actually execute the command.</p>
<h2 id="2-a-screen-wrapper-function">2) A screen wrapper function</h2>
<p>Now that I have a custom config file, I make it super-simple to invoke <code>screen</code> with it by adding this function to my <code>.bashrc</code> file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">function</span> screen<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="nv">super</span><span class="o">=</span><span class="sb">`</span>which screen<span class="sb">`</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;rails&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$super</span> -S <span class="sb">`</span>basename <span class="nv">$PWD</span><span class="sb">`</span> -c <span class="nv">$HOME</span>/.screenrailsrc
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$super</span> <span class="nv">$@</span>
</span></span><span class="line"><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>This should be pretty self-explanatory. It calls <code>screen</code> with the custom config if the first argument is &ldquo;rails&rdquo;, otherwise it punts.</p>
<p>One noteworthy tidbit is that it also sets the <code>-S</code> flag to the name of the directory holding the Rails application. That way if the session ever gets detached I can reattach it with the project name instead of some obscure session id.</p>
<h2 id="one-stone-many-birds">One Stone, Many Birds</h2>
<p>I like this system a lot. What is really nice about it is that I can easily extend it to many different scenarios by creating new custom config files and adding matchers to the wrapper function.</p>
<p>Is that perfect or what?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>jQuery: isInArray Boolean Sugar</title>
      <link>https://jerodsanto.net/2011/01/jquery-isinarray-boolean-sugar/</link>
      <pubDate>Mon, 31 Jan 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/01/jquery-isinarray-boolean-sugar/</guid>
      
      
      <description><![CDATA[<p>jQuery has a utility function called <code>jQuery.inArray</code> which confounds <a href="http://forum.jquery.com/topic/inarray">many people</a> when they first stumble across it in the <a href="http://api.jquery.com/jQuery.inArray/">API docs</a>. The name implies a boolean return, but the function is really just a proxy in front of <code>Array.indexOf</code> to allow for cross-browser use. It returns the index of the value found in the array or -1 if the value is not found.</p>
<p>The bad news is the function name won&rsquo;t be changing anytime soon due to backwards-compatibility. The good news is it&rsquo;s a one-liner to add some syntactic sugar around it for your own use. I call it <code>isInArray</code>. <code>arrayInclude</code> or <code>arrayHas</code> would work too, but they imply an array-first argument order which just adds to the confusion.</p>]]></description>
      
      <content:encoded><![CDATA[<p>jQuery has a utility function called <code>jQuery.inArray</code> which confounds <a href="http://forum.jquery.com/topic/inarray">many people</a> when they first stumble across it in the <a href="http://api.jquery.com/jQuery.inArray/">API docs</a>. The name implies a boolean return, but the function is really just a proxy in front of <code>Array.indexOf</code> to allow for cross-browser use. It returns the index of the value found in the array or -1 if the value is not found.</p>
<p>The bad news is the function name won&rsquo;t be changing anytime soon due to backwards-compatibility. The good news is it&rsquo;s a one-liner to add some syntactic sugar around it for your own use. I call it <code>isInArray</code>. <code>arrayInclude</code> or <code>arrayHas</code> would work too, but they imply an array-first argument order which just adds to the confusion.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">jQuery</span><span class="p">.</span><span class="nx">isInArray</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">array</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="o">-</span><span class="mi">1</span> <span class="o">!=</span> <span class="nx">jQuery</span><span class="p">.</span><span class="nx">inArray</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">array</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Why waste precious time and bandwidth adding such functions? Because conditionals that look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span> <span class="o">!=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">inArray</span><span class="p">(</span><span class="nx">someValue</span><span class="p">,</span> <span class="nx">someArray</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;there you are!&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span> <span class="o">==</span> <span class="nx">$</span><span class="p">.</span><span class="nx">inArray</span><span class="p">(</span><span class="nx">someValue</span><span class="p">,</span> <span class="nx">someArray</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;there you are not!&#39;</span><span class="p">);</span>
</span></span></code></pre></div><p>carry a lot more mental overhead than conditionals that look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nx">$</span><span class="p">.</span><span class="nx">isInArray</span><span class="p">(</span><span class="nx">someValue</span><span class="p">,</span> <span class="nx">someArray</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;there you are!&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">$</span><span class="p">.</span><span class="nx">isInArray</span><span class="p">(</span><span class="nx">someValue</span><span class="p">,</span> <span class="nx">someArray</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;there you are not!&#39;</span><span class="p">);</span>
</span></span></code></pre></div><p>The value is nominal when used minimally, but these kinds of niceties help keep your application logic semantic when used throughout larger code bases.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Tech Podcasts You Should Know About</title>
      <link>https://jerodsanto.net/2011/01/tech-podcasts-you-should-know-about/</link>
      <pubDate>Sun, 30 Jan 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/01/tech-podcasts-you-should-know-about/</guid>
      
      
      <description><![CDATA[<p>I&rsquo;ve been listening to tech-related podcasts since the birth of the medium. I recently realized that I&rsquo;ve never shared the shows that I think are great. So, here they are (in no particular order):</p>
<p><img src="http://jerodsanto.net/drop/podcasts-20110130-150220.png" alt="podcasts"></p>
<h2 id="the-changelog"><a href="http://thechangelog.com/">The Changelog</a></h2>
<p>The Changelog tracks what&rsquo;s new in the world of open source. I love seeing open source projects and their authors getting exposure and I&rsquo;ve learned a ton from tuning into the conversations around them.</p>]]></description>
      
      <content:encoded><![CDATA[<p>I&rsquo;ve been listening to tech-related podcasts since the birth of the medium. I recently realized that I&rsquo;ve never shared the shows that I think are great. So, here they are (in no particular order):</p>
<p><img src="http://jerodsanto.net/drop/podcasts-20110130-150220.png" alt="podcasts"></p>
<h2 id="the-changelog"><a href="http://thechangelog.com/">The Changelog</a></h2>
<p>The Changelog tracks what&rsquo;s new in the world of open source. I love seeing open source projects and their authors getting exposure and I&rsquo;ve learned a ton from tuning into the conversations around them.</p>
<h2 id="build-and-analyze"><a href="http://5by5.tv/buildanalyze">Build and Analyze</a></h2>
<p>B&amp;A is the new kid on my block. This podcast features Marco Arment and Dan Benjamin discussing iOS, Mac and web development. Marco is full of insight and never afraid of diving into the esoteric details of Cocoa development.</p>
<h2 id="this-week-in-tech"><a href="http://twit.tv/twit">This Week in Tech</a></h2>
<p><a href="http://twit.tv/twit">This Week in Tech</a> (along with Diggnation) was a show that introduced me to podcasting. Leo Laporte is an excellent host and the panel changes from week to week so you&rsquo;re always in for a fresh take. The topics are your average tech news (hardware/software releases, Facebook&rsquo;s latest move, Google vs Apple, etc.), but what makes TWiT interesting after all these years are the in-between moments.</p>
<h2 id="37signals-podcast"><a href="http://37signals.com/podcast">37Signals Podcast</a></h2>
<p>This one is hit-or-miss depending on the topic. Sometimes they&rsquo;re just pimping their book or software product (can you blame them?), but other times the discussion is lively and insightful. I especially enjoyed the programming roundtable discussion (<a href="http://37signals.com/podcast#episode20">here</a>, <a href="http://37signals.com/podcast#episode21">here</a>, and <a href="http://37signals.com/podcast#episode22">here</a>) and hope to hear more from the development team in the future.</p>
<h2 id="the-pipeline"><a href="http://5by5.tv/pipeline/">The Pipeline</a></h2>
<p><a href="http://5by5.tv/pipeline/">The Pipeline</a> is a talk show wherein Dan Benjamin interviews interesting people around the web. I really dig Dan&rsquo;s interviewing style and he <em>really does</em> land some interesting guests. A few shows that I really enjoyed include interviews with <a href="http://5by5.tv/pipeline/30">Notch</a>, <a href="http://5by5.tv/pipeline/22">Adam Lisagor</a> and <a href="http://5by5.tv/pipeline/24">Tina Roth Eisenberg</a>.</p>
<h2 id="pivotal-labs-tech-talks"><a href="http://pivotallabs.com/talks">Pivotal Labs Tech Talks</a></h2>
<p>Pivotal Labs routinely hosts Tech Talks in their office. These talks cover a broad range of technology and development topics. The talks are recorded and released in podcast format in both <a href="http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=292868490">video</a> and <a href="http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=292868492">audio</a>. I usually spring for the video and watch in the iPad or laptop when I&rsquo;m bored.</p>
<h2 id="macbreak-weekly"><a href="http://twit.tv/mbw">Macbreak Weekly</a></h2>
<p>Perhaps my &ldquo;guilty pleasure&rdquo; of the lot. Macbreak Weekly is great for one major reason: the hosts. Leo Laporte (again), Andy Inhatko, and Merlin Mann are a charismatic group and they feed off each other very well. I could take or leave Alex Lindsey, but the others are a riot to listen to. Another great thing about this podcast is that they conclude each show with a &ldquo;picks of the week&rdquo; segments wherein each host gets to promote a Mac-related product, whether that be hardware or software. I&rsquo;ve learned of a few gems from them, and I really appreciate how they give publicity to small software developers.</p>
<h2 id="ruby-on-rails-podcast"><a href="http://podcast.rubyonrails.org/">Ruby on Rails Podcast</a></h2>
<p>I wish this podcast was published more often. It&rsquo;s hosted by Geoffrey Grosenbach of <a href="http://peepcode.com">PeepCode</a> fame and features news and interviews around the Ruby and Rails communities. Geoffrey is a skilled interviewer and has one of those enigmatic voices that make for great audio.</p>
<hr>
<p>I&rsquo;m always looking for new high quality, tech-related media to consume. Please let me know if you know of any killer podcasts that I&rsquo;m missing out on!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Quickly Determine Your Current Heroku Database Size</title>
      <link>https://jerodsanto.net/2011/01/quickly-determine-your-current-heroku-database-size/</link>
      <pubDate>Wed, 26 Jan 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/01/quickly-determine-your-current-heroku-database-size/</guid>
      
      
      <description><![CDATA[<p>Heroku&rsquo;s free offering includes a 5MB shared database. After your database grows past that, you&rsquo;ll have to upgrade to their (as of now) $15 per month shared database which can grow up to 20GB.</p>
<p>5MB ain&rsquo;t much, but it will get you started. But how do you know where your app currently stands? Heroku does not expose the current database size of your application via their web interface or command-line tool, but it&rsquo;s pretty easy to get at.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Heroku&rsquo;s free offering includes a 5MB shared database. After your database grows past that, you&rsquo;ll have to upgrade to their (as of now) $15 per month shared database which can grow up to 20GB.</p>
<p>5MB ain&rsquo;t much, but it will get you started. But how do you know where your app currently stands? Heroku does not expose the current database size of your application via their web interface or command-line tool, but it&rsquo;s pretty easy to get at.</p>
<div class="alert">UPDATE: It turns out this post was 100% wrong. Heroku <em>does</em> expose the current database size of your application via the command-line tool. Simply run <code>heroku info</code> and you&rsquo;ll see it in there next to &ldquo;Data size&rdquo; in all its glory. I can&rsquo;t believe I glossed that when I was looking previously. Thanks to <a href="http://www.williamayd.com/">William Ayd</a> for sorting me out!</div>
<ol>
<li>Connect to your app&rsquo;s console:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">heroku console
</span></span></span></code></pre></div><ol start="2">
<li>Run this code:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">connection</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">&#34;SELECT pg_size_pretty(pg_database_size(&#39;postgres&#39;))&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span>
</span></span></code></pre></div><p>Alternatively, if you&rsquo;ve already upgraded to one of the paid database add-ons and you have Postgresql installed locally, you can connect directly to your app&rsquo;s database like so:</p>
<ol>
<li>Connect using the <code>heroku</code> command:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">heroku pg:psql
</span></span></span></code></pre></div><ol start="2">
<li>Run this query:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-mysql" data-lang="mysql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="nf">pg_size_pretty</span><span class="p">(</span><span class="nf">pg_database_size</span><span class="p">(</span><span class="s1">&#39;postgres&#39;</span><span class="p">));</span><span class="w">
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Three Ways to Retain Your Dev Flow</title>
      <link>https://jerodsanto.net/2011/01/three-ways-to-retain-your-dev-flow/</link>
      <pubDate>Fri, 14 Jan 2011 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2011/01/three-ways-to-retain-your-dev-flow/</guid>
      
      
      <description><![CDATA[<p>Context switching: computers are great at it, humans suck at it.</p>
<p>Every time we developers lose the context of our current task we&rsquo;re forced to waste precious time getting it back. The harder the problem we&rsquo;re trying to solve, the longer it takes to reinstate its context in our mind. This is why many developers strive to reduce interruptions, set aside large blocks of time, and create an environment that helps them get into the <a href="http://amzn.to/hL0XDk">flow</a> <em>and stay there</em>.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Context switching: computers are great at it, humans suck at it.</p>
<p>Every time we developers lose the context of our current task we&rsquo;re forced to waste precious time getting it back. The harder the problem we&rsquo;re trying to solve, the longer it takes to reinstate its context in our mind. This is why many developers strive to reduce interruptions, set aside large blocks of time, and create an environment that helps them get into the <a href="http://amzn.to/hL0XDk">flow</a> <em>and stay there</em>.</p>
<p><a href="http://weavesilk.com/" title="Image created with the Silk project"><img src="http://jerodsanto.net/drop/silk-flow-20110113-171737.jpg" alt="the flow" title="The Flow"></a></p>
<p>The problem is, there are times when we absolutely must leave our development context behind and pick it back up later. Sometimes it&rsquo;s 30 minutes, sometimes it&rsquo;s overnight. I find these times very frustrating and have found a few techniques that help me quickly get my flow back upon returning.</p>
<h2 id="1-note-to-self">1) Note to Self</h2>
<p>Leave yourself a little note saying what you were up to. This is probably the most obvious technique, and can be quite effective. The problem I&rsquo;ve found with this is that I often forget to do it or am just too tired/lazy at the end of the day to do it consistently.</p>
<p>I need to write myself a note to remind myself to write myself notes. Guh, yeah.</p>
<h2 id="2-always-be-failing">2) Always Be Failing</h2>
<p>Leave one or more tests in your test suite failing (you are writing tests, yes?). When you return to the project your first step is to run the test suite and you&rsquo;ll see the failing test(s). This one works really well, but much like leaving yourself notes it&rsquo;s something that you have to actively participate in. It can actually take more time and effort to employ than leaving notes.</p>
<h2 id="3-git-dirty">3) Git Dirty</h2>
<p>This is my favorite and most oft used technique. Leave your Git (or the DVCS of your choice) staging area in a dirty state.</p>
<p><img src="http://jerodsanto.net/drop/git-dirty-20110113-173209.jpg" alt="git dirty" title="unstaged changes"></p>
<p>When you return, you&rsquo;ll see all the changes you most recently made before you left off. This, combined with a quick perusal of the commit log will quickly bring your context back. The advantage of this over the others is that it is somewhat participation-free. Simply <em>fail to</em> commit your last changes and they&rsquo;ll be there waiting for you. At least for me, this is far more likely to happen than technique #1 or #2.</p>
<h2 id="4-">4) ???</h2>
<p>Those are a few things I&rsquo;ve been doing to get my dev flow back quickly, but I&rsquo;m sure there are others. Do you have any tricks up your sleeve? I&rsquo;d love to hear &rsquo;em!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Bridging the Gap Between JavaScript&#39;s console.log and Cocoa&#39;s NSLog</title>
      <link>https://jerodsanto.net/2010/12/bridging-the-gap-between-javascripts-console.log-and-cocoas-nslog/</link>
      <pubDate>Mon, 27 Dec 2010 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2010/12/bridging-the-gap-between-javascripts-console.log-and-cocoas-nslog/</guid>
      
      
      <description><![CDATA[<p><a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/WebKit/Classes/WebView_Class/Reference/Reference.html">Cocoa&rsquo;s WebViews</a> provide a <a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/WebKit/Classes/WebScriptObject_Class/Reference/Reference.html%23//apple_ref/doc/c_ref/WebScriptObject">scripting object</a> through which you can execute JavaScript from Objective-C and Objective-C from JavaScript.</p>
<p>Unfortunately, getting feedback from the JavaScript executed inside a <code>WebView</code> is not totally straight forward. Exceptions are converted into <code>undefined</code>s (more on that <a href="http://www.thimbleby.net/script/">here</a>) and you can only get back a single return value to use for debugging.</p>
<p>Wouldn&rsquo;t it be neato if you could just continue using <code>console.log()</code> calls from inside JavaScript like you&rsquo;re used to and have the output displayed in Xcode&rsquo;s Debugger Console? Good news, folks. You can! Here&rsquo;s how:</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/WebKit/Classes/WebView_Class/Reference/Reference.html">Cocoa&rsquo;s WebViews</a> provide a <a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/WebKit/Classes/WebScriptObject_Class/Reference/Reference.html%23//apple_ref/doc/c_ref/WebScriptObject">scripting object</a> through which you can execute JavaScript from Objective-C and Objective-C from JavaScript.</p>
<p>Unfortunately, getting feedback from the JavaScript executed inside a <code>WebView</code> is not totally straight forward. Exceptions are converted into <code>undefined</code>s (more on that <a href="http://www.thimbleby.net/script/">here</a>) and you can only get back a single return value to use for debugging.</p>
<p>Wouldn&rsquo;t it be neato if you could just continue using <code>console.log()</code> calls from inside JavaScript like you&rsquo;re used to and have the output displayed in Xcode&rsquo;s Debugger Console? Good news, folks. You can! Here&rsquo;s how:</p>
<p>First, set a <code>frameLoadDelegate</code> for your <code>WebView</code>. I&rsquo;ll just use the application delegate to keep the example simple.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="cp">#import &#34;MyAppDelegate.h&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">@implementation</span> <span class="nc">MyAppDelegate</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">@synthesize</span> <span class="n">webView</span><span class="p">,</span> <span class="n">scriptObject</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">applicationDidFinishLaunching:</span><span class="p">(</span><span class="n">NSNotification</span> <span class="o">*</span><span class="p">)</span><span class="nv">aNotification</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="n">webView</span> <span class="nl">setFrameLoadDelegate</span><span class="p">:</span><span class="nb">self</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="n">webView</span> <span class="nl">setMainFrameURL</span><span class="p">:</span><span class="s">@&#34;http://jerodsanto.net&#34;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The delegate method to employ is <code>-webView:didFinishLoadForFrame</code>. This will be called after each frame in the <code>WebView</code> is loaded. Since you only want to set up the bridge once, check that the frame that was just loaded is named &ldquo;_top&rdquo; (<a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/WebKit/Classes/WebFrame_Class/Reference/Reference.html%23//apple_ref/doc/c_ref/WebFrame">more info</a>).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="p">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">webView:</span><span class="p">(</span><span class="n">WebView</span> <span class="o">*</span><span class="p">)</span><span class="nv">sender</span> <span class="nf">didFinishLoadForFrame:</span><span class="p">(</span><span class="n">WebFrame</span> <span class="o">*</span><span class="p">)</span><span class="nv">frame</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">frame</span> <span class="o">==</span> <span class="p">[</span><span class="n">frame</span> <span class="nl">findFrameNamed</span><span class="p">:</span><span class="s">@&#34;_top&#34;</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// bridge code will go here
</span></span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Once inside the <code>if</code> statement, the scripting environment is totally initialized. Get a reference to the scripting object:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="n">scriptObject</span> <span class="o">=</span> <span class="p">[</span><span class="n">sender</span> <span class="n">windowScriptObject</span><span class="p">];</span>
</span></span></code></pre></div><p>Now, register your object so its methods can be called from JavaScript:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="p">[</span><span class="n">scriptObject</span> <span class="nl">setValue</span><span class="p">:</span><span class="nb">self</span> <span class="nl">forKey</span><span class="p">:</span><span class="s">@&#34;MyApp&#34;</span><span class="p">];</span>
</span></span></code></pre></div><p>At this point, the instance of <code>MyAppDelegate</code> is accessible to JavaScript as <code>window.MyApp</code> and its methods can be called from JavaScript! Well, not just yet&hellip;</p>
<p>For safety reasons, you have to opt-in your Objective-C methods to be executable from JavaScript. First, add the method that will be called. It will simply take the message string from JavaScript and pass it to <code>NSLog</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="p">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">consoleLog:</span><span class="p">(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">aMessage</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">NSLog</span><span class="p">(</span><span class="s">@&#34;JSLog: %@&#34;</span><span class="p">,</span> <span class="n">aMessage</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Okay, the method is defined. Now it has to be made explicitly available to JavaScript by implementing a class method called <code>isSelectorExcludedFromWebScript</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="p">+</span> <span class="p">(</span><span class="kt">BOOL</span><span class="p">)</span><span class="nf">isSelectorExcludedFromWebScript:</span><span class="p">(</span><span class="kt">SEL</span><span class="p">)</span><span class="nv">aSelector</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">aSelector</span> <span class="o">==</span> <span class="k">@selector</span><span class="p">(</span><span class="nl">consoleLog</span><span class="p">:))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">NO</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">YES</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>All that is left now is to define/override the <code>window.console</code> object which will bridge its <code>log</code> function to the exposed <code>MyAppDelegate</code> object&rsquo;s <code>consoleLog</code> method:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="p">[</span><span class="n">scriptObject</span> <span class="nl">evaluateWebScript</span><span class="p">:</span><span class="s">@&#34;console = { log: function(msg) { MyApp.consoleLog_(msg); } }&#34;</span><span class="p">];</span>
</span></span></code></pre></div><p>That&rsquo;s all there is to it! Here is the example <code>MyAppDelegate.m</code> in its entirety:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="cp">#import &#34;MyAppDelegate.h&#34;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">@implementation</span> <span class="nc">MyAppDelegate</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">@synthesize</span> <span class="n">webView</span><span class="p">,</span> <span class="n">scriptObject</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">applicationDidFinishLaunching:</span><span class="p">(</span><span class="n">NSNotification</span> <span class="o">*</span><span class="p">)</span><span class="nv">aNotification</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="n">webView</span> <span class="nl">setFrameLoadDelegate</span><span class="p">:</span><span class="nb">self</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="n">webView</span> <span class="nl">setMainFrameURL</span><span class="p">:</span><span class="s">@&#34;http://jerodsanto.net&#34;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">consoleLog:</span><span class="p">(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">aMessage</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">NSLog</span><span class="p">(</span><span class="s">@&#34;JSLog: %@&#34;</span><span class="p">,</span> <span class="n">aMessage</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">+</span> <span class="p">(</span><span class="kt">BOOL</span><span class="p">)</span><span class="nf">isSelectorExcludedFromWebScript:</span><span class="p">(</span><span class="kt">SEL</span><span class="p">)</span><span class="nv">aSelector</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">aSelector</span> <span class="o">==</span> <span class="k">@selector</span><span class="p">(</span><span class="nl">consoleLog</span><span class="p">:))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">NO</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">YES</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nf">webView:</span><span class="p">(</span><span class="n">WebView</span> <span class="o">*</span><span class="p">)</span><span class="nv">sender</span> <span class="nf">didFinishLoadForFrame:</span><span class="p">(</span><span class="n">WebFrame</span> <span class="o">*</span><span class="p">)</span><span class="nv">frame</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">frame</span> <span class="o">==</span> <span class="p">[</span><span class="n">frame</span> <span class="nl">findFrameNamed</span><span class="p">:</span><span class="s">@&#34;_top&#34;</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">scriptObject</span> <span class="o">=</span> <span class="p">[</span><span class="n">sender</span> <span class="n">windowScriptObject</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="p">[</span><span class="n">scriptObject</span> <span class="nl">setValue</span><span class="p">:</span><span class="nb">self</span> <span class="nl">forKey</span><span class="p">:</span><span class="s">@&#34;MyApp&#34;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="p">[</span><span class="n">scriptObject</span> <span class="nl">evaluateWebScript</span><span class="p">:</span><span class="s">@&#34;console = { log: function(msg) { MyApp.consoleLog_(msg); } }&#34;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">@end</span>
</span></span></code></pre></div><p>Once you have this set up you can use <code>console.log</code>s to your heart&rsquo;s desire and get the feedback you need right there in Xcode. If there is an easier/better way, please do let me know. Hope this helps!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>The Tech Behind the New Grooveshark</title>
      <link>https://jerodsanto.net/2010/12/the-tech-behind-the-new-grooveshark/</link>
      <pubDate>Wed, 15 Dec 2010 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2010/12/the-tech-behind-the-new-grooveshark/</guid>
      
      
      <description><![CDATA[<p>I recently enjoyed the opportunity to help <a href="http://listen.grooveshark.com">Grooveshark</a> reinvent their web interface. There are many things I could say about this project, but if I were limited to a summary statement it would be that we stood on the shoulders of one giant open source community.</p>
<p>This post is my thanks to the authors and contributors of the software used. We probably <em>could have</em> done it without you, but I&rsquo;m sure glad we didn&rsquo;t have to!</p>]]></description>
      
      <content:encoded><![CDATA[<p>I recently enjoyed the opportunity to help <a href="http://listen.grooveshark.com">Grooveshark</a> reinvent their web interface. There are many things I could say about this project, but if I were limited to a summary statement it would be that we stood on the shoulders of one giant open source community.</p>
<p>This post is my thanks to the authors and contributors of the software used. We probably <em>could have</em> done it without you, but I&rsquo;m sure glad we didn&rsquo;t have to!</p>
<h2 id="jquery"><a href="http://jquery.com">jQuery</a></h2>
<p><a href="http://twitter.com/wycats">Yehuda Katz</a> recently quipped that <a href="http://jquery.com">jQuery</a> is becoming the standard library of the web. And why shouldn&rsquo;t it? It makes otherwise arduous DOM manipulation a breeze, its API is mature, and it has been proven on major production sites.</p>
<p>jQuery&rsquo;s other strength is its popularity. Some people dislike it for this reason, but I believe it is of real value. One of Grooveshark&rsquo;s goals for the rewrite was to improve designer/developer accessibility. Mission accomplished. There is no other JavaScript library on Earth that more people feel comfortable using.</p>
<p>In fact, Grooveshark had already chosen jQuery as a component before I joined the party. I didn&rsquo;t complain :)</p>
<h2 id="jquery-ui"><a href="http://jqueryui.com">jQuery UI</a></h2>
<p>You may not notice <a href="http://jqueryui.com">jQuery UI</a> in the mix at first glance, but it&rsquo;s in there.</p>
<p><img src="http://jerodsanto.net/drop/new-grooveshark-20101213-215629.jpg" alt="the new Grooveshark" title="The New Grooveshark"></p>
<p>The only widgets used were Autocomplete, Slider, and Datepicker, but we employed a bevy of its other features. It drives all drag and drop interaction on the site as well as enabling the use of another big component: SlickGrid (see below).</p>
<p>One of jQuery UI&rsquo;s greatest advantages (besides its use of jQuery, of course) is that you can pick and choose just what you need. Page load speed was another driving force of the rewrite, so this was paramount for us.</p>
<h2 id="javascript-mvc"><a href="http://www.javascriptmvc.com/">JavaScript MVC</a></h2>
<p>Grooveshark&rsquo;s Flash application was built with a <a href="http://en.wikipedia.org/wiki/Model-View-Controller">Model-View-Controller</a> (MVC) architecture and they were keen on retaining that paradaigm with the JavaScript application. We looked at a few options for this and ultimately settled on <a href="http://www.javascriptmvc.com/">JavaScript MVC</a> (JMVC).</p>
<p>JMVC has a boatload of features (code generation, dependency resolution, documentation, etc.), but we ended up building just upon the core functionality it exposes. At its base is John Resig&rsquo;s <a href="http://ejohn.org/blog/simple-javascript-inheritance/">Simple Class inheritance</a>, which is key. From there JMVC provides base Model and Controller classes from which you inherit. JMVC controllers are great and with full fledged classical inheritance at our disposal we could extend it to fit all of our needs.</p>
<p>JMVC&rsquo;s default templating engine is <a href="http://embeddedjs.com/getting_started.html">EJS</a>, which will look familiar to Ruby/Rails developers. Its similarity to vanilla HTML made it attractive right out of the gate (again, for developer accessibility) and powerful features like partials and view helpers means it can handle just about anything.</p>
<h2 id="slickgrid"><a href="https://github.com/mleibman/SlickGrid/wiki">SlickGrid</a></h2>
<p>A huge component of Grooveshark is the grid. Almost every content page has it, and it is busting at the seams with features (sorting, arbitrary row rendering, drag and drop, keyboard navigation, etc.).</p>
<p><img src="http://jerodsanto.net/drop/new-grooveshark-grid-20101214-164846.jpg" alt="Grooveshark&rsquo;s Slick Grid" title="Grooveshark's Slick Grid"></p>
<p>Features aside, the main thing the grid needs is to perform well with thousands of rows. Thankfully, <a href="https://github.com/mleibman/SlickGrid/wiki">SlickGrid</a> had crossed my radar a few months before the project began. Its adaptive virtual scrolling allows it to scale to hundreds of thousands of rows without losing responsiveness.</p>
<p>The library was a joy to work with. You should definitely check it out if you have advanced grid needs.</p>
<h2 id="jquery-hashchange"><a href="http://benalman.com/projects/jquery-hashchange-plugin/">jQuery Hashchange</a></h2>
<p>Grooveshark is a single page application with many &ldquo;pages&rdquo; inside it. These pages have pretty URLs nested behind a hash (#) tag. We needed to preserve all of the publicly available URLs while providing back button and history support. Originally, we looked to <a href="http://code.quirkey.com/sammy/">Sammy JS</a> for this functionality.</p>
<p>Sammy worked great for awhile, but we had some issues with back button support on older browsers. Also, Sammy provides a ton of functionality and we were only using a smidgeon of it. Instead, we switched to Ben Alman&rsquo;s <a href="http://benalman.com/projects/jquery-hashchange-plugin/">Hashchange plugin</a> and never looked back. It works great and has a small footprint.</p>
<h2 id="storejs"><a href="https://github.com/marcuswestin/store.js">Store.js</a></h2>
<p>Performance is a big deal to the Grooveshark team. We use local storage to persist user settings and libraries so they won&rsquo;t have to wait on the server to load up the application when they return. <a href="https://github.com/marcuswestin/store.js">Store.js</a> is a local storage wrapper that consolidates all the browser quirks into a single API. Awesome stuff.</p>
<h2 id="jquery-localize"><a href="https://github.com/coderifous/jquery-localize/">jQuery-localize</a></h2>
<p>Grooveshark currently supports switching between 17 languages. When the language is switched all localized elements on the page (and future elements) need to be swapped out. Our localization technique started with the <a href="https://github.com/coderifous/jquery-localize/">jquery-localize</a> plugin. We ended up extending it quite a bit to support some advanced string localizations, but the plugin definitely gave us a jumping off point.</p>
<h2 id="other-stuff">Other Stuff</h2>
<p>Those are the big pieces of the application, but there are other players that are worth mentioning:</p>
<ul>
<li><a href="https://github.com/phiggins42/bloody-jquery-plugins">PubSub</a> — communicating between disparate sections of the app</li>
<li><a href="https://github.com/tzuryby/jquery.hotkeys">jquery.hotkeys</a> — keyboard shortcuts ftw</li>
<li><a href="http://jursza.net/dev/jjmenu/">jjmenu</a> — context menus</li>
<li><a href="http://www.json.org/">JSON</a> — pretty much the best thing ever, duh</li>
<li><a href="http://documentcloud.github.com/underscore/">Underscore</a> — not used directly, but we yoinked a few functions from it</li>
<li><a href="http://www.yaml.org/">Yaml</a> — dependency management and configuration</li>
<li><a href="http://rake.rubyforge.org/">Rake</a> — build and deploy routines</li>
<li><a href="http://code.google.com/closure/compiler/">Closure Compiler</a> — JavaScript minification</li>
<li><a href="http://www.jslint.com/">JSLint</a> — keep our codes pretty</li>
<li><a href="https://github.com/grosser/smusher">Smusher</a> — optimize images without installing ImageMagick</li>
</ul>
<hr>
<p>If you authored or contributed to any of the projects listed above: <strong>THANKS!!</strong></p>
<p>If you are interested in any of them or have questions about how we implemented other pieces of the application, please do ask.</p>
<p>Oh yeah, there is One More Thing®. If you&rsquo;re a Grooveshark user, <a href="http://listen.grooveshark.com/#/user/jerodsanto/417270/">friend</a> me up. <a href="http://listen.grooveshark.com/#/user/jerodsanto/417270/community/fans">My community is weak</a>!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Making Rails 3&#39;s Mail Gem and Paperclip Play Nice</title>
      <link>https://jerodsanto.net/2010/12/making-rails-3s-mail-gem-and-paperclip-play-nice/</link>
      <pubDate>Thu, 09 Dec 2010 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2010/12/making-rails-3s-mail-gem-and-paperclip-play-nice/</guid>
      
      
      <description><![CDATA[<p>I have a Rails application that allows people to email in attachments that become documents in the system. I use the excellent <a href="https://github.com/thoughtbot/paperclip">Paperclip</a> gem to handle the files attached to the documents. You know, like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Document</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_attached_file</span> <span class="ss">:data</span><span class="p">,</span> <span class="ss">:url</span> <span class="o">=&gt;</span> <span class="s2">&#34;/system/docs/:id/:style/:filename&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># ... clip ...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>I <em>was</em> using the <a href="http://tmail.rubyforge.org/">TMail</a> gem to parse the incoming emails and send the attachments to Paperclip. It looked a bit like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">email</span> <span class="o">=</span> <span class="no">TMail</span><span class="o">::</span><span class="no">Mail</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">incoming</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">email</span><span class="o">.</span><span class="n">has_attachments?</span>
</span></span><span class="line"><span class="cl">  <span class="n">email</span><span class="o">.</span><span class="n">attachments</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">attachment</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span> <span class="o">=</span> <span class="no">Document</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">attachment</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>That&rsquo;s pretty easy. But..</p>]]></description>
      
      <content:encoded><![CDATA[<p>I have a Rails application that allows people to email in attachments that become documents in the system. I use the excellent <a href="https://github.com/thoughtbot/paperclip">Paperclip</a> gem to handle the files attached to the documents. You know, like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Document</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_attached_file</span> <span class="ss">:data</span><span class="p">,</span> <span class="ss">:url</span> <span class="o">=&gt;</span> <span class="s2">&#34;/system/docs/:id/:style/:filename&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># ... clip ...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>I <em>was</em> using the <a href="http://tmail.rubyforge.org/">TMail</a> gem to parse the incoming emails and send the attachments to Paperclip. It looked a bit like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">email</span> <span class="o">=</span> <span class="no">TMail</span><span class="o">::</span><span class="no">Mail</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">incoming</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">email</span><span class="o">.</span><span class="n">has_attachments?</span>
</span></span><span class="line"><span class="cl">  <span class="n">email</span><span class="o">.</span><span class="n">attachments</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">attachment</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span> <span class="o">=</span> <span class="no">Document</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">attachment</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>That&rsquo;s pretty easy. But..</p>
<p>TMail has been deprecated (it seg faults Ruby 1.9) and replaced by the shiny new <a href="https://github.com/mikel/mail">Mail</a> gem. I&rsquo;m not complaining, Mail is better than TMail in just about every way, but how it handles attachments doesn&rsquo;t jive with the old method of sending the data to Paperclip.</p>
<p>The reason is that Mail treats attachments just like any other message <code>Part</code>, but Paperclip expects an I/O object from which it can derive the <code>original_filename</code>, <code>content_type</code>, and <code>size</code>. TMail provides an object like this for each attachment, but we have to make Mail play nice. Here&rsquo;s how:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Mail</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Part</span> <span class="o">&lt;</span> <span class="no">Message</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nc">PaperclipAttachment</span> <span class="o">&lt;</span> <span class="no">StringIO</span>
</span></span><span class="line"><span class="cl">      <span class="kp">attr_accessor</span> <span class="ss">:original_filename</span><span class="p">,</span> <span class="ss">:content_type</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">to_paperclip</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="kp">nil</span> <span class="k">unless</span> <span class="n">attachment?</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">paperclip</span> <span class="o">=</span> <span class="no">PaperclipAttachment</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">body</span><span class="o">.</span><span class="n">decoded</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">paperclip</span><span class="o">.</span><span class="n">original_filename</span> <span class="o">=</span> <span class="n">filename</span><span class="o">.</span><span class="n">strip</span> <span class="k">unless</span> <span class="n">filename</span><span class="o">.</span><span class="n">blank?</span>
</span></span><span class="line"><span class="cl">      <span class="n">paperclip</span><span class="o">.</span><span class="n">content_type</span> <span class="o">=</span> <span class="n">content_type</span><span class="o">[</span><span class="sr">/^(.*);/</span><span class="p">,</span> <span class="mi">1</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">      <span class="n">paperclip</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>What&rsquo;s goin on here is opening up Mail&rsquo;s <code>Mail::Part</code> class and adding a new instance method called <code>to_paperclip</code>. That method will take the known data on the message part and create a new <code>PaperclipAttachment</code> object. The <code>PaperclipAttachment</code> class is just Ruby&rsquo;s <code>StringIO</code> class with two extra attributes that Paperclip needs.</p>
<p>The only tricky part is perhaps the regular expression being used to set <code>content_type</code>. The reason for this is that attachment parts have more than just the content type in their <code>ContentType</code> field. They usually look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">Content-Type: image/png; name=&#34;jms.png&#34;
</span></span></span></code></pre></div><p>We don&rsquo;t want the name in this case, so I&rsquo;m removing that portion. Please let me know if there is a better way of doing this (does Mail support it somehow?).</p>
<p>Now, revisiting the code to create new documents from email, except using our extended Mail instead of TMail:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">email</span> <span class="o">=</span> <span class="no">Mail</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">incoming</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">email</span><span class="o">.</span><span class="n">has_attachments?</span>
</span></span><span class="line"><span class="cl">  <span class="n">email</span><span class="o">.</span><span class="n">attachments</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">part</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span> <span class="o">=</span> <span class="no">Document</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">part</span><span class="o">.</span><span class="n">to_paperclip</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>And there you have it!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Minimally Awesome Todos</title>
      <link>https://jerodsanto.net/2010/12/minimally-awesome-todos/</link>
      <pubDate>Wed, 01 Dec 2010 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2010/12/minimally-awesome-todos/</guid>
      
      
      <description><![CDATA[<p>Let&rsquo;s face it, we all spend way too much time trying out different todo systems and not enough time actually DOING the tasks we type, jot, or <em>bleed</em> into them. If we were more honest with ourselves, we might even call them <em>todon&rsquo;t</em> lists. This problem is so bad that there are people who make <a href="http://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&amp;field-keywords=gtd&amp;x=0&amp;y=0">careers</a> out of telling other people how to actually get stuff done.</p>
<p>Kinda sad, huh?</p>
<p>The good news is that I found a system that Just Works® for me. It&rsquo;s minimal and it stays out of my way and yet it gets in my way just enough and, well, I think it&rsquo;s awesome. Maybe you will too. Maybe not, but even so you can take comfort in knowing that the perfect system for you is out there waiting to be found.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Let&rsquo;s face it, we all spend way too much time trying out different todo systems and not enough time actually DOING the tasks we type, jot, or <em>bleed</em> into them. If we were more honest with ourselves, we might even call them <em>todon&rsquo;t</em> lists. This problem is so bad that there are people who make <a href="http://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&amp;field-keywords=gtd&amp;x=0&amp;y=0">careers</a> out of telling other people how to actually get stuff done.</p>
<p>Kinda sad, huh?</p>
<p>The good news is that I found a system that Just Works® for me. It&rsquo;s minimal and it stays out of my way and yet it gets in my way just enough and, well, I think it&rsquo;s awesome. Maybe you will too. Maybe not, but even so you can take comfort in knowing that the perfect system for you is out there waiting to be found.</p>
<p>Okay, enough with the hyperbole. Here is how I&rsquo;m todoing it.</p>
<h2 id="my-needs">My Needs</h2>
<p>First off, YMMV. I&rsquo;m a particular guy and your needs may not line up with mine. Here&rsquo;s what I need from a todo system:</p>
<ul>
<li>
<p><strong>Easy Peasy</strong> — For me to actually <em>use</em> any system I must be able to add and remove tasks with virtually no effort. &ldquo;Effort&rdquo; in this context includes any steps between the task&rsquo;s inception in my brain to the moment when it is fully entered into the system.</p>
</li>
<li>
<p><strong>Visibility</strong> — Having a list of things you need to do is of no value if it doesn&rsquo;t stare you in the face on a regular basis. <a href="http://www.imdb.com/title/tt0175880/quotes?qt0372122">Quietly judging you</a>.</p>
</li>
<li>
<p><strong>Nothing Else</strong> — I don&rsquo;t need due dates or nesting or task dependencies or tagging or anything else. I don&rsquo;t even need sorting. These are not features. They are millstones.</p>
</li>
</ul>
<p>It&rsquo;s simple, really. I just need a list of things that need doing staring me in the face. And I need to manipulate that list with ease. Nothing more.</p>
<h2 id="my-system">My System</h2>
<p>The system I&rsquo;m using consists of three pieces, two of which can be implemented on any stock UNIX-based operating system. The third piece employs a freeware application for Mac OS X called <a href="http://projects.tynsoe.org/en/geektool/">GeekTool</a>.</p>
<h2 id="1-a-plaintext-file">1) A Plaintext File</h2>
<p>There is no better way to store a list of things than a good ole&rsquo; text file. They&rsquo;re portable and readable by almost anything. I put mine in <code>~/Documents/todo</code>, but I&rsquo;m considering moving it to my <a href="https://www.dropbox.com">Dropbox</a> folder instead.</p>
<h2 id="2-bash-functions">2) Bash Functions</h2>
<p>I started off using Vim and TextMate to manage the text file, but even this was too much effort. I pretty much live in Terminal.app, and with the help of the awesome <a href="http://visor.binaryage.com/">Visor</a> program I have access to it from anywhere on my system via a hot-key. So I wrote two very simple Bash functions instead. Be sure to set an environment variable for where the text file is located:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">TODO</span><span class="o">=</span>~/Documents/todo
</span></span></code></pre></div><p>The first functions adds tasks to the todo list (or prints it to <code>STDOUT</code> if no arguments are given):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">function</span> todo<span class="o">()</span> <span class="o">{</span> <span class="k">if</span> <span class="o">[</span> <span class="nv">$#</span> <span class="o">==</span> <span class="s2">&#34;0&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> cat <span class="nv">$TODO</span><span class="p">;</span> <span class="k">else</span> <span class="nb">echo</span> <span class="s2">&#34;• </span><span class="nv">$@</span><span class="s2">&#34;</span> &gt;&gt; <span class="nv">$TODO</span><span class="p">;</span> <span class="k">fi</span> <span class="o">}</span>
</span></span></code></pre></div><p>It is used like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ todo put on some shades
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ todo
</span></span></span><span class="line"><span class="cl"><span class="go">• put on some shades
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ todo eat some candy corn
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ todo
</span></span></span><span class="line"><span class="cl"><span class="go">• put on some shades
</span></span></span><span class="line"><span class="cl"><span class="go">• eat some candy corn
</span></span></span></code></pre></div><p>Notice that I don&rsquo;t have to wrap the task text in quotes or anything. Again, simplicity is key.</p>
<p>The second function removes tasks from the todo list that match the arguments passed in to it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">function</span> todone<span class="o">()</span> <span class="o">{</span> sed -i -e <span class="s2">&#34;/</span><span class="nv">$*</span><span class="s2">/d&#34;</span> <span class="nv">$TODO</span><span class="p">;</span> <span class="o">}</span>
</span></span></code></pre></div><p>If I wanted to remove one of the tasks I created above, I could type:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ todone shades
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ todo
</span></span></span><span class="line"><span class="cl"><span class="go">• eat some candy corn
</span></span></span></code></pre></div><p>It matches aggressively so I can get away with passing it as small a string as possible. This is a little dangerous because it will match multiple lines so I could end up removing more tasks than intended. For example, if I would have passed it the term <code>some</code> it would have removed both of my tasks. In practice I&rsquo;ve never run into that problem. I rarely allow myself to have 10+ tasks in my list at any point, so the chance of string collisions is low.</p>
<h2 id="3-a-desktop-embed">3) A Desktop Embed</h2>
<p>So far this system is nothing revolutionary. I wouldn&rsquo;t even call it special. Where my todo system really shines is in its ability to remind me of tasks I&rsquo;ve created without being outright obnoxious.</p>
<p>I decided to embed the list right on my Mac&rsquo;s desktop.</p>
<p><img src="http://jerodsanto.net/drop/todone-20101201-060519.png" alt="&rsquo;todone in action&rsquo;"></p>
<p>This is powerful for a few reasons:</p>
<ol>
<li>It&rsquo;s always visible.</li>
<li>It&rsquo;s subtle, not annoying.</li>
<li>I hate having anything on my desktop so it provides added motivation.</li>
</ol>
<p>I use <a href="http://projects.tynsoe.org/en/geektool/">GeekTool</a> to perform the embedding. GeekTool In it&rsquo;s own (ESL) words:</p>
<blockquote>
<p>It let you display on your desktop different kind of informations</p>
</blockquote>
<p>This is really powerful, but please don&rsquo;t get carried away with it! To embed your todo list, create a new Shell Geeklet that <code>cat</code>s your text file. Here are the relevant properties for the one I created:</p>
<p><img src="http://jerodsanto.net/drop/todone-geeklet-20101201-063708.png" alt="&lsquo;geeklet settings&rsquo;"></p>
<h2 id="todone">Todone</h2>
<p>I&rsquo;ve been using this system for awhile now (longer than any other I&rsquo;ve tried) and I still love it. The key is to keep your todo list small (isn&rsquo;t that the point, after all?) so you don&rsquo;t need to manage the crap out of it.</p>
<p>Now, if you&rsquo;ll excuse me. I&rsquo;m going to go <code>todone blog about todo system</code>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Took a Detour</title>
      <link>https://jerodsanto.net/2010/11/took-a-detour/</link>
      <pubDate>Mon, 01 Nov 2010 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2010/11/took-a-detour/</guid>
      
      
      <description><![CDATA[<p>Professionally I&rsquo;ve been writing a ton of JavaScript lately, but in my free time I took a detour into the land of Cocoa to develop a tiny little Mac app. It only does one thing, but hopefully it does it well enough to get some use.</p>
<p><a href="http://detoursapp.com/">Detours</a> is a GUI to manage your Mac&rsquo;s host lookups. Yes, you can already do this by manually editing <span class="keyword">/etc/hosts</span>, but I find that method difficult to explain to others, somewhat cumbersome and not very portable. Here&rsquo;s a shot of <a href="http://detoursapp.com">Detours</a> in action:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Professionally I&rsquo;ve been writing a ton of JavaScript lately, but in my free time I took a detour into the land of Cocoa to develop a tiny little Mac app. It only does one thing, but hopefully it does it well enough to get some use.</p>
<p><a href="http://detoursapp.com/">Detours</a> is a GUI to manage your Mac&rsquo;s host lookups. Yes, you can already do this by manually editing <span class="keyword">/etc/hosts</span>, but I find that method difficult to explain to others, somewhat cumbersome and not very portable. Here&rsquo;s a shot of <a href="http://detoursapp.com">Detours</a> in action:</p>
<p><a href="http://detoursapp.com"><img src="https://jerodsanto.net/wp-content/uploads/2010/10/Detours.png" alt="" title="Detours" width="380" height="266" class="aligncenter size-full wp-image-937" /></a></p>
<p>Please give it a try if you&rsquo;re on a Mac. I&rsquo;d love some feedback.</p>
<p>Overall I really like developing for the Mac and have a few ideas of apps I might try to get in the upcoming App Store. There are a few pain points for me, but most of them are alleviated by the very exciting <a href="http://www.macruby.org/">MacRuby</a> project.</p>
<p><a href="http://detoursapp.com"><img src="https://jerodsanto.net/wp-content/uploads/2010/10/dock.png" alt="" title="dock" width="380" height="218" class="aligncenter size-full wp-image-941" /></a></p>
<p>I&rsquo;d like to give a huge <strong>THANKS</strong> to Cody Peterson of <a href="http://secretpenguin.com">Secret Penguin</a> for donating his time &amp; skills to produce the <a href="http://detoursapp.com">Detours</a> icon &amp; website. To be quite honest, I believe the artwork outshines the app itself. And I&rsquo;m okay with that :)</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>LazerCatz!</title>
      <link>https://jerodsanto.net/2010/08/lazercatz/</link>
      <pubDate>Mon, 30 Aug 2010 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2010/08/lazercatz/</guid>
      
      
      <description><![CDATA[<p>I competed in the first ever <a href="http://nodeknockout.com/">Node Knockout</a> over the weekend with a <a href="http://twitter.com/jmhobbs">couple</a> of <a href="http://twitter.com/codypeterson">friends</a> and the fruit of our labor is an 8-bit multiplayer shooter called LazerCatz!</p>
<p><a href="http://www.lazercatzthegame.com/"><img src="https://jerodsanto.net/wp-content/uploads/2010/08/lazercatz.png" alt="" title="lazercatz" width="500" height="393" class="aligncenter size-full wp-image-931" /></a></p>
<p>It was my first foray into <a href="http://nodejs.org">Node</a> and I have to say that event-based programming is a lot easier in the browser than it is on the server, at least for me anyways. There was still tons of fun to be had and the awesome <a href="http://faye.jcoglan.com/">Faye</a> library made implementing a real-time game between many players a breeze.</p>]]></description>
      
      <content:encoded><![CDATA[<p>I competed in the first ever <a href="http://nodeknockout.com/">Node Knockout</a> over the weekend with a <a href="http://twitter.com/jmhobbs">couple</a> of <a href="http://twitter.com/codypeterson">friends</a> and the fruit of our labor is an 8-bit multiplayer shooter called LazerCatz!</p>
<p><a href="http://www.lazercatzthegame.com/"><img src="https://jerodsanto.net/wp-content/uploads/2010/08/lazercatz.png" alt="" title="lazercatz" width="500" height="393" class="aligncenter size-full wp-image-931" /></a></p>
<p>It was my first foray into <a href="http://nodejs.org">Node</a> and I have to say that event-based programming is a lot easier in the browser than it is on the server, at least for me anyways. There was still tons of fun to be had and the awesome <a href="http://faye.jcoglan.com/">Faye</a> library made implementing a real-time game between many players a breeze.</p>
<p>Please <a href="http://www.lazercatzthegame.com">check out the game</a> and <a href="http://nodeknockout.com/teams/lazercatz">vote/review it</a> if you have some free time. Also, if you want to participate next year please let me know, we were short a person this time around and we could have benefited from another set of hands.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Extract &amp; Edit a Safari Extension</title>
      <link>https://jerodsanto.net/2010/08/extract-edit-a-safari-extension/</link>
      <pubDate>Sat, 21 Aug 2010 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2010/08/extract-edit-a-safari-extension/</guid>
      
      
      <description><![CDATA[<p>I <a href="http://twitter.com/jerodsanto/status/21673729874">asked this on Twitter</a> the other day, but alas nobody came back with an answer.</p>
<p>Turns out it&rsquo;s pretty easy to edit a Safari extension that you&rsquo;ve downloaded. The downloaded file will have a <span class="keyword">.safariextz</span> file extension. To extract the contents of the file, use the <span class="keyword">xar</span> command. I&rsquo;ll demonstrate with the <a href="http://www.awarepixel.com/safari/bettersource/">BetterSource</a> extension.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~/Downloads$ xar -xf BetterSource-1.0.safariextz
</span></span></span></code></pre></div><p>This will create a new directory called <span class="keyword">BetterSource-1.0.safariextension</span> which has the plugin&rsquo;s source files (plists, html, js, css, etc.). You can add this to Safari&rsquo;s Extension Builder by:</p>]]></description>
      
      <content:encoded><![CDATA[<p>I <a href="http://twitter.com/jerodsanto/status/21673729874">asked this on Twitter</a> the other day, but alas nobody came back with an answer.</p>
<p>Turns out it&rsquo;s pretty easy to edit a Safari extension that you&rsquo;ve downloaded. The downloaded file will have a <span class="keyword">.safariextz</span> file extension. To extract the contents of the file, use the <span class="keyword">xar</span> command. I&rsquo;ll demonstrate with the <a href="http://www.awarepixel.com/safari/bettersource/">BetterSource</a> extension.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~/Downloads$ xar -xf BetterSource-1.0.safariextz
</span></span></span></code></pre></div><p>This will create a new directory called <span class="keyword">BetterSource-1.0.safariextension</span> which has the plugin&rsquo;s source files (plists, html, js, css, etc.). You can add this to Safari&rsquo;s Extension Builder by:</p>
<ol>
<li> Selecting Develop -> Extension Builder from Safari's menu bar</li>
<li> Clicking the + button in the lower-left corner of the editor window</li>
<li> Pointing the open dialog box to the BetterSource-1.0.safariextension directory</li>
</ol>
<p>And that&rsquo;s all there is to it.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Some Great iOS Apps</title>
      <link>https://jerodsanto.net/2010/06/some-great-ios-apps/</link>
      <pubDate>Sat, 19 Jun 2010 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2010/06/some-great-ios-apps/</guid>
      
      
      <description><![CDATA[<h2 id="instapaper">Instapaper</h2>
<p>Instapaper&rsquo;s &ldquo;read later&rdquo; bookmarklet will change (for the better) how you consume content online. The iOS app is exactly what you need to read the content you&rsquo;ve saved and nothing more. It&rsquo;s beautifully minimal.</p>
<h2 id="imdb">IMDb</h2>
<p>Just wow. The developers of this little gem have almost ensured I&rsquo;ll never visit their website again.</p>
<h2 id="amazon">Amazon</h2>
<p>I was hooked the first time I made some significant purchases while traveling 80+ mph down I-80. Massive value.</p>]]></description>
      
      <content:encoded><![CDATA[<h2 id="instapaper">Instapaper</h2>
<p>Instapaper&rsquo;s &ldquo;read later&rdquo; bookmarklet will change (for the better) how you consume content online. The iOS app is exactly what you need to read the content you&rsquo;ve saved and nothing more. It&rsquo;s beautifully minimal.</p>
<h2 id="imdb">IMDb</h2>
<p>Just wow. The developers of this little gem have almost ensured I&rsquo;ll never visit their website again.</p>
<h2 id="amazon">Amazon</h2>
<p>I was hooked the first time I made some significant purchases while traveling 80+ mph down I-80. Massive value.</p>
<h2 id="kayak">Kayak</h2>
<p>This app has tons of use cases, but the flight tracker alone makes it worth its weight in gold. Realtime updates on the status of your flights? Yes, please!</p>
<h2 id="strategery">Strategery</h2>
<p>An addictive game akin to risk.</p>
<h2 id="issh">iSSH</h2>
<p>Best of breed SSH application. Perfect when you&rsquo;re in a pinch.</p>
<h2 id="reeder-ipad">Reeder (iPad)</h2>
<p>Seriously gorgeous interface for your Google Reader feeds. I literally wait until I&rsquo;m around my iPad to check my RSS now. It&rsquo;s that good.</p>
<h2 id="ioctocat">iOctocat</h2>
<p>The best way to keep up with open-source activity on GitHub.</p>
<h2 id="dropbox">Dropbox</h2>
<p>I&rsquo;m sure you already know about Dropbox. Unsurprisingly, their iOS app is great too.</p>
<h2 id="usa-today-ipad">USA Today (iPad)</h2>
<p>Even old people would ditch their paper versions of the USA Today if they had this application on hand. Sometimes I check up on the news just to use it.</p>
<h2 id="npr-ipad">NPR (iPad)</h2>
<p>Peruse and listen to tons of NPR content. So good.</p>
<h2 id="twitterific-ipad">Twitterific (iPad)</h2>
<p>Despite the constant Twitter API Errors, this is one of the best feeling Twitter clients I&rsquo;ve ever used. It takes advantage of the iPad&rsquo;s form factor in every way.</p>
<h2 id="google-earth-ipad">Google Earth (iPad)</h2>
<p>This app is nothing short of amazing. It literally puts the world in your hands as you tap, pinch, and spin the world around with ease. This is future stuff, and we are fortunate to be able to use such technologies.</p>
<p>Sorry for no direct links, but I was too lazy to fetch all the iTunes URLs for the apps. Just run a search in the App Store and you&rsquo;ll find them with no problem.</p>
<p>Do you have any iOS apps that you absolutely love? Please let me know as I&rsquo;m always on the lookout for great software.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Rack, and Firebug, and Fuel. Oh Hai!</title>
      <link>https://jerodsanto.net/2010/03/rack-and-firebug-and-fuel.-oh-hai/</link>
      <pubDate>Sat, 20 Mar 2010 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2010/03/rack-and-firebug-and-fuel.-oh-hai/</guid>
      
      
      <description><![CDATA[<p><a href="http://fuelyourcoding.com"><img src="https://jerodsanto.net/wp-content/uploads/2010/03/fuel_coding_logo.gif" alt="" title="fuel_coding_logo" width="200" height="126" class="alignright size-medium wp-image-897" /></a></p>
<p>So there is this really cool Rack middleware written by <a href="http://sjjdev.com/">Simon Jefford</a> which allows you to send arbitrary messages from your Rails controllers &amp; views directly to Firebug&rsquo;s (or Web Inspector&rsquo;s) console. It is great for quick debugging. If that possibility excites you, head over to my recently published article <a href="http://fuelyourcoding.com/set-rails-logging-on-fire/">on Fuel Your Coding</a> to read all about it.</p>
<p>You may be wondering, what&rsquo;s up with this <a href="http://fuelyourcoding.com/">Fuel Your Coding</a> stuff? Well, I&rsquo;ll tell you what&rsquo;s up.</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="http://fuelyourcoding.com"><img src="https://jerodsanto.net/wp-content/uploads/2010/03/fuel_coding_logo.gif" alt="" title="fuel_coding_logo" width="200" height="126" class="alignright size-medium wp-image-897" /></a></p>
<p>So there is this really cool Rack middleware written by <a href="http://sjjdev.com/">Simon Jefford</a> which allows you to send arbitrary messages from your Rails controllers &amp; views directly to Firebug&rsquo;s (or Web Inspector&rsquo;s) console. It is great for quick debugging. If that possibility excites you, head over to my recently published article <a href="http://fuelyourcoding.com/set-rails-logging-on-fire/">on Fuel Your Coding</a> to read all about it.</p>
<p>You may be wondering, what&rsquo;s up with this <a href="http://fuelyourcoding.com/">Fuel Your Coding</a> stuff? Well, I&rsquo;ll tell you what&rsquo;s up.</p>
<p>I&rsquo;m very happy to announce that I&rsquo;ve joined the <a href="http://www.fuelbrandnetwork.com/">Fuel Network</a> as co-editor of <a href="http://fuelyourcoding.com">FYC</a>! I&rsquo;m excited to help improve upon an already awesome blog by cultivating more content on the technologies that I use and love. I won&rsquo;t be authoring articles there too often, but will be actively seeking and promoting quality contributions (If you&rsquo;d like an opportunity to write for <a href="http://fuelyourcoding.com/">FYC</a>, please contact me).</p>
<p>What does this mean for my blog? Not too much. Like before, I&rsquo;ll be writing here as inspiration hits. Which is rarely, hah!</p>
<p>If you enjoy my blog, I highly encourage you to subscribe to FYC&rsquo;s <a href="http://feeds.feedburner.com/FuelYourCoding">RSS feed</a> as I&rsquo;m sure you&rsquo;ll find future content there very interesting.</p>
<p>Cheers!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Exciting Projects I Watched in February</title>
      <link>https://jerodsanto.net/2010/03/exciting-projects-i-watched-in-february/</link>
      <pubDate>Sat, 06 Mar 2010 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2010/03/exciting-projects-i-watched-in-february/</guid>
      
      
      <description><![CDATA[<p>Another month, another handful of great open-source projects I found on GitHub. Let&rsquo;s get straight to it, shall we?</p>
<hr/>
<strong><a href="http://github.com/Me1000/RLOfflineDataStore">RLOfflineDataStore</a></strong> - <em>A simple Cappuccino wrapper for html5 offline data storage.</em>
<p><strong><a href="http://github.com/mleibman/SlickGrid">SlickGrid</a></strong> - <em>A lightening fast JavaScript grid/spreadsheet.</em> Finding a quality client-side grid tool is tough. This might just be one.</p>
<p><strong><a href="http://github.com/JulianEberius/Textmate-Minimap">Textmate-Minimap</a></strong> - <em>A minimap plugin for TextMate.</em> So great for those overly sized source documents we all hate to admit we have.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Another month, another handful of great open-source projects I found on GitHub. Let&rsquo;s get straight to it, shall we?</p>
<hr/>
<strong><a href="http://github.com/Me1000/RLOfflineDataStore">RLOfflineDataStore</a></strong> - <em>A simple Cappuccino wrapper for html5 offline data storage.</em>
<p><strong><a href="http://github.com/mleibman/SlickGrid">SlickGrid</a></strong> - <em>A lightening fast JavaScript grid/spreadsheet.</em> Finding a quality client-side grid tool is tough. This might just be one.</p>
<p><strong><a href="http://github.com/JulianEberius/Textmate-Minimap">Textmate-Minimap</a></strong> - <em>A minimap plugin for TextMate.</em> So great for those overly sized source documents we all hate to admit we have.</p>
<p><strong><a href="http://github.com/carlhuda/bundler">Bundler</a></strong> - <em>Gemfiles are fun.</em> - Like it or not, this will be the de facto for managing ruby gems. Might as well embrace it.</p>
<p><strong><a href="http://github.com/rails/jquery-ujs">jquery-ujs</a></strong> - Rails 3 has embraced unobtrusive JavaScript, and this is the official rails.js for jQuery.</p>
<p><strong><a href="http://github.com/razorjack/quicksand">quicksand</a></strong> - <em>jQuery plugin. Reorder and filter items with a nice shuffling animation.</em> GORGEOUS!</p>
<p><strong><a href="http://github.com/wbzyl/rack-codehighlighter">rack-codehighlighter</a></strong> - <em>Rack Middleware for code highlighting.</em> This will power my blog&rsquo;s syntax highlighting when I switch platforms to toto (see below).</p>
<p><strong><a href="http://github.com/ruby/ruby">ruby</a></strong> - <em>The Ruby Programming Language.</em> The official git mirror.</p>
<p><strong><a href="http://github.com/yakischloba/ruby-cisco">ruby-cisco</a></strong> - <em>Cisco Library for Ruby.</em> Remote control Cisco routers with ease.</p>
<p><strong><a href="http://github.com/jaz303/tipsy">tipsy</a></strong> - <em>Facebook-style tooltips plugin for jQuery.</em> I&rsquo;m using these on almost every new project.</p>
<p><strong><a href="http://github.com/cloudhead/toto">toto</a></strong> - <em>the 10 second blog-engine for hackers.</em> So awesome. Stay tuned for more on toto in the coming weeks.</p>
<hr/>
<p>That&rsquo;s it for now!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Exciting Projects I Watched In January</title>
      <link>https://jerodsanto.net/2010/02/exciting-projects-i-watched-in-january/</link>
      <pubDate>Tue, 02 Feb 2010 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2010/02/exciting-projects-i-watched-in-january/</guid>
      
      
      <description><![CDATA[<p>One of my favorite <a href="http://github.com">GitHub</a> features is the ability to find and track the progress of projects that interest me. I&rsquo;ve decided to post the new projects I find each month on this blog since there is a high likelihood that you and I have common interests (<a href="http://twitter.com/jerodsanto/status/6934234494">Hey, I&rsquo;m curating something on the internet!</a>).</p>
<img class="aligncenter size-full wp-image-876" title="watch-3" src="https://jerodsanto.net/wp-content/uploads/2010/02/watch-3.png" height="45" alt="" width="320" />
<p>So I wrote a little script using the GitHub API to track which new projects I watch during each month. I watched 17 new projects in January. They are linked below along with their authors&rsquo; descriptions and some commentary of my own:</p>]]></description>
      
      <content:encoded><![CDATA[<p>One of my favorite <a href="http://github.com">GitHub</a> features is the ability to find and track the progress of projects that interest me. I&rsquo;ve decided to post the new projects I find each month on this blog since there is a high likelihood that you and I have common interests (<a href="http://twitter.com/jerodsanto/status/6934234494">Hey, I&rsquo;m curating something on the internet!</a>).</p>
<img class="aligncenter size-full wp-image-876" title="watch-3" src="https://jerodsanto.net/wp-content/uploads/2010/02/watch-3.png" height="45" alt="" width="320" />
<p>So I wrote a little script using the GitHub API to track which new projects I watch during each month. I watched 17 new projects in January. They are linked below along with their authors&rsquo; descriptions and some commentary of my own:</p>
<p><strong><a href="http://github.com/isaac/BirdWatch">BirdWatch</a></strong> - <em>&ldquo;Twitter search client written in MacRuby&rdquo;</em> I&rsquo;m dying to give MacRuby a go, especially now that it has hit 0.5.</p>
<p><strong><a href="http://github.com/jfahrenkrug/CPVideoKit">CPVideoKit</a></strong> - <em>&ldquo;Cappuccino &amp; Objective-J wrapper for the YouTube JavaScript API.&rdquo;</em> I love all the stuff coming out of the Cappuccino community lately.</p>
<p><strong><a href="http://github.com/hammerdr/Cappuccino-Test-Extensions">Cappuccino-Test-Extensions</a></strong> - <em>&ldquo;A group of extensions, classes, and more that aid developers in testing Cappuccino applications.&rdquo;</em></p>
<p><strong><a href="http://github.com/eliasklughammer/EKImageReflection">EKImageReflection</a></strong> - <em>&ldquo;With EKImageReflection you can extend a regular CPImageView for displaying a reflection on it&rsquo;s bottom.&rdquo;</em></p>
<p><strong><a href="http://github.com/msanders/PNGSquash">PNGSquash</a></strong> - <em>&ldquo;A PNG compressor app for OS X.&rdquo;</em> Great tool for pre-processing images for web use.</p>
<p><strong><a href="http://github.com/jashkenas/coffee-script">coffee-script</a></strong> - <em>&ldquo;Unfancy JavaScript.&rdquo;</em> This is easily the most active project I follow. Dude codes all day and night.</p>
<p><strong><a href="http://github.com/jstorimer/delayed_paperclip">delayed_paperclip</a></strong> - <em>&ldquo;Process your Paperclip attachments in the background with delayed_job.&rdquo;</em> A nice implementation joining two of my favorite Rails-related gems.</p>
<p><strong><a href="http://github.com/plataformatec/has_scope">has_scope</a></strong> - <em>&ldquo;Maps controller filters to your resource scopes.&rdquo;</em> Super-useful for filtering on index actions.</p>
<p><strong><a href="http://github.com/jamespadolsey/jQuery-Lint">jQuery-Lint</a></strong> - <em>&ldquo;jQuery Lint is a simple script you can download and use with jQuery. It works over the top of jQuery and diligently reports errors and any incorrect usage of jQuery.&rdquo;</em> - I want this to support Web Inspector.</p>
<p><strong><a href="http://github.com/thumblemonks/jubilator">jubilator</a></strong> - <em>&ldquo;Browse public GitHub repos in style!&rdquo;</em> Great for when you don&rsquo;t want to clone a repo just to grok its source.</p>
<p><strong><a href="http://github.com/nakajima/makers-mark">makers-mark</a></strong> - <em>&ldquo;Syntax highlight your markdown.&rdquo;</em> Could be useful for statically generated blogging.</p>
<p><strong><a href="http://github.com/ryanb/nested_form">nested_form</a></strong> - <em>&ldquo;Rails plugin to conveniently handle multiple models in a single form.&rdquo;</em> &lsquo;Nuff said.</p>
<p><strong><a href="http://github.com/christkv/node-mongodb-native">node-mongodb-native</a></strong> - <em>&ldquo;Mongo DB Native NodeJS Driver.&rdquo;</em> Two technologies I&rsquo;m looking for an excuse to play with.</p>
<p><strong><a href="http://github.com/jtrupiano/rack-rewrite">rack-rewrite</a></strong> - <em>&ldquo;A web server agnostic rack middleware for defining and applying rewrite rules. In many cases you can get away with Rack::Rewrite instead of writing Apache mod_rewrite rules.&rdquo;</em></p>
<p><strong><a href="http://github.com/schacon/showoff">showoff</a></strong> - <em>&ldquo;the best damn presentation software a developer could ever love.&rdquo;</em> Bold claim. We&rsquo;ll see.</p>
<p><strong><a href="http://github.com/technoweenie/twitter-node">twitter-node</a></strong> - <em>&ldquo;node.js lib that creates a streaming connection with twitter and pushes any incoming statuses to a tweet event.&rdquo;</em> Could be a good learning tool.</p>
<p><strong><a href="http://github.com/proutils/webme">webme</a></strong> - <em>&ldquo;Convert your README into a Website.&rdquo;</em> Uhm, yes please!</p>
<p>That wraps it up for January. I hope you find one or more of these open-source projects interesting. Stay tuned for next month&rsquo;s list and <a href="http://github.com/jerodsanto">follow me</a> on GitHub to follow along!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Cappuccino On Rails</title>
      <link>https://jerodsanto.net/2010/01/cappuccino-on-rails/</link>
      <pubDate>Tue, 12 Jan 2010 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2010/01/cappuccino-on-rails/</guid>
      
      
      <description><![CDATA[<p>I&rsquo;m happy to announce the release of <a href="http://github.com/jerodsanto/CappuccinoResource">CappuccinoResource</a> (CR), a library dedicated to interfacing between a <a href="http://cappuccino.org">Cappuccino</a> front-end and a <a href="http://rubyonrails.org">Rails</a> back-end.</p>
<p>CR should feel very familiar to Rails developers. Its interface is akin to <a href="http://api.rubyonrails.org/classes/ActiveResource/Base.html">ActiveResource</a> and it borrows heavily from the (very good) <a href="http://iphoneonrails.com/">ObjectiveResource</a> library for the iPhone.</p>
<p>All basic CRUD operations are supported, and you can perform advanced finds with arbitrary parameters. A brief example of fetching a record, modifying it, and saving it:</p>]]></description>
      
      <content:encoded><![CDATA[<p>I&rsquo;m happy to announce the release of <a href="http://github.com/jerodsanto/CappuccinoResource">CappuccinoResource</a> (CR), a library dedicated to interfacing between a <a href="http://cappuccino.org">Cappuccino</a> front-end and a <a href="http://rubyonrails.org">Rails</a> back-end.</p>
<p>CR should feel very familiar to Rails developers. Its interface is akin to <a href="http://api.rubyonrails.org/classes/ActiveResource/Base.html">ActiveResource</a> and it borrows heavily from the (very good) <a href="http://iphoneonrails.com/">ObjectiveResource</a> library for the iPhone.</p>
<p>All basic CRUD operations are supported, and you can perform advanced finds with arbitrary parameters. A brief example of fetching a record, modifying it, and saving it:</p>
<pre tabindex="0"><code class="language-objj" data-lang="objj">var post = [Post find:@&#34;42&#34;];
[post setTitle:@&#34;Why X is Better than Y&#34;];
[post save];
</code></pre><p>Check out the README on the project&rsquo;s page <a href="http://github.com/jerodsanto/CappuccinoResource">on GitHub</a> for more details and usage examples.</p>
<h2 id="live-demo">Live Demo</h2>
<p>I also created a demo application which is a simplified clone of OS X&rsquo;s Address Book. The demo is <a href="http://capp-resource-example.heroku.com">live on Heroku</a>. Check it out. The source for the demo is also <a href="http://github.com/jerodsanto/CappResourceExample">on GitHub</a>.</p>
<p>If you&rsquo;re a Rails developer waiting for a good opportunity to try out Cappuccino, there&rsquo;s no better time than now.</p>
<p>If you&rsquo;re a Cappuccino developer looking for an easy-to-use, powerful back-end for your applications, Rails might be the answer for you.</p>
<p>CR is a young project, but it drives one of my client applications that is production-ready (albeit not deployed), so I believe it is ready for prime time. Please try it and let me know how it goes.</p>
<p>Fork, <a href="http://github.com/jerodsanto/CappuccinoResource/issues">report issues</a>, et cetera.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Testing Cappuccino Notifications</title>
      <link>https://jerodsanto.net/2010/01/testing-cappuccino-notifications/</link>
      <pubDate>Thu, 07 Jan 2010 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2010/01/testing-cappuccino-notifications/</guid>
      
      
      <description><![CDATA[<p>Writing a web app using <a href="http://cappuccino.org">Cappuccino</a> has a lot of benefits, one of which is a really nice message passing system wherein certain objects can register to observe events and take action when other objects post notifications of those events.</p>
<p>Here is a very basic way to test if your app is posting event notifications as you expect it to. First, create an Observer class inside a test helper file, which will be included into your tests:
~</p>]]></description>
      
      <content:encoded><![CDATA[<p>Writing a web app using <a href="http://cappuccino.org">Cappuccino</a> has a lot of benefits, one of which is a really nice message passing system wherein certain objects can register to observe events and take action when other objects post notifications of those events.</p>
<p>Here is a very basic way to test if your app is posting event notifications as you expect it to. First, create an Observer class inside a test helper file, which will be included into your tests:
~</p>
<pre tabindex="0"><code class="language-objj" data-lang="objj">// this is TestHelper.j
@import &lt;Foundation/CPObject.j&gt;

@implementation Observer : CPObject
{
    CPArray postedNotifications;
}

- (id)init
{
    if (self = [super init])
    {
        postedNotifications   = [CPArray array];
    }
    return self;
}

- (void)startObserving:(CPString)aNotificationName
{
    [[CPNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(notificationPosted:)
                                                 name:aNotificationName
                                               object:nil];
}

- (void)notificationPosted:(id)sender
{
    [postedNotifications addObject:[sender name]];
}

- (BOOL)didObserve:(CPString)aNotificationName
{
    return [postedNotifications containsObject:aNotificationName];
}

@end
</code></pre><p>This class can be configured to register for certain notifications via <code>-startObserving</code> and when they are posted, it stores them in an array (postedNotifications). You can then ask it at any time if a notification has been observed using the <code>-didObserve</code> method and it will respond with <code>YES</code> or <code>NO</code>.</p>
<p>So, to use this in your tests, do something like this:</p>
<pre tabindex="0"><code class="language-objj" data-lang="objj">// This is MyTest.j
@import &#34;TestHelper.j&#34;

@implementation MyTest : OJTestCase

- (void)setUp
{
    observer = [[Observer alloc] init];
}

- (void)testMyMethodDidPostNotification
{
    [observer startObserving:@&#34;MyMethodDidFinishExecution&#34;];
    // do stuff that you would expect to get the notification posted
    [self assertTrue:[observer didObserve:@&#34;MyMethodDidFinishExecution&#34;];
}

@end
</code></pre><p>You could get more fancy with this (like allowing object observing and not just name observing), but the concept doesn&rsquo;t change. Hope this helps anybody thinking about how to test their Capp apps!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Rubular</title>
      <link>https://jerodsanto.net/2009/12/rubular/</link>
      <pubDate>Wed, 02 Dec 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/12/rubular/</guid>
      
      
      <description><![CDATA[<p>Most of the online regular expression testers I&rsquo;ve used just aren&rsquo;t that useful, but <a href="http://rubular.com">Rubular</a> is the exception. It uses AJAX appropriately, has a clean aesthetic, and the quick reference at the bottom of the page is so useful it hurts. Plus, it has great features such as match extraction and the ability to create permalinks to your results.</p>
<img class="aligncenter size-full wp-image-812" title="rubular" src="https://jerodsanto.net/wp-content/uploads/2009/12/rubular.png" height="279" alt="rubular" width="650" />
<p><a href="http://rubular.com">Rubular</a> is only for Ruby regexes. I wish somebody would make one for JavaScript. Jubular, anyone?</p>]]></description>
      
      <content:encoded><![CDATA[<p>Most of the online regular expression testers I&rsquo;ve used just aren&rsquo;t that useful, but <a href="http://rubular.com">Rubular</a> is the exception. It uses AJAX appropriately, has a clean aesthetic, and the quick reference at the bottom of the page is so useful it hurts. Plus, it has great features such as match extraction and the ability to create permalinks to your results.</p>
<img class="aligncenter size-full wp-image-812" title="rubular" src="https://jerodsanto.net/wp-content/uploads/2009/12/rubular.png" height="279" alt="rubular" width="650" />
<p><a href="http://rubular.com">Rubular</a> is only for Ruby regexes. I wish somebody would make one for JavaScript. Jubular, anyone?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>280 Atlas Introductory Screencast</title>
      <link>https://jerodsanto.net/2009/11/280-atlas-introductory-screencast/</link>
      <pubDate>Mon, 16 Nov 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/11/280-atlas-introductory-screencast/</guid>
      
      
      <description><![CDATA[<p>The much anticipated <a href="https://atlas-beta.heroku.com/" rel="external">280 Atlas developer beta</a> is under way and I recorded a brief screencast introducing the basic concepts of creating applications with Atlas.</p>
<p>In it I build a simplified version of the payment calculator from chapter 2 of <a href="http://www.amazon.com/gp/product/0321535022?ie=UTF8&amp;tag=standadeviat-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0321535022">Cocoa Design Patterns</a>.</p>
<p>Topics covered include creating a Cib-based project, laying out an interface and binding outlets/actions to working code.</p>
<p>Hope it helps!</p>
<p><object height="360" width="640"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=7646585&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" /><embed allowfullscreen="true" src="http://vimeo.com/moogaloop.swf?clip_id=7646585&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" allowscriptaccess="always" type="application/x-shockwave-flash" height="360" width="640"></embed></object></p>
<p><a href="http://vimeo.com/7646585">280 Atlas Introductory Screencast</a> from <a href="http://vimeo.com/jerodsanto">Jerod Santo</a> on <a href="http://vimeo.com">Vimeo</a>.</p>]]></description>
      
      <content:encoded><![CDATA[<p>The much anticipated <a href="https://atlas-beta.heroku.com/" rel="external">280 Atlas developer beta</a> is under way and I recorded a brief screencast introducing the basic concepts of creating applications with Atlas.</p>
<p>In it I build a simplified version of the payment calculator from chapter 2 of <a href="http://www.amazon.com/gp/product/0321535022?ie=UTF8&amp;tag=standadeviat-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0321535022">Cocoa Design Patterns</a>.</p>
<p>Topics covered include creating a Cib-based project, laying out an interface and binding outlets/actions to working code.</p>
<p>Hope it helps!</p>
<p><object height="360" width="640"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=7646585&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" /><embed allowfullscreen="true" src="http://vimeo.com/moogaloop.swf?clip_id=7646585&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" allowscriptaccess="always" type="application/x-shockwave-flash" height="360" width="640"></embed></object></p>
<p><a href="http://vimeo.com/7646585">280 Atlas Introductory Screencast</a> from <a href="http://vimeo.com/jerodsanto">Jerod Santo</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
<p>Related Posts:</p>
<ul>
<li><a href="https://jerodsanto.net/2009/11/learning-cappuccino-a-linked-list">Learning Cappuccino: Resource List</a></li>
<li><a href="https://jerodsanto.net/2009/10/cappuccinoflow">CappuccinoFlow</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Ad Hoc Command-Line Notifications with Twitter</title>
      <link>https://jerodsanto.net/2009/11/ad-hoc-command-line-notifications-with-twitter/</link>
      <pubDate>Sat, 14 Nov 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/11/ad-hoc-command-line-notifications-with-twitter/</guid>
      
      
      <description><![CDATA[<p>Have you ever spent way too much time babysitting a long-running command? Code compilation, large file transfers, software upgrades and other time consuming tasks can trash productivity by requiring intermittent attention.</p>
<p>I have a novel idea; <strong>let&rsquo;s not do that anymore!</strong></p>
<p>There are undoubtedly dozens of solutions for this little problem, but I chose Ruby + Twitter for a few reasons:</p>
<ul>
<li>I like Ruby</li>
<li>I like Twitter</li>
<li>I want hassle-free SMS</li>
<li>I want portability (small client-side configuration)</li>
</ul>
<p>So, with those things in mind, here is what I&rsquo;ve come up with:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Have you ever spent way too much time babysitting a long-running command? Code compilation, large file transfers, software upgrades and other time consuming tasks can trash productivity by requiring intermittent attention.</p>
<p>I have a novel idea; <strong>let&rsquo;s not do that anymore!</strong></p>
<p>There are undoubtedly dozens of solutions for this little problem, but I chose Ruby + Twitter for a few reasons:</p>
<ul>
<li>I like Ruby</li>
<li>I like Twitter</li>
<li>I want hassle-free SMS</li>
<li>I want portability (small client-side configuration)</li>
</ul>
<p>So, with those things in mind, here is what I&rsquo;ve come up with:</p>
<h2 id="1-a-special-twitter-account">1) A Special Twitter Account</h2>
<p>Create a new account for your notifications. You&rsquo;ll most likely want to protect its tweets unless you don&rsquo;t mind <em>just about anybody</em> seeing all the notifications you&rsquo;re sending to yourself. Once the account is set up, follow it from your main Twitter account and enable SMS notifications for its tweets.</p>
<img class="aligncenter size-full wp-image-767" title="twitter-sms" src="https://jerodsanto.net/wp-content/uploads/2009/11/twitter-sms.png" height="136" alt="twitter-sms" width="532" />
<h2 id="2-a-dead-simple-ruby-script">2) A dead simple Ruby script</h2>
<p>I&rsquo;ve written about John Nunemaker&rsquo;s Twitter gem a <a href="https://jerodsanto.net/2009/05/expand-your-twitter-network-in-less-than-15-lines-of-ruby/">couple</a> of <a href="https://jerodsanto.net/2009/05/see-which-twitterers-dont-follow-youback-in-less-than-15-lines-of-ruby/">times</a>, and it once again makes its way into the toolbelt. Install if you don&rsquo;t have it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ sudo gem install twitter
</span></span></span></code></pre></div><p>This gem makes the notification script just a few lines of code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="ch">#!/usr/bin/env ruby</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rubygems&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;twitter&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">abort</span> <span class="s">%|Usage: #{File.basename(__FILE__)} &#34;your message&#34;|</span> <span class="k">unless</span> <span class="no">ARGV</span><span class="o">.</span><span class="n">length</span> <span class="o">==</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="n">user</span> <span class="o">=</span> <span class="s2">&#34;your_username&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">pass</span> <span class="o">=</span> <span class="s2">&#34;your_password&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">Twitter</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Twitter</span><span class="o">::</span><span class="no">HTTPAuth</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="n">pass</span><span class="p">))</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="s2">&#34;NOTICE: </span><span class="si">#{</span><span class="no">ARGV</span><span class="o">.</span><span class="n">first</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>You can name the script anything you like. I call it <code>twitter_notify</code>. Make it executable and ensure it is in your shell&rsquo;s execution path (I symlink it so I can keep my code organized):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ chmod +x src/ruby/twitter/twitter_notify.rb
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ ln -s ~/src/ruby/twitter/twitter_notify.rb /usr/local/bin/twitter_notify
</span></span></span></code></pre></div><h2 id="3-a-call-to-notify">3) A Call to Notify</h2>
<p>Anytime you want to be notified that a command has completed, just follow it with the <code>twitter_notify</code> command. There are a couple of ways to do this, and they are slightly different:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ cp /tmp/bigfile.tgz /somewhere/else &amp;&amp; twitter_notify &#34;all done copying!&#34;
</span></span></span></code></pre></div><p>Or:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ cp /tmp/bigfile.tgz /somewhere/else; twitter_notify &#34;command complete!&#34;
</span></span></span></code></pre></div><p>Using <code>&amp;&amp;</code> will only call the second command if the first command completed successfully. Using <code>;</code> will call the second command regardless of how the first command completed. Adjust usage depending on your circumstance.</p>
<h2 id="4-a-deserved-break">4) A Deserved Break</h2>
<p>Now that you&rsquo;ve set up your tools to handle the grunt work, walk away from the computer! Go outside, watch a movie, hang with your fam, who cares! When that task is complete you&rsquo;ll get an SMS and you can deal with it then.</p>
<p>That&rsquo;s all I got.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Managing Broken Symlinks</title>
      <link>https://jerodsanto.net/2009/11/managing-broken-symlinks/</link>
      <pubDate>Thu, 05 Nov 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/11/managing-broken-symlinks/</guid>
      
      
      <description><![CDATA[<p>I just added two new functions to my <a href="http://github.com/jerodsanto/dotfiles/blob/master/bashrc">bashrc</a> which make it super-simple to find &amp; remove broken symbolic links on your system.</p>
<p>They&rsquo;re simple wrappers around the ever-useful &ldquo;find&rdquo; utility:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">function</span> find_broken_symlinks<span class="o">()</span> <span class="o">{</span> find -x -L <span class="s2">&#34;</span><span class="si">${</span><span class="nv">1</span><span class="p">-.</span><span class="si">}</span><span class="s2">&#34;</span> -type l<span class="p">;</span> <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> rm_broken_symlinks<span class="o">()</span> <span class="o">{</span> find -x -L <span class="s2">&#34;</span><span class="si">${</span><span class="nv">1</span><span class="p">-.</span><span class="si">}</span><span class="s2">&#34;</span> -type l -exec rm <span class="o">{}</span> +<span class="p">;</span> <span class="o">}</span>
</span></span></code></pre></div><p>You can call the functions with a specific path:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ find_broken_symlinks /usr/local/bin
</span></span></span></code></pre></div><p>Or you can call them sans argument to search your current working directory:</p>]]></description>
      
      <content:encoded><![CDATA[<p>I just added two new functions to my <a href="http://github.com/jerodsanto/dotfiles/blob/master/bashrc">bashrc</a> which make it super-simple to find &amp; remove broken symbolic links on your system.</p>
<p>They&rsquo;re simple wrappers around the ever-useful &ldquo;find&rdquo; utility:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">function</span> find_broken_symlinks<span class="o">()</span> <span class="o">{</span> find -x -L <span class="s2">&#34;</span><span class="si">${</span><span class="nv">1</span><span class="p">-.</span><span class="si">}</span><span class="s2">&#34;</span> -type l<span class="p">;</span> <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> rm_broken_symlinks<span class="o">()</span> <span class="o">{</span> find -x -L <span class="s2">&#34;</span><span class="si">${</span><span class="nv">1</span><span class="p">-.</span><span class="si">}</span><span class="s2">&#34;</span> -type l -exec rm <span class="o">{}</span> +<span class="p">;</span> <span class="o">}</span>
</span></span></code></pre></div><p>You can call the functions with a specific path:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ find_broken_symlinks /usr/local/bin
</span></span></span></code></pre></div><p>Or you can call them sans argument to search your current working directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ find_broken_symlinks
</span></span></span></code></pre></div><p>Enjoy!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Learning Cappuccino: A Linked List</title>
      <link>https://jerodsanto.net/2009/11/learning-cappuccino-a-linked-list/</link>
      <pubDate>Sun, 01 Nov 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/11/learning-cappuccino-a-linked-list/</guid>
      
      
      <description><![CDATA[<p>Picking up a new framework can be a daunting task, especially if you&rsquo;re simultaneously learning a new language. I&rsquo;ve done this before when I learned Ruby &amp; Rails all in one fell swoop. It was not easy. I&rsquo;m doing it again with Objective-J and <a href="http://cappuccino.org">Cappuccino</a>.</p>
<p>Along the way I&rsquo;ve compiled a list of valuable resources. I believe many others will be taking this same journey in the coming days (especially once <a href="http://280atlas.com">Atlas</a> drops), so I&rsquo;m sharing my findings for the benefit of all.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Picking up a new framework can be a daunting task, especially if you&rsquo;re simultaneously learning a new language. I&rsquo;ve done this before when I learned Ruby &amp; Rails all in one fell swoop. It was not easy. I&rsquo;m doing it again with Objective-J and <a href="http://cappuccino.org">Cappuccino</a>.</p>
<p>Along the way I&rsquo;ve compiled a list of valuable resources. I believe many others will be taking this same journey in the coming days (especially once <a href="http://280atlas.com">Atlas</a> drops), so I&rsquo;m sharing my findings for the benefit of all.</p>
<h2 id="laying-foundation">Laying Foundation</h2>
<p><a href="http://www.amazon.com/gp/product/0596517742?ie=UTF8&amp;tag=standadeviat-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0596517742">JavaScript: The Good Parts</a> - <em>the</em> book to read on JavaScript</p>
<p><a href="http://www.amazon.com/gp/product/0321503619?ie=UTF8&amp;tag=standadeviat-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0321503619">Cocoa Programming for Mac OS X</a> - focus on the &ldquo;how&rdquo; of Cocoa</p>
<p><a href="http://www.amazon.com/gp/product/0321535022?ie=UTF8&amp;tag=standadeviat-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0321535022">Cocoa Design Patterns</a> - focus on the &ldquo;why&rdquo; of Cocoa</p>
<p><a href="http://cappuccino.org/learn/">Introduction to Cappuccino &amp; Objective-J</a> - you&rsquo;ve probably already read this</p>
<p><a href="http://cappuccino.org/learn/tutorials/objective-j-tutorial.php">Learning Objective-J</a> - this too, but if you haven&rsquo;t you absolutely should</p>
<p><a href="https://peepcode.com/products/objective-c-for-rubyists">Objective-C for Rubyists</a> - great ($9) screencast if you&rsquo;re coming from a Ruby background like me</p>
<h2 id="tutorials--walk-thrus">Tutorials &amp; Walk-Thrus</h2>
<p><a href="http://cappuccino.org/learn/tutorials/scrapbook-tutorial-1">Scrapbook Tutorial</a> - first &ldquo;real&rdquo; tutorial on cappuccino.org</p>
<p><a href="http://www.nice-panorama.com/Programmation/cappuccino/">Nice-Panorama Tutorials</a> - great breadth of content here</p>
<p><a href="http://cappuccinocasts.com">CappuccinoCasts</a> - Thomas does a great job with these. Watch &rsquo;em all</p>
<h2 id="reference-material">Reference Material</h2>
<p><a href="http://cappuccino.org/learn/documentation/">Cappuccino Online Documentation</a> - Unfortunately these are often outdated</p>
<p><a href="http://www.icoretech.org/2009/10/installing-cappuccino-from-source/">Compiling from source</a> - includes how to compile a local version of the documentation</p>
<p><a href="http://www.devworld.apple.com/cocoa/">Apple&rsquo;s Cocoa Documentation</a> - Often fills the voids in Capp&rsquo;s documentation for certain topics</p>
<p><a href="http://github.com/sazameki/cocoa-browser-air">Cocoa Browser Air</a> - A great tool for browsing Apple&rsquo;s documentation on a Mac</p>
<h2 id="staying-informed">Staying Informed</h2>
<p><a href="http://cappuccino.org/discuss/">Official Cappuccino Blog</a> - duh</p>
<p><a href="http://280north.com/blog/">280 North&rsquo;s blog</a> - because they&rsquo;re driving this train</p>
<p><a href="http://suitmymind.com/category/cappuccino.html">This Week in Edge Cappuccino</a> - summaries of recent changes &amp; additions</p>
<p><a href="http://cappuccinoflow.com">CappuccinoFlow</a> - a link blog with content provided by the community</p>
<p><a href="http://github.com/280north/cappuccino">GitHub repository</a> - if you&rsquo;re hardcore you can watch every commit to the project</p>
<h2 id="getting-help">Getting Help</h2>
<p><a href="http://groups.google.com/group/objectivej">Google Group</a> - good archives to search here as well</p>
<p><a href="irc://irc.freenode.net/cappuccino">IRC Channel</a> - direct IRC link</p>
<p><a href="https://wave.google.com/wave/?pli=1#restored:wave:googlewave.com!w%252Bu8FiBvxgC">Google Wave</a> - this is pretty new &amp; I haven&rsquo;t personally used it much</p>
<p>That&rsquo;s what I&rsquo;ve found &amp; used so far. I plan on updating this page when I come across new helpful resources so come back from time to time and consider <a href="https://jerodsanto.net/feed.xml">subscribing to my blog</a> as well.</p>
<p>Also, let me know in the comments of Cappuccino resources you&rsquo;ve found valuable and I&rsquo;ll add them to the list. Cheers!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>A WordPress Skeleton Key</title>
      <link>https://jerodsanto.net/2009/10/a-wordpress-skeleton-key/</link>
      <pubDate>Thu, 29 Oct 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/10/a-wordpress-skeleton-key/</guid>
      
      
      <description><![CDATA[<p>File this one under** &ldquo;scratching my own itch&rdquo;**</p>
<h2 id="a-problem">A Problem</h2>
<p>I often use WordPress as a CMS and have a couple of sites with many users contributing. I rarely go a week without an email or phone call from a user who needs help posting. When it comes to remote support there is no substitute for seeing what they&rsquo;re seeing.</p>
<p>However, if you want to login to the site with their user account you have to either ask for their password (<em>tacky &amp; insecure</em>) or reset their password temporarily (<em>amateurish &amp; annoying</em>).</p>]]></description>
      
      <content:encoded><![CDATA[<p>File this one under** &ldquo;scratching my own itch&rdquo;**</p>
<h2 id="a-problem">A Problem</h2>
<p>I often use WordPress as a CMS and have a couple of sites with many users contributing. I rarely go a week without an email or phone call from a user who needs help posting. When it comes to remote support there is no substitute for seeing what they&rsquo;re seeing.</p>
<p>However, if you want to login to the site with their user account you have to either ask for their password (<em>tacky &amp; insecure</em>) or reset their password temporarily (<em>amateurish &amp; annoying</em>).</p>
<h2 id="a-solution">A Solution</h2>
<p>~
<img class="alignright size-thumbnail wp-image-711" title="large_SkeletonKeyP" src="https://jerodsanto.net/wp-content/uploads/2009/10/large_SkeletonKeyP-150x150.jpg" height="150" alt="large_SkeletonKeyP" width="150" />
Say goodbye to the days of <em>tacky, amateurish, insecure &amp; annoying</em>. The <a href="http://wordpress.org/extend/plugins/skeleton-key/" rel="external">Skeleton Key</a> plugin allows WordPress administrators (level 10 users) to login to the site as any user by authenticating with the user&rsquo;s login and their own (administrator) password. Once logged in, you <em>are</em> that user. Handy, huh?</p>
<p><em><strong><strong>UPDATE</strong></strong></em>
The plugin has already gotten some TLC and it is now more performant and secure. We are now requiring admins to login with their own login followed by a &ldquo;+&rdquo; followed by the user&rsquo;s login. This will cut down on the chances of people guessing administrative passwords. In a weird, corny way the &ldquo;+&rdquo; is your digital skeleton key&hellip; so to login as user &ldquo;joeblow&rdquo; as an admin I would provide:</p>
<p>username = admin+joeblow
password = [the admin&rsquo;s password]
<strong>/<em><strong>UPDATE</strong></em></strong></p>
<h2 id="an-explanation">An Explanation</h2>
<p>This plugin is dead simple. It hooks into WordPress&rsquo; authentication chain using 2.8&rsquo;s new &lsquo;<strong>authenticate</strong>&rsquo; hook. The Skeleton Key&rsquo;s function sets its priority higher than the built-in authentication functions and checks the password against the admin account provided before the &ldquo;+&rdquo; in the database. If the check fails it returns an error and the next function in the chain is called (like normal). If it matches, the <a href="http://wordpress.org/extend/plugins/skeleton-key/" rel="external">Skeleton Key</a> hands back the user account tied to the login and you&rsquo;re good to go.</p>
<p>The <a href="http://github.com/jerodsanto/wp-skeleton-key" rel="external">source is on GitHub</a>, like usual. Feel free to grok it &amp; provide feedback if interested.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Cheating on Rails</title>
      <link>https://jerodsanto.net/2009/10/cheating-on-rails/</link>
      <pubDate>Tue, 20 Oct 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/10/cheating-on-rails/</guid>
      
      
      <description><![CDATA[<p>Fellow command-line junkies either love <a href="http://cheat.errtheblog.com/" rel="external">the cheat gem</a> by <a href="http://ozmm.org/" rel="external">Chris Wanstrath</a> or they&rsquo;ve never heard of it.</p>
<p>What &ldquo;cheat&rdquo; offers is a plethora (currently 601) of text-based cheat sheets at the tip of your fingers. Go ahead, give it a try:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ sudo gem install cheat
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ cheat apache2
</span></span></span></code></pre></div><p>Pretty cool, huh?</p>
<p>Some cheats are kind of long, so pipe them to &ldquo;less&rdquo; for pagination:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ cheat git | less
</span></span></span></code></pre></div><p>List all the cheats available:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Fellow command-line junkies either love <a href="http://cheat.errtheblog.com/" rel="external">the cheat gem</a> by <a href="http://ozmm.org/" rel="external">Chris Wanstrath</a> or they&rsquo;ve never heard of it.</p>
<p>What &ldquo;cheat&rdquo; offers is a plethora (currently 601) of text-based cheat sheets at the tip of your fingers. Go ahead, give it a try:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ sudo gem install cheat
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ cheat apache2
</span></span></span></code></pre></div><p>Pretty cool, huh?</p>
<p>Some cheats are kind of long, so pipe them to &ldquo;less&rdquo; for pagination:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ cheat git | less
</span></span></span></code></pre></div><p>List all the cheats available:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ cheat sheets
</span></span></span></code></pre></div><p>Or find one matching a search string:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ cheat sheets | grep [your search string]
</span></span></span></code></pre></div><p>To learn more about cheat:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ cheat cheat
</span></span></span></code></pre></div><h2 id="cheat-on-rails">Cheat on Rails</h2>
<p>There a bunch of Rails-related cheats, which are great help in a pinch. Here are a few that I highly recommend:</p>
<ul>
<li><strong>status_codes</strong> - all HTTP status codes and their matching Rails symbols</li>
<li><strong>rails_migrations</strong> - for when you forget valid data types</li>
<li><strong>rubydebug</strong> - debugging is powerful but it&rsquo;s easy to forget how</li>
<li><strong>rails_tips</strong> - nice reminders and tips for beginners</li>
<li><strong>jquery</strong> - you are using jQuery in your Rails apps, right?</li>
</ul>
<p>Let me know if you find any juicy cheats that I should know about.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>CappuccinoFlow</title>
      <link>https://jerodsanto.net/2009/10/cappuccinoflow/</link>
      <pubDate>Wed, 14 Oct 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/10/cappuccinoflow/</guid>
      
      
      <description><![CDATA[<h2 id="in-brief">In Brief</h2>
<p><a href="http://cappuccinoflow.com"><img title="CappuccinoFlow" src="https://jerodsanto.net/wp-content/uploads/2009/10/cf.png" height="125" alt="CappuccinoFlow" width="125" /></a>
I just launched <a href="http://cappuccinoflow.com" rel="external">CappuccinoFlow</a>: a community driven link blog for the <a href="http://www.cappuccino.org" rel="external">Cappuccino</a> framework. If you&rsquo;re at all interested in this amazing new technology out of <a href="http://280north.com/" rel="external">280north</a>, make sure to subscribe to the <a href="http://feeds.feedburner.com/cappuccinoflow">RSS feed</a>, follow along on <a href="http://twitter.com/cappuccinoflow" rel="external">Twitter</a>, and <a href="http://cappuccinoflow.com/items/new" rel="external">post</a> cool Cappuccino-related links to the flow!</p>
<h2 id="in-detail">In Detail</h2>
<p>It has been a little quiet around these parts lately. I blame Cappuccino.</p>
<p>For the uninitiated, Cappuccino is a framework for writing web applications (<a href="http://almost.at" rel="external">for example</a>). It is built on Objective-J, which is an Objective-C-esque superset of JavaScript. It is completely rad. Also, it&rsquo;s all client-side so I still get to use Rails on the back-end. I believe the apt word is: woot.</p>]]></description>
      
      <content:encoded><![CDATA[<h2 id="in-brief">In Brief</h2>
<p><a href="http://cappuccinoflow.com"><img title="CappuccinoFlow" src="https://jerodsanto.net/wp-content/uploads/2009/10/cf.png" height="125" alt="CappuccinoFlow" width="125" /></a>
I just launched <a href="http://cappuccinoflow.com" rel="external">CappuccinoFlow</a>: a community driven link blog for the <a href="http://www.cappuccino.org" rel="external">Cappuccino</a> framework. If you&rsquo;re at all interested in this amazing new technology out of <a href="http://280north.com/" rel="external">280north</a>, make sure to subscribe to the <a href="http://feeds.feedburner.com/cappuccinoflow">RSS feed</a>, follow along on <a href="http://twitter.com/cappuccinoflow" rel="external">Twitter</a>, and <a href="http://cappuccinoflow.com/items/new" rel="external">post</a> cool Cappuccino-related links to the flow!</p>
<h2 id="in-detail">In Detail</h2>
<p>It has been a little quiet around these parts lately. I blame Cappuccino.</p>
<p>For the uninitiated, Cappuccino is a framework for writing web applications (<a href="http://almost.at" rel="external">for example</a>). It is built on Objective-J, which is an Objective-C-esque superset of JavaScript. It is completely rad. Also, it&rsquo;s all client-side so I still get to use Rails on the back-end. I believe the apt word is: woot.</p>
<p>I wanted a link blog to support the small, but growing, Cappuccino community. I&rsquo;m a big fan of <a href="http://rubyflow.com" rel="external">RubyFlow</a> so I contacted its creator, <a href="http://peterc.org" rel="external">Peter Cooper</a>, and asked him if I could set one up for Cappuccino. He was gracious enough to say yes, and he had conveniently <a href="http://github.com/Sutto/rubyflow/" rel="external">open-sourced</a> an old version of his site awhile back. I <a href="http://github.com/jerodsanto/cappuccinoflow" rel="external">forked it</a> on GitHub and the result is <a href="http://cappuccinoflow.com" rel="external">CappuccinoFlow</a>.</p>
<p>I&rsquo;ll probably have to modify my blog&rsquo;s headline soon, because once I become proficient in Objective-J &amp; Cappuccino you&rsquo;ll probably see some posts about it.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Run OS X System Profiler From Terminal</title>
      <link>https://jerodsanto.net/2009/09/run-os-x-system-profiler-from-terminal/</link>
      <pubDate>Wed, 09 Sep 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/09/run-os-x-system-profiler-from-terminal/</guid>
      
      
      <description><![CDATA[<p>OS X&rsquo;s built-in <code>System Profiler</code> provides a great graphical display of pretty much anything you&rsquo;ll want to know about your Mac.</p>
<img class="aligncenter size-full wp-image-664" title="profiler" src="https://jerodsanto.net/wp-content/uploads/2009/09/profiler.png" height="216" alt="profiler" width="640" />
<p>That&rsquo;s cool and all, but what if you want to access that information programmatically? Turns out you can also run the <strong><em>System Profiler</em></strong> from the terminal by executing this command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ /usr/sbin/system_profiler
</span></span></span></code></pre></div><p>What&rsquo;s great about this access method is that it allows you to slurp that data into any other program and have your way with it! For instance, I wanted to track my <a href="http://twitter.com/jerodsanto/status/3780056282" rel="external">new battery&rsquo;s</a> cycle count and charge capacity over time. Why? I dunno, because I&rsquo;m a geek, okay, get off my back!&hellip; Anyways, with the <strong><em>system_profiler</em></strong> command I simply run this little Ruby script every day:</p>]]></description>
      
      <content:encoded><![CDATA[<p>OS X&rsquo;s built-in <code>System Profiler</code> provides a great graphical display of pretty much anything you&rsquo;ll want to know about your Mac.</p>
<img class="aligncenter size-full wp-image-664" title="profiler" src="https://jerodsanto.net/wp-content/uploads/2009/09/profiler.png" height="216" alt="profiler" width="640" />
<p>That&rsquo;s cool and all, but what if you want to access that information programmatically? Turns out you can also run the <strong><em>System Profiler</em></strong> from the terminal by executing this command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ /usr/sbin/system_profiler
</span></span></span></code></pre></div><p>What&rsquo;s great about this access method is that it allows you to slurp that data into any other program and have your way with it! For instance, I wanted to track my <a href="http://twitter.com/jerodsanto/status/3780056282" rel="external">new battery&rsquo;s</a> cycle count and charge capacity over time. Why? I dunno, because I&rsquo;m a geek, okay, get off my back!&hellip; Anyways, with the <strong><em>system_profiler</em></strong> command I simply run this little Ruby script every day:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;date&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">data</span>      <span class="o">=</span> <span class="sb">`/usr/sbin/system_profiler SPPowerDataType`</span>
</span></span><span class="line"><span class="cl"><span class="n">cycles</span>    <span class="o">=</span> <span class="n">data</span><span class="o">[</span><span class="sr">/Cycle count: (\d+)/</span><span class="p">,</span> <span class="mi">1</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="n">condition</span> <span class="o">=</span> <span class="n">data</span><span class="o">[</span><span class="sr">/Condition: (\w+)/</span><span class="p">,</span> <span class="mi">1</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="n">capacity</span>  <span class="o">=</span> <span class="n">data</span><span class="o">[</span><span class="sr">/Full charge capacity \(mAh\): (\d+)/</span><span class="p">,</span> <span class="mi">1</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s1">&#39;/Users/jerod/Documents/battery_history.csv&#39;</span><span class="p">,</span> <span class="s2">&#34;a&#34;</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">file</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">Date</span><span class="o">.</span><span class="n">today</span><span class="si">}</span><span class="s2">,</span><span class="si">#{</span><span class="n">cycles</span><span class="si">}</span><span class="s2">,</span><span class="si">#{</span><span class="n">capacity</span><span class="si">}</span><span class="s2">,</span><span class="si">#{</span><span class="n">condition</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Ruby uses the (possibly familiar) backticks to capture output from a shell command. All that was left for me to do was to parse the raw data and save it to a CSV file.</p>
<p>Finally, note that the my script is passing <code>SPPowerDataType</code> as an argument which narrows down the returned results. You can learn more about how to use the <code>system_profiler</code> command by readings its manual. Just:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ man system_profiler
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>cd up, up, and a-up</title>
      <link>https://jerodsanto.net/2009/09/cd-up-up-and-a-up/</link>
      <pubDate>Mon, 07 Sep 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/09/cd-up-up-and-a-up/</guid>
      
      
      <description><![CDATA[<p>Command-line jockeys are intimately familiar with the <code>cd</code> command. We&rsquo;ve typed commands like this one a gozillion times:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src/ruby/rails$ cd ..
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src/ruby$
</span></span></span></code></pre></div><p>We all know that <code>.</code> represents the current working directory and <code>..</code> represents the current working directory&rsquo;s parent directory. If we follow this pattern to its logical conclusion, then <code>...</code> would represent the current working directory&rsquo;s parent&rsquo;s parent, etc. Unfortunately, <code>cd</code> doesn&rsquo;t work that way.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~src/ruby/rails$ cd ...
</span></span></span><span class="line"><span class="cl"><span class="go">-bash: cd: ...: No such file or directory
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~$
</span></span></span></code></pre></div><p>Total bummer!</p>]]></description>
      
      <content:encoded><![CDATA[<p>Command-line jockeys are intimately familiar with the <code>cd</code> command. We&rsquo;ve typed commands like this one a gozillion times:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src/ruby/rails$ cd ..
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src/ruby$
</span></span></span></code></pre></div><p>We all know that <code>.</code> represents the current working directory and <code>..</code> represents the current working directory&rsquo;s parent directory. If we follow this pattern to its logical conclusion, then <code>...</code> would represent the current working directory&rsquo;s parent&rsquo;s parent, etc. Unfortunately, <code>cd</code> doesn&rsquo;t work that way.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~src/ruby/rails$ cd ...
</span></span></span><span class="line"><span class="cl"><span class="go">-bash: cd: ...: No such file or directory
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~$
</span></span></span></code></pre></div><p>Total bummer!</p>
<p>Instead, we have to repeat ourselves for every directory up we want to move. So, we end up typing commands that look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src/ruby/rails$ cd ../..
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src$
</span></span></span></code></pre></div><p>The more directories up to move in the file system, the more ridiculous the command becomes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src/ruby/rails/box-office/app/models$ cd ../../../../..
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src$
</span></span></span></code></pre></div><div class="notice">in this contrived example, cd ~/src would be a much better way of navigating</div>
<p>If you&rsquo;ve ever typed a command like that one, you feel my pain. I put up with this for a long time, but I&rsquo;ve finally had enough of it. Here is a simple <strong>bash</strong> function that allows us to simply append <strong><em>.</em></strong>&rsquo;s to the <strong><em>cd</em></strong> command for each directory we want to move up.</p>
<img title="bad_function" src="https://jerodsanto.net/wp-content/uploads/2009/09/bad_function.png"/>
<div class="notice"><a href="https://jerodsanto.net/2009/09/cd-up-up-up/#comment-16188007">some guy</a> pointed out that my function didn&rsquo;t correctly handle pathnames with spaces in it and he was kind enough to supply a simplified function to the one I wrote.</div>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">function</span> <span class="nb">cd</span> <span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="o">[[</span> <span class="nv">$#</span> &gt; <span class="m">0</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> <span class="si">${</span><span class="nv">1</span><span class="p">:</span><span class="nv">0</span><span class="p">:</span><span class="nv">2</span><span class="si">}</span> <span class="o">==</span> <span class="s1">&#39;..&#39;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">      <span class="nv">rest</span><span class="o">=</span><span class="si">${</span><span class="nv">1</span><span class="p">:</span><span class="nv">2</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">      <span class="nv">rest</span><span class="o">=</span><span class="si">${</span><span class="nv">rest</span><span class="p">//./../</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">      <span class="nb">builtin</span> <span class="nb">cd</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">1</span><span class="p">:</span><span class="nv">0</span><span class="p">:</span><span class="nv">2</span><span class="si">}</span><span class="s2">/</span><span class="si">${</span><span class="nv">rest</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="nb">builtin</span> <span class="nb">cd</span> <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="nb">builtin</span> <span class="nb">cd</span>
</span></span><span class="line"><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>This is a simple wrapper function that ends up calling the shell&rsquo;s built-in <code>cd</code> command. Drop it in your <code>.bashrc</code> or <code>.bash_profile</code> and give it a whirl!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src/ruby/rails$ cd ...
</span></span></span><span class="line"><span class="cl"><span class="go">jerod@mbp:~/src$
</span></span></span></code></pre></div><p>Much better!</p>
<div class="notice">I added this function to my <code>.bashrc</code> which I store in a <a href="http://github.com/jerodsanto/dotfiles/tree/master">dotfiles repo on GitHub</a>. Feel free to check it out and see if there&rsquo;s any other useful tricks you can add to your command-line répertoire.</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Kill Snow Leopard&#39;s Blue Ring of Expos&amp;eacute;</title>
      <link>https://jerodsanto.net/2009/09/kill-snow-leopards-blue-ring-of-exposeacute/</link>
      <pubDate>Tue, 01 Sep 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/09/kill-snow-leopards-blue-ring-of-exposeacute/</guid>
      
      
      <description><![CDATA[<p>Looks like <a href="https://twitter.com/dougneiner/status/3679180990">I&rsquo;m not the only one</a> who hates Snow Leopard&rsquo;s blue ring around selected windows in Exposé.</p>
<p>Thankfully, we don&rsquo;t have to live with such monstrosities. Here&rsquo;s a quick fix to free yourself from the blue haze. Fire up Terminal.app, then:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">cd /System/Library/CoreServices/Dock.app/Contents/Resources/
</span></span></span><span class="line"><span class="cl"><span class="go">sudo mv expose-window-selection-big.png expose-window-selection-big.ugly
</span></span></span><span class="line"><span class="cl"><span class="go">sudo mv expose-window-selection-small.png expose-window-selection-small.ugly
</span></span></span><span class="line"><span class="cl"><span class="go">sudo killall Dock
</span></span></span></code></pre></div><p>This will disable the blue rings altogether, but you may want to replace these two images with some custom ones that look a little cooler instead.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Looks like <a href="https://twitter.com/dougneiner/status/3679180990">I&rsquo;m not the only one</a> who hates Snow Leopard&rsquo;s blue ring around selected windows in Exposé.</p>
<p>Thankfully, we don&rsquo;t have to live with such monstrosities. Here&rsquo;s a quick fix to free yourself from the blue haze. Fire up Terminal.app, then:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">cd /System/Library/CoreServices/Dock.app/Contents/Resources/
</span></span></span><span class="line"><span class="cl"><span class="go">sudo mv expose-window-selection-big.png expose-window-selection-big.ugly
</span></span></span><span class="line"><span class="cl"><span class="go">sudo mv expose-window-selection-small.png expose-window-selection-small.ugly
</span></span></span><span class="line"><span class="cl"><span class="go">sudo killall Dock
</span></span></span></code></pre></div><p>This will disable the blue rings altogether, but you may want to replace these two images with some custom ones that look a little cooler instead.</p>
<p>Let me know via the comments or on <a href="https://twitter.com/jerodsanto">Twitter</a> if you find some replacement images!</p>
<p><em><strong><strong>UPDATE</strong></strong></em></p>
<p><a href="https://www.google.com/profiles/douglasneiner">My friend Doug</a> provided some <a href="https://jerodsanto.net/wp-content/uploads/2009/09/expose-rings.zip">drop-in replacement images</a> that are a huge improvement. Just drop them into the directory after renaming the default images. So after step #3 above, do:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">open .
</span></span></span></code></pre></div><p>This will open Finder in the current directory. Now drag the replacement images into this directory, provide your password, and kill the Dock.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">sudo killall Dock
</span></span></span></code></pre></div><p>VoilÃ !</p>
<p><a href="https://jerodsanto.net/wp-content/uploads/2009/09/expose-rings.zip">DOWNLOAD HERE</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Just Abort It</title>
      <link>https://jerodsanto.net/2009/08/just-abort-it/</link>
      <pubDate>Mon, 24 Aug 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/08/just-abort-it/</guid>
      
      
      <description><![CDATA[<p>A lot of people end up writing Ruby methods that looks something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">stop_error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="s2">&#34;ERROR: </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Which they call in their app like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">stop_error</span> <span class="s2">&#34;Oh noes, file doesn&#39;t exist!&#34;</span> <span class="k">unless</span> <span class="no">File</span><span class="o">.</span><span class="n">exist?</span><span class="p">(</span><span class="n">file</span><span class="p">)</span>
</span></span></code></pre></div><p>I used to write that method a lot too. Did you know Ruby has a built-in method that provides just what we&rsquo;re all looking for?</p>
<p><code>Kernel::abort</code></p>
<p>So, stop writing your own little method and just abort it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">abort</span> <span class="s2">&#34;Oh noes, file doesn&#39;t exist!&#34;</span> <span class="k">unless</span> <span class="no">File</span><span class="o">.</span><span class="n">exist?</span><span class="p">(</span><span class="n">file</span><span class="p">)</span>
</span></span></code></pre></div>]]></description>
      
      <content:encoded><![CDATA[<p>A lot of people end up writing Ruby methods that looks something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">stop_error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="s2">&#34;ERROR: </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Which they call in their app like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">stop_error</span> <span class="s2">&#34;Oh noes, file doesn&#39;t exist!&#34;</span> <span class="k">unless</span> <span class="no">File</span><span class="o">.</span><span class="n">exist?</span><span class="p">(</span><span class="n">file</span><span class="p">)</span>
</span></span></code></pre></div><p>I used to write that method a lot too. Did you know Ruby has a built-in method that provides just what we&rsquo;re all looking for?</p>
<p><code>Kernel::abort</code></p>
<p>So, stop writing your own little method and just abort it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">abort</span> <span class="s2">&#34;Oh noes, file doesn&#39;t exist!&#34;</span> <span class="k">unless</span> <span class="no">File</span><span class="o">.</span><span class="n">exist?</span><span class="p">(</span><span class="n">file</span><span class="p">)</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>AppStore Ruby Module</title>
      <link>https://jerodsanto.net/2009/08/appstore-ruby-module/</link>
      <pubDate>Sat, 22 Aug 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/08/appstore-ruby-module/</guid>
      
      
      <description><![CDATA[<p>So I was writing my first <a href="%22http://iphoneonrails.com/%22">iPhone on Rails</a> app when my <a href="%22http://www.google.com/reader/shared/05234492019244193889%22">Google Reader</a> dropped some pretty awful news on me. Somebody pre-empted my release with their (pretty excellent, but poorly named) <strong>&ldquo;Bargain Bin with Push!&rdquo;</strong> app (<a href="%22http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=302951751&amp;mt=8%22">iTunes link</a>).</p>
<h2 id="the-bad-news">The Bad News</h2>
<p><strong>&ldquo;Bargain Bin with Push!&rdquo;</strong> does pretty much exactly what my app was going to do. It provides push notifications when apps you&rsquo;re interested in drop in price. Instead of raging against the dying of the light, I&rsquo;ve decided to just user their app and let mine go gently into that good night.</p>]]></description>
      
      <content:encoded><![CDATA[<p>So I was writing my first <a href="%22http://iphoneonrails.com/%22">iPhone on Rails</a> app when my <a href="%22http://www.google.com/reader/shared/05234492019244193889%22">Google Reader</a> dropped some pretty awful news on me. Somebody pre-empted my release with their (pretty excellent, but poorly named) <strong>&ldquo;Bargain Bin with Push!&rdquo;</strong> app (<a href="%22http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=302951751&amp;mt=8%22">iTunes link</a>).</p>
<h2 id="the-bad-news">The Bad News</h2>
<p><strong>&ldquo;Bargain Bin with Push!&rdquo;</strong> does pretty much exactly what my app was going to do. It provides push notifications when apps you&rsquo;re interested in drop in price. Instead of raging against the dying of the light, I&rsquo;ve decided to just user their app and let mine go gently into that good night.</p>
<h2 id="the-good-news">The Good News</h2>
<p>All was not lost. I learned a ton along the way and decided to open-source the module I was using to fetch app information out of Apple&rsquo;s App Store.</p>
<h2 id="the-goods">The Goods</h2>
<p><a href="%22http://github.com/jerodsanto/app_store%22">The code is on GitHub, of course</a>. Clone, fork and branch to your heart&rsquo;s content. Hopefully somebody will find it useful.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Self-Scheduling Ruby Scripts</title>
      <link>https://jerodsanto.net/2009/08/self-scheduling-ruby-scripts/</link>
      <pubDate>Thu, 20 Aug 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/08/self-scheduling-ruby-scripts/</guid>
      
      
      <description><![CDATA[<p><a href="%22http://github.com/javan/whenever/%22">Whenever</a> is an awesome library that:</p>
<blockquote>
<p>&ldquo;provides a clean ruby syntax for defining messy cron jobs and running them Whenever.</p>
</blockquote>
<p><code>Whenever</code> has become very popular for use with Rails apps and there are plenty of tutorials on how to use it. This <a href="%22http://railscasts.com/episodes/164-cron-in-ruby%22">RailsCast</a> is a good place to get started if you&rsquo;re interested in that.</p>
<p>However, I haven&rsquo;t seen too many people writing about using the library outside of Rails (or other web frameworks).</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="%22http://github.com/javan/whenever/%22">Whenever</a> is an awesome library that:</p>
<blockquote>
<p>&ldquo;provides a clean ruby syntax for defining messy cron jobs and running them Whenever.</p>
</blockquote>
<p><code>Whenever</code> has become very popular for use with Rails apps and there are plenty of tutorials on how to use it. This <a href="%22http://railscasts.com/episodes/164-cron-in-ruby%22">RailsCast</a> is a good place to get started if you&rsquo;re interested in that.</p>
<p>However, I haven&rsquo;t seen too many people writing about using the library outside of Rails (or other web frameworks).</p>
<p>I have a lot of Ruby scripts running on different servers all scheduled via cron and it&rsquo;s quite easy to forget what script is scheduled when and how often. I decided to try using <code>Whenever</code> to create the cron jobs instead of creating them manually. First, I set out to just slap arbitrary Ruby code inside of an <code>every</code> block. You know, something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">every</span> <span class="ss">:hour</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="s2">&#34;this is just some ruby that will execute every hour&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Unfortunately, it doesn&rsquo;t work like that. If it did, that would own because then we could simply wrap all our scripts inside an <code>every</code> block and call it a day. Instead the library lets you define a few different kinds of tasks inside an <code>every</code> block.</p>
<ol>
<li>rake tasks</li>
<li>external commands</li>
<li>runner scripts (Rails)</li>
</ol>
<p>So, we can use the <code>command</code> method to execute our pre-existing scripts like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">every</span> <span class="mi">1</span><span class="o">.</span><span class="n">day</span><span class="p">,</span> <span class="ss">:at</span> <span class="o">=&gt;</span> <span class="s1">&#39;4:30 am&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">command</span> <span class="s2">&#34;/scripts/daily_backup.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>So that&rsquo;s cool, but now we have to create a separate file that houses our schedule definitions and manage it as well as the scripts we want to run. What would be really cool, I thought, would be to include the schedule definitions at the top of each script. The big win in this case is easily accessible &amp; portable schedule documentation.</p>
<p>One way of accomplishing this is to prepend all your Ruby scripts with a snippet similar to this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># filename: /scripts/weekly_backup.rb</span>
</span></span><span class="line"><span class="cl"><span class="n">every</span> <span class="ss">:sunday</span><span class="p">,</span> <span class="ss">:at</span> <span class="o">=&gt;</span> <span class="s1">&#39;12pm&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">command</span> <span class="s2">&#34;/scripts/weekly_backup.rb&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span> <span class="k">if</span> <span class="n">defined?</span><span class="p">(</span><span class="no">Whenever</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="k">unless</span> <span class="bp">__FILE__</span> <span class="o">==</span> <span class="vg">$PROGRAM_NAME</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="s2">&#34;this is the start of my backup script&#34;</span>
</span></span></code></pre></div><p>When this script is executed using the <code>whenever</code> command (which you use to actually generate and install the cron jobs), the first <code>every</code> block will be used and everything after the <code>return</code> line will be ignored. When this script is executed directly, the <code>every</code> block will be ignored and everything after the <code>return</code> line will be executed.</p>
<p>Writing the crontab with this script will look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">whenever -w -f /scripts/weekly_backup.rb
</span></span></span></code></pre></div><p>Which will install a cron job that looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">0 12 * * 0 /scripts/weekly_backup.rb
</span></span></span></code></pre></div><p>The major drawback to this method is we have to hard code the full path to the script instead of using the <code>__FILE__</code> variable, which hurts portability. This is because <code>Whenever</code> <code>evals</code> the content of the file read in and in this case <code>__FILE__</code> is useless. There is access to the calling file path via <code>Whenever::CommandLine.default_identifier</code> but this is currently a protected method.</p>
<p>This is just my first attempt at embedding scheduling information inside the script being scheduled, so there are probably easier/better ways of getting this done. Know any?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Amending Git Commits</title>
      <link>https://jerodsanto.net/2009/08/amending-git-commits/</link>
      <pubDate>Sun, 16 Aug 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/08/amending-git-commits/</guid>
      
      
      <description><![CDATA[<p>I recently learned that you can fix you&rsquo;re previous commit (modify commit message, add more files, etc.) quite easily with git. An example:</p>
<p>You&rsquo;re in early stages of developing a Rails app and you decide that you want to go back and add some indexes to your tables. No need to create a new migration at this point, just add the indexes to the old migrations and run them again. After making the changes, you create a commit</p>]]></description>
      
      <content:encoded><![CDATA[<p>I recently learned that you can fix you&rsquo;re previous commit (modify commit message, add more files, etc.) quite easily with git. An example:</p>
<p>You&rsquo;re in early stages of developing a Rails app and you decide that you want to go back and add some indexes to your tables. No need to create a new migration at this point, just add the indexes to the old migrations and run them again. After making the changes, you create a commit</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">git commit -a -m &#34;Added missing indexes to tables&#34;
</span></span></span></code></pre></div><p>Next you re-run all your migrations to get the indexes in there.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">rake db:migrate:reset
</span></span></span></code></pre></div><p>At this point, you check git status and remember that now your schema file has changed. This probably should have been included in the last commit! Piece of cake.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">git commit db/schema.rb --amend
</span></span></span></code></pre></div><p>You&rsquo;ll be prompted to optionally change the commit message.</p>
<p>At this point git status will tell you that your working directory is clean and the changes to your schema were tracked in the same commit as the migration changes.</p>
<p>Butter.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Mini Book Review: Ruby Best Practices</title>
      <link>https://jerodsanto.net/2009/08/mini-book-review-ruby-best-practices/</link>
      <pubDate>Sat, 01 Aug 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/08/mini-book-review-ruby-best-practices/</guid>
      
      
      <description><![CDATA[<p>I just finished reading Gregory Brown&rsquo;s &ldquo;<strong>Ruby Best Practices</strong>&rdquo; (RBP). I could probably write a book about this book, but I hate long book reviews so I&rsquo;ll try to keep this short.</p>
<h2 id="in-summary">In Summary</h2>
<p>If you are an intermediate-to-expert Ruby programmer you should absolutely read this book. Beginners may want to start elsewhere and work their way up. <a href="#wheretobuy">Where To Get It</a></p>
<h2 id="in-detail">In Detail</h2>
<p>The beauty and power of Ruby is its dynamism which leads to freedom. The problem with freedom is it means <a href="https://en.wikipedia.org/wiki/There%27s_more_than_one_way_to_do_it">TMTOWTDI</a>. The problem with <a href="https://en.wikipedia.org/wiki/There%27s_more_than_one_way_to_do_it">TMTOWTDI</a> is best summarized by me, in a quote of myself:</p>]]></description>
      
      <content:encoded><![CDATA[<p>I just finished reading Gregory Brown&rsquo;s &ldquo;<strong>Ruby Best Practices</strong>&rdquo; (RBP). I could probably write a book about this book, but I hate long book reviews so I&rsquo;ll try to keep this short.</p>
<h2 id="in-summary">In Summary</h2>
<p>If you are an intermediate-to-expert Ruby programmer you should absolutely read this book. Beginners may want to start elsewhere and work their way up. <a href="#wheretobuy">Where To Get It</a></p>
<h2 id="in-detail">In Detail</h2>
<p>The beauty and power of Ruby is its dynamism which leads to freedom. The problem with freedom is it means <a href="https://en.wikipedia.org/wiki/There%27s_more_than_one_way_to_do_it">TMTOWTDI</a>. The problem with <a href="https://en.wikipedia.org/wiki/There%27s_more_than_one_way_to_do_it">TMTOWTDI</a> is best summarized by me, in a quote of myself:</p>
<blockquote>
<p>&ldquo;There is more than one way to do it but most of them are really, really bad.&rdquo;</p>
</blockquote>
<p>Thankfully, we can combat these bad coding methods by resorting to helpful idioms and best practices, and Ruby has these in spades. The purpose of <strong>RBP</strong> is stated plainly on the front cover:</p>
<blockquote>
<p>&ldquo;Increase Your Productivity &ndash; Write Better Code&rdquo;</p>
</blockquote>
<p>With that in mind, here is a breakdown of what it offers:</p>
<p>The first thing I noticed when reading <strong>RBP</strong> is that it uses real-world code samples. None of that &ldquo;let&rsquo;s make a tic-tac-toe game&rdquo; type of stuff. Gregory uses a couple of his own projects (Prawn &amp; Ruport) as well as other popular libraries (Haml, flexmock, XML Builder, Gibberish, faker). This is beyond awesome.</p>
<p>He also steps through a lot of code using IRB, which means you can follow right along in your favorite shell. Gregory highly recommends you get your hands dirty with the code he presents and I agree with him. However, I also like to read physical books in places not my computer, since the opportunity so rarely presents itself.</p>
<p>The book starts, aptly, with a chapter on testing. The following two chapters are (for me) the highlights:</p>
<p><em><strong>Designing Beautiful APIs</strong></em> and <em><strong>Mastering the Dynamic Toolkit</strong></em>.</p>
<p>The value found in these two sections alone cover the cost of the entire book. A few of the topics discussed include: flexible argument handling, code blocks, implementing per-object behavior, building classes and modules programatically and registering hooks and callbacks. Gregory released a free section of <em><strong>Mastering the Dynamic Toolkit</strong></em> so you don&rsquo;t have to take my word for it, <a href="https://cdn.oreilly.com/books/9780596523008/Mastering_the_Dynamic_Toolkit.xml.pdf">have a taste for yourself.</a></p>
<p>I need to wrap this up or I&rsquo;ll be forced to remove the &ldquo;<strong>Mini</strong>&rdquo; from the post title. You&rsquo;ll also find sections on File &amp; Text processing, functional programming, debugging, project maintenance (much of which is obsolete if you use the wonderful <a href="https://github.com/technicalpickles/jeweler/">Jeweler gem</a>), and internationalization.</p>
<p>Tips, tricks and suggestions abound. Even expert-level Rubyists should learn something. <strong>RBP</strong> left me wanting more of Gregory&rsquo;s teaching. He really does a good job of explaining concepts and walking through code. Thankfully, he started up a <a href="https://blog.rubybestpractices.com/">Ruby Best Practices blog</a> with more content!</p>
<h3 id="wheretobuy">Where to Buy</h3>
<p><strong>RBP</strong> is an O&rsquo;Reilly publication and they have it on sale at their web site for <strong>$34.99</strong>.</p>
<p>However, I don&rsquo;t know why you&rsquo;d do that because Amazon has it for <strong>$23.10</strong> and eligible for free shipping with Amazon Prime. Booyah!</p>
<p>If you go that route, use <a href="http://www.amazon.com/gp/product/0596523009?ie=UTF8&amp;tag=standadeviat-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0596523009">this link</a> to pitch me a little something for the effort, or just buy directly via <a href="http://www.amazon.com/Ruby-Best-Practices-Gregory-Brown/dp/0596523009/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1249140448&amp;sr=8-1">this link</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>WordPress Console Update</title>
      <link>https://jerodsanto.net/2009/07/wordpress-console-update/</link>
      <pubDate>Thu, 23 Jul 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/07/wordpress-console-update/</guid>
      
      
      <description><![CDATA[<p>It&rsquo;s been awhile since I posted about my <a href="http://github.com/jerodsanto/wordpress-console" rel="external">open-source</a> WordPress Console (WPC) plugin, and enough has happened since I released it that I thought it deserved a little mention.</p>
<h2 id="whats-new">What&rsquo;s New</h2>
<ol>
<li>Basic Security</li>
<p>Thanks to <a href="http://blog.apokalyptik.com/" rel="external">Apokalyptik</a>, the back-end PHP scripts now require a shared secret from the console before executing any code. As he so eloquently described it:</p>
<blockquote>As is the plugin is negligently insecure (but outstandingly cool and useful and I want this plugin to be installable, thus the patch)</blockquote>
<p>Even though the increased security is a huge improvement from what we had before, I still wouldn&rsquo;t run the plugin on production servers.</p>]]></description>
      
      <content:encoded><![CDATA[<p>It&rsquo;s been awhile since I posted about my <a href="http://github.com/jerodsanto/wordpress-console" rel="external">open-source</a> WordPress Console (WPC) plugin, and enough has happened since I released it that I thought it deserved a little mention.</p>
<h2 id="whats-new">What&rsquo;s New</h2>
<ol>
<li>Basic Security</li>
<p>Thanks to <a href="http://blog.apokalyptik.com/" rel="external">Apokalyptik</a>, the back-end PHP scripts now require a shared secret from the console before executing any code. As he so eloquently described it:</p>
<blockquote>As is the plugin is negligently insecure (but outstandingly cool and useful and I want this plugin to be installable, thus the patch)</blockquote>
<p>Even though the increased security is a huge improvement from what we had before, I still wouldn&rsquo;t run the plugin on production servers.</p>
<li>Tab-Completion</li>
<p>This is the biggest functional improvement to WPC by far. It was a feature that I wanted to release the plugin with initially, but it didn&rsquo;t make the cut because I wanted to release early. The best thing about tab-completion is that it allows you to explore the PHP &amp; WP environments in a very fulfilling way. If you haven&rsquo;t tried the plugin with this feature, please give it a go.</p>
<li>Small Things</li>
<p>WPC now handles command history with more grace. Using the up-arrow puts the cursor at the end of input, you can&rsquo;t walk off the end of the history buffer, and a few other improvements to the code quality.</p>
</ol>
<h2 id="some-press">Some Press</h2>
<p>Thanks to <a href="http://pixelgraphics.us/" rel="external">Doug Neiner</a> for giving WPC a solid review on the <a href="http://fuelyourcoding.com/plugin-review-wordpress-console" rel="external">Fuel Your Coding blog</a>, and for contributing to the project.</p>
<h2 id="whats-next">What&rsquo;s Next</h2>
<p>I&rsquo;m not really sure.</p>
<p>I&rsquo;ve considered adding in-console documentation for PHP &amp; WP functions, but not sure if people would use it much. I also have a command-line version of the console which I could spit shine and include with the plugin, but that might not be too attractive either. Maybe the plugin is as good as done. Any ideas or suggestions?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Ad Hoc Rails Console Logging</title>
      <link>https://jerodsanto.net/2009/07/ad-hoc-rails-console-logging/</link>
      <pubDate>Sat, 18 Jul 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/07/ad-hoc-rails-console-logging/</guid>
      
      
      <description><![CDATA[<p>I recently found a couple of cool <a href="%22http://toolmantim.com/articles/logging_rails_activity_in_script_console%22">old</a> <a href="%22http://toolmantim.com/articles/system_wide_script_console_logging%22">tricks</a> for logging Rails activity directly to the console, but these solutions are pretty static once you&rsquo;re inside a console session. In my experience, sometimes logging to the console is cool and most of the time it isn&rsquo;t.</p>
<p>If you want to be able to toggle your development log between the default Rails logger and the console, just add this method to your <code>~/.irbrc</code>:</p>]]></description>
      
      <content:encoded><![CDATA[<p>I recently found a couple of cool <a href="%22http://toolmantim.com/articles/logging_rails_activity_in_script_console%22">old</a> <a href="%22http://toolmantim.com/articles/system_wide_script_console_logging%22">tricks</a> for logging Rails activity directly to the console, but these solutions are pretty static once you&rsquo;re inside a console session. In my experience, sometimes logging to the console is cool and most of the time it isn&rsquo;t.</p>
<p>If you want to be able to toggle your development log between the default Rails logger and the console, just add this method to your <code>~/.irbrc</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rails_log_to_console</span><span class="p">(</span><span class="n">toggle</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">logger</span> <span class="o">=</span> <span class="n">toggle</span> <span class="o">==</span> <span class="kp">true</span> <span class="p">?</span> <span class="no">Logger</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">STDOUT</span><span class="p">)</span> <span class="p">:</span> <span class="no">RAILS_DEFAULT_LOGGER</span>
</span></span><span class="line"><span class="cl">  <span class="n">reload!</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Now, you can start logging to the console at any time by doing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">&gt;</span>&gt; rails_log_to_console <span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="go">Reloading...
</span></span></span><span class="line"><span class="cl"><span class="go">=&gt; true
</span></span></span></code></pre></div><p>And turn it back off as simply as:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">&gt;</span>&gt; rails_log_to_console <span class="nb">false</span>
</span></span><span class="line"><span class="cl"><span class="go">Reloading...
</span></span></span><span class="line"><span class="cl"><span class="go">=&gt; true
</span></span></span></code></pre></div><p>Really, passing any argument <em>except</em> true to the method will switch the logging back to the default.</p>
<p>Are there any better ways to achieve this (like without having to call <code>reload!</code>)? If you know of one, holler back in the comments.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Sass Never Forgets</title>
      <link>https://jerodsanto.net/2009/07/sass-never-forgets/</link>
      <pubDate>Thu, 09 Jul 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/07/sass-never-forgets/</guid>
      
      
      <description><![CDATA[<p>Some CSS techniques require me to Google about like a chicken with its head cut off. One such technique is placing a div in the dead center of a page (vertically and horizontally). Since I&rsquo;m using <a href="http://sass-lang.com">Sass</a> pretty much exclusively these days, I decided to define this technique as a mixin so I don&rsquo;t have to look it up anymore.</p>
<p>Hopefully this can be of use to others. YMMV:</p>
<p>The mixin definition:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Some CSS techniques require me to Google about like a chicken with its head cut off. One such technique is placing a div in the dead center of a page (vertically and horizontally). Since I&rsquo;m using <a href="http://sass-lang.com">Sass</a> pretty much exclusively these days, I decided to define this technique as a mixin so I don&rsquo;t have to look it up anymore.</p>
<p>Hopefully this can be of use to others. YMMV:</p>
<p>The mixin definition:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sass" data-lang="sass"><span class="line"><span class="cl"><span class="nf">=dead-center</span><span class="p">(</span><span class="nv">!height</span><span class="o">,!</span><span class="no">width</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">  :</span><span class="no">height</span><span class="o">=</span> <span class="nv">!height</span>
</span></span><span class="line"><span class="cl"><span class="o">  :</span><span class="no">margin-top</span><span class="o">=</span> <span class="nf">-</span><span class="p">(</span><span class="nv">!height</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nd">:top</span> <span class="nt">50</span><span class="err">%</span>
</span></span><span class="line"><span class="cl"><span class="o">  :</span><span class="no">width</span><span class="o">=</span> <span class="nv">!width</span>
</span></span><span class="line"><span class="cl"><span class="o">  :</span><span class="no">margin-left</span><span class="o">=</span> <span class="nf">-</span><span class="p">(</span><span class="nv">!width</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nd">:left</span> <span class="nt">50</span><span class="err">%</span>
</span></span><span class="line"><span class="cl">  <span class="nd">:position</span> <span class="nt">absolute</span>
</span></span><span class="line"><span class="cl">  <span class="nd">:text-align</span> <span class="nt">center</span>
</span></span></code></pre></div><p>To use it on an element:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">#</span><span class="nn">centered</span>
</span></span><span class="line"><span class="cl">  <span class="o">+</span><span class="nt">dead-center</span><span class="o">(</span><span class="nt">200px</span><span class="o">,</span><span class="nt">500px</span><span class="o">)</span>
</span></span></code></pre></div><p>Now I&rsquo;ll never forget how. Thanks Sass!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>On WordPress Plugin Release Notes</title>
      <link>https://jerodsanto.net/2009/07/on-wordpress-plugin-release-notes/</link>
      <pubDate>Fri, 03 Jul 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/07/on-wordpress-plugin-release-notes/</guid>
      
      
      <description><![CDATA[<p>Plugin upgrade notifications and one-click installs are great for both developers and users alike, but there is one thing about them that I find irritating as a user, and easily avoidable as a developer: <strong>displaying reasons to upgrade</strong>.</p>
<p>I love, love, love new software and usually can&rsquo;t wait to install the latest shiny update. However, before I put myself at risk and click the <strong>upgrade automatically</strong> button, I&rsquo;d like to know what the new release is going to do differently than what I already have. I&rsquo;m pretty sure I&rsquo;m not alone in this desire, as the WordPress developers have implemented a nice <strong>View Version [x] Details</strong> button right there in the plugin admin page. You know, this one:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Plugin upgrade notifications and one-click installs are great for both developers and users alike, but there is one thing about them that I find irritating as a user, and easily avoidable as a developer: <strong>displaying reasons to upgrade</strong>.</p>
<p>I love, love, love new software and usually can&rsquo;t wait to install the latest shiny update. However, before I put myself at risk and click the <strong>upgrade automatically</strong> button, I&rsquo;d like to know what the new release is going to do differently than what I already have. I&rsquo;m pretty sure I&rsquo;m not alone in this desire, as the WordPress developers have implemented a nice <strong>View Version [x] Details</strong> button right there in the plugin admin page. You know, this one:</p>
<img class="aligncenter size-full wp-image-506" title="update_notification" src="https://jerodsanto.net/wp-content/uploads/2009/07/update_notification.png" height="89" alt="update_notification" width="640" />
<p>Awesome!! Well, kinda.</p>
<p>The problem is that most plugin developers don&rsquo;t include release notes with each version, rendering the awesomeness of no effect. For instance, I have <strong>no idea</strong> what I&rsquo;m getting by upgrading the <a href="http://wordpress.org/extend/plugins/google-sitemap-generator/">Google XML Sitemaps</a> plugin. This is what you see after clicking the &ldquo;View Version 3.1.4 Details&rdquo; button:</p>
<img class="aligncenter size-full wp-image-508" title="update_no_info" src="https://jerodsanto.net/wp-content/uploads/2009/07/update_no_info.png" height="393" alt="update_no_info" width="640" />
<p>The closest thing offered there is a link to the Changelog, which opens in a new tab/window. Totally lame. Check out the <a href="http://www.arnebrachhold.de/projects/wordpress-plugins/google-xml-sitemaps-generator/changelog/">Changelog</a>. It is filled with totally useful information. Make it accessible!</p>
<p>Slap your users in the face with reasons to upgrade!</p>
<p>Contrast that with what is displayed to potential upgraders (not sure if that&rsquo;s even a word) of my <a href="http://wordpress.org/extend/plugins/wordpress-console/">WordPress Console</a> plugin:</p>
<img class="aligncenter size-full wp-image-510" title="update_info" src="https://jerodsanto.net/wp-content/uploads/2009/07/update_info.png" height="405" alt="update_info" width="640" />
<p>Two things to notice:</p>
<ol>
<li>WordPress provides a special tab called <strong>Changelog</strong> that will be loaded right inside this window by adding a <strong>Changelog</strong> section to your plugin&rsquo;s readme.txt. USE IT!! (<a href="http://wordpress.org/extend/plugins/about/readme.txt">more readme info</a>)</li>
<li>For some people, even clicking the Changelog tab is asking too much of them. Including what&rsquo;s new since the last release of your plugin right in the description is a big win for everybody involved.</li>
</ol>
<p>There are many plugins that handle this just fine, but many more that do not. My hope is that other WordPress plugin developers will adopt this practice, to the benefit of their users and the community as a whole.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>jQuery Tools 101: Nested Tabs</title>
      <link>https://jerodsanto.net/2009/07/jquery-tools-101-nested-tabs/</link>
      <pubDate>Wed, 01 Jul 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/07/jquery-tools-101-nested-tabs/</guid>
      
      
      <description><![CDATA[<p><a href="http://flowplayer.org/tools/index.html">jQuery Tools</a> is the new kid on the block when it comes to jQuery-based user interface libraries. What it offers is a solid foundation of widgets at an extremely small file size (5.8 KB minified).</p>
<p>I decided to ditch <a href="http://jqueryui.com">jQuery UI</a> on a recent project when I couldn&rsquo;t get tabs &amp; accordions to play nice together (also, their site was down when I needed it so I took that as a sign). This was a great opportunity to try jQuery Tools and I have been very impressed thus far.</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="http://flowplayer.org/tools/index.html">jQuery Tools</a> is the new kid on the block when it comes to jQuery-based user interface libraries. What it offers is a solid foundation of widgets at an extremely small file size (5.8 KB minified).</p>
<p>I decided to ditch <a href="http://jqueryui.com">jQuery UI</a> on a recent project when I couldn&rsquo;t get tabs &amp; accordions to play nice together (also, their site was down when I needed it so I took that as a sign). This was a great opportunity to try jQuery Tools and I have been very impressed thus far.</p>
<p>To use the minimal tabs interface you can simply <a href="http://flowplayer.org/tools/demos/tabs/index.html">follow the instructions on their site</a>, but if you want to nest tabs inside one another you&rsquo;ll need to change things slightly. I&rsquo;ll demonstrate below:</p>
<h2 id="the-mark-up">the Mark Up</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ul</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;tabs main&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#1&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;main&#34;</span><span class="p">&gt;</span>Main Tab 1<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#1&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;main&#34;</span><span class="p">&gt;</span>Main Tab 2<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;panes main&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>this is main tab 1 content. it includes another set of tabs
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">ul</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;tabs nested&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#2&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;nested&#34;</span><span class="p">&gt;</span>Nested Tab 1<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#2&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;nested&#34;</span><span class="p">&gt;</span>Nested Tab 2<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;panes nested&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>this is my nested tab 1 content<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>this is my nested tab 2 content<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>this is my main tab 2 content<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>The key in this sequence is that the seemingly arbitrary href attribute on the tab anchors has different content for the main tabs (#1) than the nested tabs (#2). This is required or your nested tabs will keep returning to the main tabs when you click on a tab link.</p>
<h2 id="the-javascript">the JavaScript</h2>
<p>To configure the nested tabs, you can define the JavaScript as simply as this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;ul.main.tabs&#34;</span><span class="p">).</span><span class="nx">tabs</span><span class="p">(</span><span class="s2">&#34;div.main.panes &gt; div&#34;</span><span class="p">,</span> <span class="p">{</span><span class="nx">tabs</span><span class="o">:</span> <span class="s1">&#39;a.main&#39;</span><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;ul.nested.tabs&#34;</span><span class="p">).</span><span class="nx">tabs</span><span class="p">(</span><span class="s2">&#34;div.nested.panes &gt; div&#34;</span><span class="p">,</span> <span class="p">{</span><span class="nx">tabs</span><span class="o">:</span> <span class="s1">&#39;a.nested&#39;</span><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>the <code>tabs()</code> function is called on a jQuery selector that matches the unordered list and it takes as its first argument a selector for where to find the tab&rsquo;s content panes. It&rsquo;s second argument is a single option that tells <code>tabs()</code> which elements to use as links for the tabs. The default is <code>a</code>, but to differentiate the main tabs from the nested tabs, it needs to be passed explicitly with the correct class for the anchor.</p>
<h2 id="the-style">the Style</h2>
<p>Of course, to make these look cool you&rsquo;ll need some nice CSS applied to the elements at play. I&rsquo;ll leave that to you, but you can get a decent start from the <a href="http://flowplayer.org/tools/tabs.html">demo page</a> for the library.</p>
<p>Enjoy!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Introducing the WordPress Console</title>
      <link>https://jerodsanto.net/2009/06/introducing-the-wordpress-console/</link>
      <pubDate>Tue, 23 Jun 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/06/introducing-the-wordpress-console/</guid>
      
      
      <description><![CDATA[<p>One killer feature of Ruby on Rails (for me) is <strong>script/console</strong>. Being able to interact with your code and data inside the full Rails environment is a powerful tool for development. Some days I practically live there, and if I get carried away, I do a lot of my testing there too (bad, I know).</p>
<p>I love Ruby and Rails, but being a contract developer means I go where the money is and recently that has been in WordPress plugin development. I enjoy developing for WordPress, but I&rsquo;ve been spoiled by Rails and I often long for an interactive console for WordPress.</p>]]></description>
      
      <content:encoded><![CDATA[<p>One killer feature of Ruby on Rails (for me) is <strong>script/console</strong>. Being able to interact with your code and data inside the full Rails environment is a powerful tool for development. Some days I practically live there, and if I get carried away, I do a lot of my testing there too (bad, I know).</p>
<p>I love Ruby and Rails, but being a contract developer means I go where the money is and recently that has been in WordPress plugin development. I enjoy developing for WordPress, but I&rsquo;ve been spoiled by Rails and I often long for an interactive console for WordPress.</p>
<p>As a result, I&rsquo;ve been developing (and using) a Wordpress plugin built for Wordpress developers. It provides an in-browser console where you can &ldquo;play&rdquo; with the code you&rsquo;re working on.</p>
<p>If a picture is worth 1,000 words, this screencast will be worth at least a bazillion of &rsquo;em:
~
<object height="368" width="640"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=5300607&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" /><embed allowfullscreen="true" src="http://vimeo.com/moogaloop.swf?clip_id=5300607&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" allowscriptaccess="always" type="application/x-shockwave-flash" height="368" width="640"></embed></object></p>
<p>So there you have it. It is currently version <strong>0.1.0</strong> (very young) and I would love some help to make it even more awesome. The source code is hosted on <a href="http://github.com/jerodsanto/wordpress-console">GitHub</a>, so please fork away and I&rsquo;d be happy to merge in your changes.</p>
<p>Or, simply go <a href="http://wordpress.org/extend/plugins/wordpress-console/">download</a> the plugin and try it.</p>
<p><em><strong>NOTE</strong></em>
In developing this plugin, I leaned on a few other open-source projects for inspiration (and in some cases, code). They are:</p>
<ul>
<li><a href="http://pear.php.net/package/PHP_Shell/">PHP_Shell</a></li>
<li><a href="http://code.google.com/p/htsh/">htsh</a></li>
</ul>
<p>I thank the authors for opening their source.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Sniff Your iPhone&#39;s Network Traffic</title>
      <link>https://jerodsanto.net/2009/06/sniff-your-iphones-network-traffic/</link>
      <pubDate>Sat, 13 Jun 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/06/sniff-your-iphones-network-traffic/</guid>
      
      
      <description><![CDATA[<p>Ever wanted (or needed) to see your iPhone&rsquo;s network traffic? All you need is a wireless LAN and the cross-platform proxy application, <a href="http://www.parosproxy.org/index.shtml">Paros.</a> There are other proxy server&rsquo;s that can be used, but Paros was built for web application security assessments, so it provides an intimate hook into the HTTP request/response flow. Let&rsquo;s get started!</p>
<h2 id="1-download-and-install-paros">1) Download and Install Paros</h2>
<p>Grab the download from SourceForge (<a href="https://sourceforge.net/projects/paros/files/latest/download">direct link</a>).</p>
<h2 id="2-configure-paros">2) Configure Paros</h2>
<p>Once installed, launch Paros and find the configuration options (on OS X they are under Tools -&gt; Options). Paros is configured by default to listen on localhost only, but we are going to route our iPhone&rsquo;s traffic through Paros, so we need to set it to listen on the IP address of the interface connected to the same LAN as the iPhone.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Ever wanted (or needed) to see your iPhone&rsquo;s network traffic? All you need is a wireless LAN and the cross-platform proxy application, <a href="http://www.parosproxy.org/index.shtml">Paros.</a> There are other proxy server&rsquo;s that can be used, but Paros was built for web application security assessments, so it provides an intimate hook into the HTTP request/response flow. Let&rsquo;s get started!</p>
<h2 id="1-download-and-install-paros">1) Download and Install Paros</h2>
<p>Grab the download from SourceForge (<a href="https://sourceforge.net/projects/paros/files/latest/download">direct link</a>).</p>
<h2 id="2-configure-paros">2) Configure Paros</h2>
<p>Once installed, launch Paros and find the configuration options (on OS X they are under Tools -&gt; Options). Paros is configured by default to listen on localhost only, but we are going to route our iPhone&rsquo;s traffic through Paros, so we need to set it to listen on the IP address of the interface connected to the same LAN as the iPhone.</p>
<p>My LAN&rsquo;s network is 1.1.1.0/16, so I&rsquo;ll configure the Local Proxy address accordingly:</p>
<img src="https://jerodsanto.net/wp-content/uploads/2009/06/paros_config.png" height="246" alt="paros_config" width="540" />
<p>That should be the only setting that we need to fuss with. Paros is all set and listening on port 8080, let&rsquo;s configure the iPhone to route its traffic through our proxy!</p>
<h2 id="3-configure-iphone">3) Configure iPhone</h2>
<p>On the iPhone, open the &ldquo;<strong>Settings</strong>&rdquo; app and navigate to the Wi-Fi page. Once there, edit the settings for the wireless network you are currently connected to (this needs to be the same network where your proxy is running). To do this, click the little blue arrow on the right side of the screen.</p>
<img src="https://jerodsanto.net/wp-content/uploads/2009/06/config_wifi.png" height="480" alt="config_wifi" width="322" />
<p>Now, scroll all the way to the bottom of the settings page and change the <strong>&ldquo;HTTP Proxy&rdquo;</strong> setting to manual. Enter the IP address and port number of your Paros Proxy.</p>
<img src="https://jerodsanto.net/wp-content/uploads/2009/06/config_proxy.png" height="480" alt="config_proxy" width="320" />
<p>All set! Now all web traffic to and from the iPhone is routed through Paros. Let&rsquo;s go see what we can see.</p>
<h2 id="4-using-paros">4) Using Paros</h2>
<p>The main section of Paros is the &ldquo;<strong>Request/Response/Trap</strong>.&rdquo; As the iPhone talks through Paros to Internet sites, it will display the iPhone&rsquo;s request and the server&rsquo;s response. The &ldquo;<strong>trap</strong>&rdquo; functionality allows you to stop either the request or the response and view/modify it before sending it along to the recipient. Trapping is very cool, and why Paros is used for security auditing, but for our purposes we just want to see what is going on, so I won&rsquo;t explain it any further.</p>
<p>For now, let&rsquo;s see what happens when we fire up my iPhone&rsquo;s &ldquo;<strong>App Store</strong>&rdquo; app:</p>
<p>In the bottom section of the screen is the history viewer. There we can see that my iPhone made 4 requests to different servers ( 3 GETs and 1 POST):</p>
<img src="https://jerodsanto.net/wp-content/uploads/2009/06/history.png" height="67" alt="history" width="540" />
<p>Highlighting the first GET in the history list shows its details. The iPhone&rsquo;s HTTP request header looked like this:</p>
<img src="https://jerodsanto.net/wp-content/uploads/2009/06/request.png" height="337" alt="request" width="540" />
<p>One noteworthy tidbit is that the iPhone is sending a custom header (X-Apple-Connection-Type) which tells the server that it is connected to WiFi. Next, let&rsquo;s take a look at the server&rsquo;s response:</p>
<img src="https://jerodsanto.net/wp-content/uploads/2009/06/response.png" height="408" alt="response" width="540" />
<p>Notice that in the response we see both the headers that the server returned AND the response data itself, in this case an xml plist file.</p>
<p>Sniffing traffic like this can help you understand how different iPhone apps work behind the scenes or it can help debug interaction for an app that you&rsquo;re writing. Hope this helps you get started!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>PHP5 with readline support on OS X</title>
      <link>https://jerodsanto.net/2009/06/php5-with-readline-support-on-os-x/</link>
      <pubDate>Sat, 06 Jun 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/06/php5-with-readline-support-on-os-x/</guid>
      
      
      <description><![CDATA[<p>OS X ships with PHP5 installed but it does not have <code>readline()</code> support compiled in. Anybody using PHP from the command-line will want this, as it allows handy things such as tab completion and scrolling through command history using the up arrow.</p>
<p>Thankfully, <a href="http://www.macports.org/">MacPorts</a> has a readline variant that can be easily installed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">sudo port install php5 +readline
</span></span></span></code></pre></div><p>If you execute the command above, apache2 will come along for the ride because it&rsquo;s a default variant for the PHP5 port. If you don&rsquo;t want apache2 (OS X ships with apache2 anyways), modify the command to look like this:</p>]]></description>
      
      <content:encoded><![CDATA[<p>OS X ships with PHP5 installed but it does not have <code>readline()</code> support compiled in. Anybody using PHP from the command-line will want this, as it allows handy things such as tab completion and scrolling through command history using the up arrow.</p>
<p>Thankfully, <a href="http://www.macports.org/">MacPorts</a> has a readline variant that can be easily installed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">sudo port install php5 +readline
</span></span></span></code></pre></div><p>If you execute the command above, apache2 will come along for the ride because it&rsquo;s a default variant for the PHP5 port. If you don&rsquo;t want apache2 (OS X ships with apache2 anyways), modify the command to look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">sudo port install php5 -apache2 +readline
</span></span></span></code></pre></div><p>Not sure if your PHP install has readline support? Execute this one-liner to find out:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span> <span class="k">echo</span> <span class="nx">function_exists</span><span class="p">(</span><span class="s1">&#39;readline&#39;</span><span class="p">)</span> <span class="o">?</span> <span class="s2">&#34;yes</span><span class="se">\n</span><span class="s2">&#34;</span> <span class="o">:</span> <span class="s2">&#34;no</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">;</span> <span class="cp">?&gt;</span><span class="err">
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Let Capistrano Compile Ruby 1.9 For You</title>
      <link>https://jerodsanto.net/2009/06/let-capistrano-compile-ruby-1.9-for-you/</link>
      <pubDate>Tue, 02 Jun 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/06/let-capistrano-compile-ruby-1.9-for-you/</guid>
      
      
      <description><![CDATA[<p>A Capistrano task to install Ruby 1.9.1 to <code>/opt/ruby-1.9.1</code> on Debian:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">APTGET</span> <span class="o">=</span> <span class="s2">&#34;apt-get install -qqy&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">RUBY19</span> <span class="o">=</span> <span class="s2">&#34;ruby-1.9.1-p129&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">SRC</span>    <span class="o">=</span> <span class="s2">&#34;/usr/local/src&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">WGET</span>   <span class="o">=</span> <span class="s2">&#34;wget -q&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">namespace</span> <span class="ss">:ruby</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">desc</span> <span class="s1">&#39;download and compile Ruby 1.9&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">task</span> <span class="ss">:install_19</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">deps</span>    <span class="o">=</span> <span class="sx">%w&#39;zlib1g-dev libopenssl-ruby1.9&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">version</span> <span class="o">=</span> <span class="no">RUBY19</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/-p\d+$/</span><span class="p">,</span><span class="s2">&#34;&#34;</span><span class="p">)</span> <span class="c1"># remove patch level</span>
</span></span><span class="line"><span class="cl">    <span class="n">run</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">APTGET</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">deps</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">cmd</span> <span class="o">=</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;cd </span><span class="si">#{</span><span class="no">SRC</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;</span><span class="si">#{</span><span class="no">WGET</span><span class="si">}</span><span class="s2"> ftp://ftp.ruby-lang.org/pub/ruby/1.9/</span><span class="si">#{</span><span class="no">RUBY19</span><span class="si">}</span><span class="s2">.tar.gz&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;tar zxvf </span><span class="si">#{</span><span class="no">RUBY19</span><span class="si">}</span><span class="s2">.tar.gz&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;cd </span><span class="si">#{</span><span class="no">RUBY19</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;./configure --prefix=/opt/</span><span class="si">#{</span><span class="n">version</span><span class="si">}</span><span class="s2"> --enable-shared&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;make&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;make install&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34; &amp;&amp; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">run</span> <span class="n">cmd</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>With this task, you can quickly upgrade all your Debian machines to Ruby 1.9.1 without having to go through the process each time. Just:</p>]]></description>
      
      <content:encoded><![CDATA[<p>A Capistrano task to install Ruby 1.9.1 to <code>/opt/ruby-1.9.1</code> on Debian:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">APTGET</span> <span class="o">=</span> <span class="s2">&#34;apt-get install -qqy&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">RUBY19</span> <span class="o">=</span> <span class="s2">&#34;ruby-1.9.1-p129&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">SRC</span>    <span class="o">=</span> <span class="s2">&#34;/usr/local/src&#34;</span>
</span></span><span class="line"><span class="cl"><span class="no">WGET</span>   <span class="o">=</span> <span class="s2">&#34;wget -q&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">namespace</span> <span class="ss">:ruby</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">desc</span> <span class="s1">&#39;download and compile Ruby 1.9&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">task</span> <span class="ss">:install_19</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">deps</span>    <span class="o">=</span> <span class="sx">%w&#39;zlib1g-dev libopenssl-ruby1.9&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">version</span> <span class="o">=</span> <span class="no">RUBY19</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/-p\d+$/</span><span class="p">,</span><span class="s2">&#34;&#34;</span><span class="p">)</span> <span class="c1"># remove patch level</span>
</span></span><span class="line"><span class="cl">    <span class="n">run</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">APTGET</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">deps</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">cmd</span> <span class="o">=</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;cd </span><span class="si">#{</span><span class="no">SRC</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;</span><span class="si">#{</span><span class="no">WGET</span><span class="si">}</span><span class="s2"> ftp://ftp.ruby-lang.org/pub/ruby/1.9/</span><span class="si">#{</span><span class="no">RUBY19</span><span class="si">}</span><span class="s2">.tar.gz&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;tar zxvf </span><span class="si">#{</span><span class="no">RUBY19</span><span class="si">}</span><span class="s2">.tar.gz&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;cd </span><span class="si">#{</span><span class="no">RUBY19</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;./configure --prefix=/opt/</span><span class="si">#{</span><span class="n">version</span><span class="si">}</span><span class="s2"> --enable-shared&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;make&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;make install&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34; &amp;&amp; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">run</span> <span class="n">cmd</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>With this task, you can quickly upgrade all your Debian machines to Ruby 1.9.1 without having to go through the process each time. Just:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">cap ruby:install_19
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Dead Simple Rails Deployment</title>
      <link>https://jerodsanto.net/2009/05/dead-simple-rails-deployment/</link>
      <pubDate>Sun, 31 May 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/05/dead-simple-rails-deployment/</guid>
      
      
      <description><![CDATA[<p>Deploying a Rails app used to suck. Reverse proxies, Mongrel clusters, Monit, etc. <a href="http://www.capify.org/">Capistrano</a> helped out a lot (once you set it up the first time), but all in all the process was still pretty painful.</p>
<p>Thankfully, a couple of technologies have come along and made my deployment process a whole lot easier.</p>
<ol>
<li>
<p><a href="http://modrails.com/">Passenger</a>
This was the big one. The <a href="http://www.phusion.nl/">Phusion</a> guys&rsquo; &ldquo;Hello World&rdquo; app (as they called it) has really had a positive impact on the Rails community, and me personally. Suddenly my Rails (and Rack) web apps are first class citizens to Apache (and Nginx), which means I can just point a virtual host at the public directory and go. I had almost forgotten how good it feels to just drop some files in a directory and have Apache serve them.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Deploying a Rails app used to suck. Reverse proxies, Mongrel clusters, Monit, etc. <a href="http://www.capify.org/">Capistrano</a> helped out a lot (once you set it up the first time), but all in all the process was still pretty painful.</p>
<p>Thankfully, a couple of technologies have come along and made my deployment process a whole lot easier.</p>
<ol>
<li>
<p><a href="http://modrails.com/">Passenger</a>
This was the big one. The <a href="http://www.phusion.nl/">Phusion</a> guys&rsquo; &ldquo;Hello World&rdquo; app (as they called it) has really had a positive impact on the Rails community, and me personally. Suddenly my Rails (and Rack) web apps are first class citizens to Apache (and Nginx), which means I can just point a virtual host at the public directory and go. I had almost forgotten how good it feels to just drop some files in a directory and have Apache serve them.</p>
</li>
<li>
<p><a href="http://git-scm.com">Git</a>
Ok, so maybe Subversion allows a similar workflow, but for some reason Git is one of those tools that is so much fun to use that it makes me think of <a href="https://jerodsanto.net/2009/05/git-informed-when-your-site-is-hacked/">different ways</a> I can use it.</p>
</li>
</ol>
<h2 id="my-flow">My Flow</h2>
<p>How I deploy these days (when I&rsquo;m not deploying to <a href="https://jerodsanto.net/2009/05/3-reasons-why-heroku-is-a-game-changer/">Heroku</a>) is dead simple. I host my private Git repos using <a href="http://eagain.net/gitweb/?p=gitosis.git;a=summary">Gitosis</a>, but the same would work with <a href="http://github.com">GitHub</a> or any Git server.</p>
<h2 id="initial-setup">Initial Setup</h2>
<ol>
<li>Clone the repository on production server.</li>
<li>Create database.yml and any other production-specific configs</li>
<li>Configure an Apache virtual host pointing to &ldquo;public&rdquo; folder of the repository</li>
</ol>
<h2 id="deploys">Deploys</h2>
<p><strong>locally:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ git push origin master
</span></span></span></code></pre></div><p><strong>remotely:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">jerod@mbp:~$ git pull origin master &amp;&amp; touch tmp/restart.txt
</span></span></span></code></pre></div><p>I know what you&rsquo;re thinking, &ldquo;Wow, that <em>is</em> dead simple&rdquo;. It&rsquo;s even easier by using Capistrano to execute the remote commands. Here is an example Capistrano task from one of my Rails apps:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">task</span> <span class="ss">:deploy</span><span class="p">,</span> <span class="ss">:roles</span>  <span class="o">=&gt;</span> <span class="ss">:production</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="nb">system</span> <span class="s2">&#34;git push origin master&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">cmd</span> <span class="o">=</span> <span class="o">[</span> <span class="s2">&#34;cd </span><span class="si">#{</span><span class="n">root_dir</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span> <span class="s2">&#34;git pull&#34;</span><span class="p">,</span> <span class="s2">&#34;touch tmp/restart.txt&#34;</span> <span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">run</span> <span class="n">cmd</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34; &amp;&amp; &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>This task can be extended to automatically install required gems, update Git submodules, migrate the database, and so on.</p>
<h2 id="other-benefits">Other Benefits</h2>
<p>Besides the simplicity and ease of deployment in this process, I have also enjoyed the ability to make edits in production and pull them back in to my development environment. And because my production environment has a complete history of code changes, it is trivial to revert commits that cause major problems.</p>
<p>This work flow is by no means a panacea. How do you handle deployment?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Rename A Gitosis Repository</title>
      <link>https://jerodsanto.net/2009/05/rename-a-gitosis-repository/</link>
      <pubDate>Thu, 28 May 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/05/rename-a-gitosis-repository/</guid>
      
      
      <description><![CDATA[<p>I use <a href="http://eagain.net/gitweb/?p=gitosis.git;a=summary">gitosis</a> for private git repository hosting (and it&rsquo;s awesome). If you are interested, this<a href="http://scie.nti.st/2007/11/14/hosting-git-repositories-the-easy-and-secure-way"> great tutorial</a> will walk you through setting it up yourself.</p>
<p>I recently needed to rename one of my repositories and couldn&rsquo;t find any info on how to do it, so here is a walk-thru. I will demonstrate the steps of renaming a repository called &ldquo;<strong>tk</strong>&rdquo; to &ldquo;<strong>show-time</strong>&rdquo;.</p>
<p>Rename project in gitosis.conf</p>
<p>Before:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">[group main]
</span></span></span><span class="line"><span class="cl"><span class="go">writable = tk
</span></span></span></code></pre></div><p>After:</p>]]></description>
      
      <content:encoded><![CDATA[<p>I use <a href="http://eagain.net/gitweb/?p=gitosis.git;a=summary">gitosis</a> for private git repository hosting (and it&rsquo;s awesome). If you are interested, this<a href="http://scie.nti.st/2007/11/14/hosting-git-repositories-the-easy-and-secure-way"> great tutorial</a> will walk you through setting it up yourself.</p>
<p>I recently needed to rename one of my repositories and couldn&rsquo;t find any info on how to do it, so here is a walk-thru. I will demonstrate the steps of renaming a repository called &ldquo;<strong>tk</strong>&rdquo; to &ldquo;<strong>show-time</strong>&rdquo;.</p>
<p>Rename project in gitosis.conf</p>
<p>Before:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">[group main]
</span></span></span><span class="line"><span class="cl"><span class="go">writable = tk
</span></span></span></code></pre></div><p>After:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">[group main]
</span></span></span><span class="line"><span class="cl"><span class="go">writable = show-time
</span></span></span></code></pre></div><p>Push changes</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">git push origin master
</span></span></span></code></pre></div><p>Connect to gitosis server and rename correct folder</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">cd /home/git/repositories
</span></span></span><span class="line"><span class="cl"><span class="go">mv tk show-time
</span></span></span></code></pre></div><p>Change the remote reference in all repository clones</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">cd /src/show-time
</span></span></span><span class="line"><span class="cl"><span class="go">git remote rm origin
</span></span></span><span class="line"><span class="cl"><span class="go">git remote add origin git@example-git-server.com:show-time.git
</span></span></span></code></pre></div><p>Done and done.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>3 Reasons Why Heroku is a Game Changer</title>
      <link>https://jerodsanto.net/2009/05/3-reasons-why-heroku-is-a-game-changer/</link>
      <pubDate>Tue, 26 May 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/05/3-reasons-why-heroku-is-a-game-changer/</guid>
      
      
      <description><![CDATA[<p>For the uninitiated, <a href="http://heroku.com">Heroku</a> is a &ldquo;cloud&rdquo; (hate that term) hosting service for Ruby applications. Their claim, which I believe they have substantiated, is to be an &ldquo;<strong>instant ruby platform</strong>&rdquo; and their success changes the game. Here is why.</p>
<h2 id="1-microwavesque-deployment">1) Microwavesque Deployment</h2>
<p>When Heroku claims that you can deploy your Ruby app instantly, they&rsquo;re pretty much not exaggerating. Seriously, you should try it. It&rsquo;s so fast I had to ignore my spell-checker and describe it as microwavesque. Remember when microwaves first hit the scene and people couldn&rsquo;t believe how fast they could &ldquo;deploy&rdquo; a meal? Yah me either, but the microwave changed the game big time.</p>]]></description>
      
      <content:encoded><![CDATA[<p>For the uninitiated, <a href="http://heroku.com">Heroku</a> is a &ldquo;cloud&rdquo; (hate that term) hosting service for Ruby applications. Their claim, which I believe they have substantiated, is to be an &ldquo;<strong>instant ruby platform</strong>&rdquo; and their success changes the game. Here is why.</p>
<h2 id="1-microwavesque-deployment">1) Microwavesque Deployment</h2>
<p>When Heroku claims that you can deploy your Ruby app instantly, they&rsquo;re pretty much not exaggerating. Seriously, you should try it. It&rsquo;s so fast I had to ignore my spell-checker and describe it as microwavesque. Remember when microwaves first hit the scene and people couldn&rsquo;t believe how fast they could &ldquo;deploy&rdquo; a meal? Yah me either, but the microwave changed the game big time.</p>
<p>If you haven&rsquo;t tried deploying a Rails app to Heroku, just go do it. Here are the steps from start to finish:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">rails myapp
</span></span></span><span class="line"><span class="cl"><span class="go">cd myapp
</span></span></span><span class="line"><span class="cl"><span class="go">git init
</span></span></span><span class="line"><span class="cl"><span class="go">git add .
</span></span></span><span class="line"><span class="cl"><span class="go">git commit -m &#34;my new app&#34;
</span></span></span><span class="line"><span class="cl"><span class="go">heroku create myapp
</span></span></span><span class="line"><span class="cl"><span class="go">git push heroku master
</span></span></span></code></pre></div><p>If that looks like too much work for you, have Remi walk you through it in <a href="http://remi.org/2009/04/23/deploying-rails-and-rack-applications-to-heroku.html">this nice screencast</a>.</p>
<h2 id="2-drag-to-scale">2) Drag To Scale</h2>
<p>Scaling is a problem we all want to have, but few of us do. The rest of us spend way too much time thinking about it. With Heroku&rsquo;s architecture, you don&rsquo;t have to think about scaling. You just do it. If you&rsquo;re fortunate enough to have your app featured on TechCrunch, Slashdot, Digg, HN, Reddit, etc, etc, you simply log in to the Heroku admin panel and &ldquo;crank your dynos&rdquo; (love that term) to handle the load. How do you do that? By dragging a little bar vertically. How&rsquo;s that for a learning curve?</p>
<p><a href="https://jerodsanto.net/wp-content/uploads/2009/05/dyno.png"><img class="aligncenter size-medium wp-image-381" title="dyno" src="https://jerodsanto.net/wp-content/uploads/2009/05/dyno-189x300.png" height="300" alt="dyno" width="189" /></a></p>
<p>Note: This isn&rsquo;t the only way to ensure your app scales. You should still optimize your code for performance, but Heroku takes care of all the server configuration (compression, caching, memory allocation, etc) and you just ask for more power when you need it.</p>
<h2 id="3-rack-support">3) Rack Support</h2>
<p>Heroku isn&rsquo;t just a Rails platform, it&rsquo;s a Ruby platform. They support any <a href="http://rack.rubyforge.org/">Rack-enabled</a> Ruby app. Check out how many Ruby web frameworks have Rack adapters:</p>
<ul>
<li>Camping</li>
<li>Coset</li>
<li>Halcyon</li>
<li>Mack</li>
<li>Maveric</li>
<li>Merb</li>
<li>Racktools::SimpleApplication</li>
<li>Ramaze</li>
<li>Ruby on Rails</li>
<li>Rum</li>
<li>Sinatra</li>
<li>Sin</li>
<li>Vintage</li>
<li>Waves</li>
<li>Wee</li>
</ul>
<p>That&rsquo;s a long frickin&rsquo; list! Developers can pick the right tool for their need (or write their own Rack-enabled app) and not have to worry about deployment gotchas.</p>
<p>For example, I wanted a way to quickly resolve the public IP address of any machine I&rsquo;m using (including servers) so I wrote <a href="http://my-ip.heroku.com">and deployed</a> this trivial Sinatra app in less than 5 minutes.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># public_ip.rb</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rubygems&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;sinatra&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">get</span> <span class="s1">&#39;/&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">raw</span> <span class="o">=</span> <span class="vi">@request</span><span class="o">.</span><span class="n">env</span><span class="o">[</span><span class="s2">&#34;REMOTE_ADDR&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">raw</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sr">/^(\d+\.\d+\.\d+\.\d+),?/</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@ip</span> <span class="o">=</span> <span class="vg">$1</span>
</span></span><span class="line"><span class="cl">  <span class="n">haml</span> <span class="s1">&#39;=@ip&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># config.ru</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;public_ip.rb&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">run</span> <span class="no">Sinatra</span><span class="o">::</span><span class="no">Application</span>
</span></span></code></pre></div><p>The rackup file (config.ru) tells Heroku to use Sinatra to run my application. Rails would have been overkill for this, don&rsquo;t you think? (Heck, Sinatra may be overkill too. You could probably write a raw Rack application in even fewer LOC, post in the comments if you have a better solution).</p>
<p><strong>UPDATE:</strong> Blake Mizerany provided a few shorter examples <a href="http://gist.github.com/118217">here</a>.</p>
<h2 id="so">So</h2>
<p>What Heroku offers Ruby web developers is instant deployment, fast &amp; easy scaling, and vast tool selection. Now we can concentrate on building our applications and forget the tedious deployment and server administration tasks that used to strangle our productivity. We can deploy fast, scale quickly, and adjust to circumstance as needs arise. The game is changing, and Heroku (and others like them) are changing it.</p>
<p><strong><strong>disclaimer</strong></strong>
I am only affiliated with Heroku as a customer, and receive no recompense from the ridiculous amount of props I&rsquo;m throwing their way. They do have a few drawbacks, one that is a show stopper for many apps, which I will detail in a future post.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Create Arbitrarily Sized Files In Less Than 15 Lines of Ruby</title>
      <link>https://jerodsanto.net/2009/05/create-arbitrarily-sized-files-in-less-than-15-lines-of-ruby/</link>
      <pubDate>Fri, 22 May 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/05/create-arbitrarily-sized-files-in-less-than-15-lines-of-ruby/</guid>
      
      
      <description><![CDATA[<p>Ok, so I&rsquo;m having fun with this &ldquo;in less than 15 lines of Ruby&rdquo; idea, but this time I turn my attention away from Twitter (I know, that&rsquo;s hard to do right now) and toward file generation.</p>
<p>Sometimes you just need a 1GB file. Or a 350MB file. Or a 1MB file. It doesn&rsquo;t matter what is in that file, but size does matter (heh).</p>
<p>With this little Ruby script, you can arbitrarily generate files of any size (using 1MB increments).</p>]]></description>
      
      <content:encoded><![CDATA[<p>Ok, so I&rsquo;m having fun with this &ldquo;in less than 15 lines of Ruby&rdquo; idea, but this time I turn my attention away from Twitter (I know, that&rsquo;s hard to do right now) and toward file generation.</p>
<p>Sometimes you just need a 1GB file. Or a 350MB file. Or a 1MB file. It doesn&rsquo;t matter what is in that file, but size does matter (heh).</p>
<p>With this little Ruby script, you can arbitrarily generate files of any size (using 1MB increments).</p>
<h2 id="the-script">The Script</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">print</span> <span class="s1">&#39;Enter file size (MB): &#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">the_size</span> <span class="o">=</span> <span class="nb">gets</span><span class="o">.</span><span class="n">chomp</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">fail</span> <span class="s2">&#34;bad file size&#34;</span> <span class="k">unless</span> <span class="n">the_size</span> <span class="o">=~</span> <span class="sr">/^\d+$/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">file_size</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="n">string</span>    <span class="o">=</span> <span class="s2">&#34;abcdefghijklmnopqrstuvwxyz123456&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">the_size</span> <span class="o">+</span> <span class="s1">&#39;MB&#39;</span><span class="p">,</span> <span class="s1">&#39;w&#39;</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="k">while</span> <span class="n">file_size</span> <span class="o">&lt;</span> <span class="n">the_size</span><span class="o">.</span><span class="n">to_i</span> <span class="o">*</span> <span class="mi">1048576</span> <span class="c1"># bytes in 1MB</span>
</span></span><span class="line"><span class="cl">    <span class="n">f</span><span class="o">.</span><span class="n">print</span> <span class="n">string</span>
</span></span><span class="line"><span class="cl">    <span class="n">file_size</span> <span class="o">+=</span> <span class="n">string</span><span class="o">.</span><span class="n">size</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>The resulting file will be named &ldquo;<strong>[X]MB</strong>&rdquo; where <strong>X</strong> is the size you requested.</p>
<h2 id="a-note">A Note</h2>
<p>This script is derived from one I found awhile ago somewhere on the internet (don&rsquo;t remember where). It didn&rsquo;t work correctly (file sizes were off) and was much more verbose.</p>
<p>Enjoy!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>jQuery: Open External Links In New Window/Tab</title>
      <link>https://jerodsanto.net/2009/05/jquery-open-external-links-in-new-window/tab/</link>
      <pubDate>Mon, 18 May 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/05/jquery-open-external-links-in-new-window/tab/</guid>
      
      
      <description><![CDATA[<p>The common technique to force links to open a new window or tab is to add a <code>target</code> attribute like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;http://hulu.com&#34;</span> <span class="na">target</span><span class="o">=</span><span class="s">&#34;_blank&#34;</span><span class="p">&gt;</span>check it out<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>This works just fine but is not actually valid markup. Fortunately, we can use jQuery to add the target attribute so it doesn&rsquo;t muck up the HTML.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;a[rel=&#34;external&#34;]&#39;</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">    <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s1">&#39;target&#39;</span><span class="p">,</span><span class="s1">&#39;_blank&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Change that invalid link to look like this instead:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;http://hulu.com&#34;</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;external&#34;</span><span class="p">&gt;</span>check it out<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Now the link forces browsers to open a new window/tab and the markup is still valid. Easy cheesy.</p>]]></description>
      
      <content:encoded><![CDATA[<p>The common technique to force links to open a new window or tab is to add a <code>target</code> attribute like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;http://hulu.com&#34;</span> <span class="na">target</span><span class="o">=</span><span class="s">&#34;_blank&#34;</span><span class="p">&gt;</span>check it out<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>This works just fine but is not actually valid markup. Fortunately, we can use jQuery to add the target attribute so it doesn&rsquo;t muck up the HTML.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;a[rel=&#34;external&#34;]&#39;</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">    <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s1">&#39;target&#39;</span><span class="p">,</span><span class="s1">&#39;_blank&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Change that invalid link to look like this instead:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;http://hulu.com&#34;</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;external&#34;</span><span class="p">&gt;</span>check it out<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Now the link forces browsers to open a new window/tab and the markup is still valid. Easy cheesy.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Expand Your Twitter Network In Less Than 15 Lines of Ruby</title>
      <link>https://jerodsanto.net/2009/05/expand-your-twitter-network-in-less-than-15-lines-of-ruby/</link>
      <pubDate>Sun, 17 May 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/05/expand-your-twitter-network-in-less-than-15-lines-of-ruby/</guid>
      
      
      <description><![CDATA[<p>A great way to meet new people on Twitter is by checking out the people your friends are interacting with. We can assume that if many of your friends follow somebody, that person has a high likelihood of being interesting to you (or it is <a href="http://twitter.com/aplusk">Ashton Kutcher</a>). Let&rsquo;s use Ruby to generate a list of people highly followed by our friends.</p>
<h2 id="the-flow">The Flow</h2>
<ol>
<li>fetch the ids of all the people you follow</li>
<li>use those ids to fetch the ids of all the people they follow</li>
<li>remove any ids in both groups (sound familiar?)</li>
<li>tally the occurrences of each unique id in the list</li>
<li>sort them by most occurrences</li>
<li>iterate the top 10 and print the user information</li>
</ol>
<h2 id="the-script">The Script</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rubygems&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;twitter&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">base</span>        <span class="o">=</span> <span class="no">Twitter</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Twitter</span><span class="o">::</span><span class="no">HTTPAuth</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="s1">&#39;password&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">my_friends</span>  <span class="o">=</span> <span class="n">base</span><span class="o">.</span><span class="n">friend_ids</span>
</span></span><span class="line"><span class="cl"><span class="n">candidates</span>  <span class="o">=</span> <span class="n">my_friends</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="nb">Array</span><span class="o">.</span><span class="n">new</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">array</span><span class="p">,</span><span class="nb">id</span><span class="o">|</span> <span class="n">array</span> <span class="o">+=</span> <span class="no">Twitter</span><span class="o">.</span><span class="n">friend_ids</span><span class="p">(</span><span class="nb">id</span><span class="p">);</span> <span class="n">array</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">candidates</span> <span class="o">-=</span> <span class="n">my_friends</span>
</span></span><span class="line"><span class="cl"><span class="n">tallied</span>     <span class="o">=</span> <span class="n">candidates</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="no">Hash</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span> <span class="p">{</span> <span class="o">|</span><span class="nb">hash</span><span class="p">,</span> <span class="n">can</span><span class="o">|</span> <span class="nb">hash</span><span class="o">[</span><span class="n">can</span><span class="o">]</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span> <span class="nb">hash</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">ordered</span>     <span class="o">=</span> <span class="n">tallied</span><span class="o">.</span><span class="n">sort</span> <span class="p">{</span> <span class="o">|</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="o">|</span> <span class="n">y</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">&lt;=&gt;</span> <span class="n">x</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ordered</span><span class="o">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">9</span><span class="o">].</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">array</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">user</span> <span class="o">=</span> <span class="n">base</span><span class="o">.</span><span class="n">user</span><span class="p">(</span><span class="n">array</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">user</span><span class="o">.</span><span class="n">screen_name</span><span class="si">}</span><span class="s2"> is followed by </span><span class="si">#{</span><span class="n">array</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span><span class="si">}</span><span class="s2"> of people you follow.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="some-explanation">Some Explanation</h2>
<p>The only &ldquo;tricky&rdquo; thing I&rsquo;m doing is making good use of Ruby&rsquo;s <a href="http://www.ruby-doc.org/core/classes/Enumerable.html#M003171">Enumerable#inject</a> method. Twice. This method iterates an enumerable object similar to how <code>each</code> does except it takes an argument and passes it through the block with the object it is iterating. The variable passed can be modified and is returned by the method. For a good write-up on <code>inject</code>, <a href="http://buddingrubyist.com/2008/02/05/why-i-like-to-inject/">see this blog post by The Budding Rubyist.</a></p>]]></description>
      
      <content:encoded><![CDATA[<p>A great way to meet new people on Twitter is by checking out the people your friends are interacting with. We can assume that if many of your friends follow somebody, that person has a high likelihood of being interesting to you (or it is <a href="http://twitter.com/aplusk">Ashton Kutcher</a>). Let&rsquo;s use Ruby to generate a list of people highly followed by our friends.</p>
<h2 id="the-flow">The Flow</h2>
<ol>
<li>fetch the ids of all the people you follow</li>
<li>use those ids to fetch the ids of all the people they follow</li>
<li>remove any ids in both groups (sound familiar?)</li>
<li>tally the occurrences of each unique id in the list</li>
<li>sort them by most occurrences</li>
<li>iterate the top 10 and print the user information</li>
</ol>
<h2 id="the-script">The Script</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rubygems&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;twitter&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">base</span>        <span class="o">=</span> <span class="no">Twitter</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Twitter</span><span class="o">::</span><span class="no">HTTPAuth</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="s1">&#39;password&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">my_friends</span>  <span class="o">=</span> <span class="n">base</span><span class="o">.</span><span class="n">friend_ids</span>
</span></span><span class="line"><span class="cl"><span class="n">candidates</span>  <span class="o">=</span> <span class="n">my_friends</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="nb">Array</span><span class="o">.</span><span class="n">new</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">array</span><span class="p">,</span><span class="nb">id</span><span class="o">|</span> <span class="n">array</span> <span class="o">+=</span> <span class="no">Twitter</span><span class="o">.</span><span class="n">friend_ids</span><span class="p">(</span><span class="nb">id</span><span class="p">);</span> <span class="n">array</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">candidates</span> <span class="o">-=</span> <span class="n">my_friends</span>
</span></span><span class="line"><span class="cl"><span class="n">tallied</span>     <span class="o">=</span> <span class="n">candidates</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="no">Hash</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span> <span class="p">{</span> <span class="o">|</span><span class="nb">hash</span><span class="p">,</span> <span class="n">can</span><span class="o">|</span> <span class="nb">hash</span><span class="o">[</span><span class="n">can</span><span class="o">]</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span> <span class="nb">hash</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">ordered</span>     <span class="o">=</span> <span class="n">tallied</span><span class="o">.</span><span class="n">sort</span> <span class="p">{</span> <span class="o">|</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="o">|</span> <span class="n">y</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">&lt;=&gt;</span> <span class="n">x</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ordered</span><span class="o">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">9</span><span class="o">].</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">array</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">user</span> <span class="o">=</span> <span class="n">base</span><span class="o">.</span><span class="n">user</span><span class="p">(</span><span class="n">array</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">user</span><span class="o">.</span><span class="n">screen_name</span><span class="si">}</span><span class="s2"> is followed by </span><span class="si">#{</span><span class="n">array</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span><span class="si">}</span><span class="s2"> of people you follow.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="some-explanation">Some Explanation</h2>
<p>The only &ldquo;tricky&rdquo; thing I&rsquo;m doing is making good use of Ruby&rsquo;s <a href="http://www.ruby-doc.org/core/classes/Enumerable.html#M003171">Enumerable#inject</a> method. Twice. This method iterates an enumerable object similar to how <code>each</code> does except it takes an argument and passes it through the block with the object it is iterating. The variable passed can be modified and is returned by the method. For a good write-up on <code>inject</code>, <a href="http://buddingrubyist.com/2008/02/05/why-i-like-to-inject/">see this blog post by The Budding Rubyist.</a></p>
<h2 id="a-limitation">A Limitation</h2>
<p>Unfortunately, this script is hindered by Twitter&rsquo;s 100 API calls per hour. If you follow more than 100 people on Twitter you&rsquo;re going to need a workaround. I&rsquo;ll leave these as an exercise for whoever is interested, but a few ideas are:</p>
<ol>
<li>sleep the script between API hits</li>
<li>toggle between multiple accounts for requests</li>
<li><a href="http://twitter.com/help/request_whitelisting">request whitelisting</a> from Twitter</li>
</ol>
<h2 id="a-challenge">A Challenge</h2>
<p>Can you can make this script smaller, more readable, or more robust? I put it on <a href="http://github.com">GitHub</a> so you can <a href="http://gist.github.com/113270">fork my gist</a> and put a link to your version in the comments!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Avoid Sore Fingers While SSHing Around</title>
      <link>https://jerodsanto.net/2009/05/avoid-sore-fingers-while-sshing-around/</link>
      <pubDate>Thu, 14 May 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/05/avoid-sore-fingers-while-sshing-around/</guid>
      
      
      <description><![CDATA[<p>If you&rsquo;re anything like me, you&rsquo;ve gotten accustomed to commands like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">jerod@mbp:~$ ssh <span class="o">[</span>user<span class="o">]</span>@<span class="o">[</span>remote.server.com<span class="o">]</span>
</span></span></code></pre></div><p>If you find yourself connecting to the same machines repeatedly, save a few keystrokes by creating a handy alias for them. Create (or edit) &ldquo;<strong>~/.ssh/config</strong>&rdquo; and add as many of these as your little heart desires:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Host <span class="o">[</span>the alias<span class="o">]</span>
</span></span><span class="line"><span class="cl">HostName <span class="o">[</span>domain name or IP address<span class="o">]</span>
</span></span><span class="line"><span class="cl">User <span class="o">[</span>the account to login as<span class="o">]</span>
</span></span></code></pre></div><p>Now you don&rsquo;t have to use the full command to access the machine, just use the alias! For example, here is how I access one of my DreamHost servers:</p>]]></description>
      
      <content:encoded><![CDATA[<p>If you&rsquo;re anything like me, you&rsquo;ve gotten accustomed to commands like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">jerod@mbp:~$ ssh <span class="o">[</span>user<span class="o">]</span>@<span class="o">[</span>remote.server.com<span class="o">]</span>
</span></span></code></pre></div><p>If you find yourself connecting to the same machines repeatedly, save a few keystrokes by creating a handy alias for them. Create (or edit) &ldquo;<strong>~/.ssh/config</strong>&rdquo; and add as many of these as your little heart desires:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Host <span class="o">[</span>the alias<span class="o">]</span>
</span></span><span class="line"><span class="cl">HostName <span class="o">[</span>domain name or IP address<span class="o">]</span>
</span></span><span class="line"><span class="cl">User <span class="o">[</span>the account to login as<span class="o">]</span>
</span></span></code></pre></div><p>Now you don&rsquo;t have to use the full command to access the machine, just use the alias! For example, here is how I access one of my DreamHost servers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">jerod@mbp:~$ ssh dh
</span></span></code></pre></div><p>The same goes for SCP! So, to secure copy a file (my_file.txt) in my current directory to the same machine I would simply issue:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">jerod@mbp:~$ scp my_file.txt dh:
</span></span></code></pre></div><p>Ahh&hellip; that is easy on the fingers! What else can we do with SSH config files?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>See Which Twitterers Don&#39;t Follow You Back In Less Than 15 Lines of Ruby</title>
      <link>https://jerodsanto.net/2009/05/see-which-twitterers-dont-follow-you-back-in-less-than-15-lines-of-ruby/</link>
      <pubDate>Tue, 12 May 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/05/see-which-twitterers-dont-follow-you-back-in-less-than-15-lines-of-ruby/</guid>
      
      
      <description><![CDATA[<p>The asynchronous nature of Twitter is one of its keys to success. No friend requests. <strong>Awesome.</strong></p>
<p>A lot of the people I follow on Twitter have no business following me. I didn&rsquo;t get upset when <a href="http://twitter.com/dhh">DHH</a>, <a href="http://twitter.com/_why">_why</a>, and <a href="http://twitter.com/alexalbrecht">alexalbrecht</a> did not reciprocate interest. Why would they? They don&rsquo;t know me from Adam (even though I&rsquo;m waayyy cooler than that dude&hellip;).</p>
<p>However, sometimes <em>it is</em> interesting to see all the <strike>jerks</strike> people who you follow that do not follow you back. There&rsquo;s probably a web application out there that does this, but who needs a web app when this is a perfectly good excuse to play with Ruby?</p>]]></description>
      
      <content:encoded><![CDATA[<p>The asynchronous nature of Twitter is one of its keys to success. No friend requests. <strong>Awesome.</strong></p>
<p>A lot of the people I follow on Twitter have no business following me. I didn&rsquo;t get upset when <a href="http://twitter.com/dhh">DHH</a>, <a href="http://twitter.com/_why">_why</a>, and <a href="http://twitter.com/alexalbrecht">alexalbrecht</a> did not reciprocate interest. Why would they? They don&rsquo;t know me from Adam (even though I&rsquo;m waayyy cooler than that dude&hellip;).</p>
<p>However, sometimes <em>it is</em> interesting to see all the <strike>jerks</strike> people who you follow that do not follow you back. There&rsquo;s probably a web application out there that does this, but who needs a web app when this is a perfectly good excuse to play with Ruby?</p>
<p>The <a href="http://github.com/jnunemaker/twitter/tree/master">twitter gem</a> by <a href="http://railstips.org/">John Nunemaker</a> makes this task so easy it&rsquo;s retarded. First, get the gem if you don&rsquo;t already have it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">jerod@mbp:~$ gem install twitter
</span></span></code></pre></div><p>Now. The script:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rubygems&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;twitter&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">auth</span>   <span class="o">=</span> <span class="no">Twitter</span><span class="o">::</span><span class="no">HTTPAuth</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="s1">&#39;password&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">base</span>   <span class="o">=</span> <span class="no">Twitter</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">auth</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">guilty</span> <span class="o">=</span> <span class="n">base</span><span class="o">.</span><span class="n">friend_ids</span> <span class="o">-</span> <span class="n">base</span><span class="o">.</span><span class="n">follower_ids</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">guilty</span><span class="o">.</span><span class="n">size</span><span class="si">}</span><span class="s2"> people you follow don&#39;t follow you back&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">guilty</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">user_id</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">user</span> <span class="o">=</span> <span class="n">base</span><span class="o">.</span><span class="n">user</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nb">puts</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">user</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2"> follows </span><span class="si">#{</span><span class="n">user</span><span class="o">.</span><span class="n">friends_count</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">       <span class="s2">&#34; and has </span><span class="si">#{</span><span class="n">user</span><span class="o">.</span><span class="n">followers_count</span><span class="si">}</span><span class="s2"> followers.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Isn&rsquo;t that simple? Are you with me on the retarded comment now? How it works:</p>
<p>The <code>friend_ids</code> and <code>follower_ids</code> methods each return an array of ids and we subtract the followers array from the friends array. All that is left over after subtraction are the ids of users in your friends list (people you follow) that are not in your followers list. Then we loop over the new array of &ldquo;guilty&rdquo; parties and fetch their user account information based on their user_id, printing the details each time.</p>
<p>Just replace <code>username</code> and <code>password</code> with your own information and you&rsquo;re off to the races. Run the script from your command line and you should see output similar to this:</p>
<img class="aligncenter size-full wp-image-296" title="no_follows" src="https://jerodsanto.net/wp-content/uploads/2009/05/no_follows.png" height="212" alt="no_follows" width="475" />
<p><strong>NOTE:</strong> Twitter only allows (currently) 100 API requests per hour. Each user account lookup requires an API request, so if your &ldquo;guilty&rdquo; array is quite large, you&rsquo;ll probably get an error before the script terminates (Maybe its time to un-follow a few peeps!).</p>
<p>What other cool tricks can we do using the Twitter gem?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>jQuery: Set Mouse Focus On Page Load</title>
      <link>https://jerodsanto.net/2009/05/jquery-set-mouse-focus-on-page-load/</link>
      <pubDate>Sat, 09 May 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/05/jquery-set-mouse-focus-on-page-load/</guid>
      
      
      <description><![CDATA[<h2 id="first-input">First Input:</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;input:text:first&#39;</span><span class="p">).</span><span class="nx">focus</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><h2 id="first-empty-input">First Empty Input:</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;input:text[value=&#34;&#34;]:first&#39;</span><span class="p">).</span><span class="nx">focus</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div>]]></description>
      
      <content:encoded><![CDATA[<h2 id="first-input">First Input:</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;input:text:first&#39;</span><span class="p">).</span><span class="nx">focus</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><h2 id="first-empty-input">First Empty Input:</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;input:text[value=&#34;&#34;]:first&#39;</span><span class="p">).</span><span class="nx">focus</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Git Informed When Your Site Is Hacked</title>
      <link>https://jerodsanto.net/2009/05/git-informed-when-your-site-is-hacked/</link>
      <pubDate>Mon, 04 May 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/05/git-informed-when-your-site-is-hacked/</guid>
      
      
      <description><![CDATA[<p>Good security require <a href="http://en.wikipedia.org/wiki/Defense_in_Depth_(computing)">defense in depth</a>. The more security layers you add, the harder it is to subvert them all. Here is a reactionary layer you can add to your security practices.</p>
<p>The only thing worse than getting hacked is getting hacked and not knowing it (or worse yet, having one of your clients inform you). Often times an attacker needs to add and/or change files on your site to accomplish their goal. Wouldn&rsquo;t it be nice if something could inform you of unauthorized changes? Enter <a href="http://git-scm.com/">Git</a>.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Good security require <a href="http://en.wikipedia.org/wiki/Defense_in_Depth_(computing)">defense in depth</a>. The more security layers you add, the harder it is to subvert them all. Here is a reactionary layer you can add to your security practices.</p>
<p>The only thing worse than getting hacked is getting hacked and not knowing it (or worse yet, having one of your clients inform you). Often times an attacker needs to add and/or change files on your site to accomplish their goal. Wouldn&rsquo;t it be nice if something could inform you of unauthorized changes? Enter <a href="http://git-scm.com/">Git</a>.</p>
<p>I will demonstrate using Git for change notification on a WordPress install using Ruby, but you can apply this principle in many scenarios (hmm, /etc&hellip;?).</p>
<p>~</p>
<p><strong>1) ignore folders/directories of no interest</strong></p>
<p>We don&rsquo;t want Git to track every file in the directory, so we&rsquo;ll tell it which ones to ignore. Common choices are temporary directories, log files, and any file or directory that gets update frequently. Create a file called .gitignore in your site&rsquo;s root directory. List the stuff for Git to ignore in it. It should look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wp-content/cache/*
</span></span><span class="line"><span class="cl">wp-content/uploads/*
</span></span></code></pre></div><p><strong>2) create new Git repository, add all files and execute first commit</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git init
</span></span><span class="line"><span class="cl">git add .
</span></span><span class="line"><span class="cl">git commit -a -m <span class="s2">&#34;Initial Commit&#34;</span>
</span></span></code></pre></div><p>Now you&rsquo;re set.</p>
<p><strong>3) download &amp; customize script</strong></p>
<p>Git alone won&rsquo;t check itself and email you. For this, we&rsquo;ll need help of a scripting language. I wrote this little script in Ruby, but the same could be accomplished in Bash, Python, or whatever suits your fancy. You can write your own or download and customize the one I use:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wget http://jerodsanto.net/src/ruby/git_watch.rb
</span></span><span class="line"><span class="cl">chmod +x git_watch.rb
</span></span></code></pre></div><p>The key to the script is where it shells out and runs the &lsquo;git status&rsquo; command. If there have been changes to the repository that were not properly committed, &lsquo;git status&rsquo; will not return &ldquo;working directory clean&rdquo;. The check is simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">result</span> <span class="o">=</span> <span class="sb">`git status`</span>
</span></span><span class="line"><span class="cl"><span class="k">unless</span> <span class="n">result</span> <span class="o">=~</span> <span class="sr">/working directory clean/</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># set up email</span>
</span></span><span class="line"><span class="cl">  <span class="n">send_email</span> <span class="no">SEND_TO</span><span class="p">,</span> <span class="ss">:body</span>  <span class="o">=&gt;</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>You&rsquo;ll need to edit this file and change the <code>SEND_TO</code> variable to your email address. You can also customize the email that is sent by modifying the <code>send_email</code> function near the top.</p>
<p><strong>4) schedule script execution</strong></p>
<p>Now, lets make this script check the blog for changes once every hour. Edit your user&rsquo;s cron configuration and add a line similar to this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="m">0</span> * * * * /scripts/git_watch.rb /var/www/mysite/wordpress
</span></span></code></pre></div><p>I put all my custom scripts in a <code>/scripts</code> directory on every server I administer. That way I always know where to look no matter what server I&rsquo;m currently on. The script takes one argument, the directory to check for changes. Adjust your cron for your script and site locations.</p>
<p>But what about authorized changes? Simple. Commit them using Git and they won&rsquo;t be triggered on.</p>
<p>For example, you install a new plugin to your blog and you don&rsquo;t want your git_watch script to email you about it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git add wp-content/plugins/github-widget/
</span></span><span class="line"><span class="cl">git commit -a -m <span class="s2">&#34;installed github widget plugin&#34;</span>
</span></span></code></pre></div><p>The added bonus of this technique is you are creating a verifiable changelog of your site over time, complete with notes! An example from one of my sites:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git log
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">commit 109d30026118de089297a4d7fa56babff3677bdc
</span></span><span class="line"><span class="cl">Author: Jerod Santo &lt;jerod.santo@gmail.com&gt;
</span></span><span class="line"><span class="cl">Date:   Sat May <span class="m">2</span> 07:55:08 <span class="m">2009</span> -0700
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    fixed bad php4 install and upgraded wp-cache
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">commit edc1e89c2cbf38ae1373f6b5cf03d29942399fd8
</span></span><span class="line"><span class="cl">Author: Jerod Santo &lt;jerod.santo@gmail.com&gt;
</span></span><span class="line"><span class="cl">Date:   Fri Apr <span class="m">10</span> 06:25:31 <span class="m">2009</span> -0700
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    ignore sitemap changes
</span></span></code></pre></div><p>Plus, it is easy to trace the attacker&rsquo;s steps if you do get compromised because you can see which files have been changed and what has been done to them (using the <code>git diff</code> command). Once you diagnose the problem, you can simply revert to an old commit before the attack (you&rsquo;re still vulnerable at this point, but at least your site is clean until you can patch it).</p>
<p>Enjoy!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Dreamy: Now With 100% API Coverage</title>
      <link>https://jerodsanto.net/2009/05/dreamy-now-with-100-api-coverage/</link>
      <pubDate>Sat, 02 May 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/05/dreamy-now-with-100-api-coverage/</guid>
      
      
      <description><![CDATA[<p>The folks at DreamHost were gracious enough to provide me with a complimentary Private Server account so I could expand <a href="http://github.com/jerodsanto/dreamy">Dreamy&rsquo;s</a> functionality. What a nice surprise that was!</p>
<p>So, to ensure they got their money&rsquo;s worth, I added all the Private Server API calls to Dreamy! This means that Dreamy now covers the entire DreamHost API. Check out all the new commands added to &ldquo;dh&rdquo; (the accompanying command-line tool):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ps                                 <span class="c1"># list private servers</span>
</span></span><span class="line"><span class="cl">ps:add &lt;web<span class="p">|</span>mysql&gt; &lt;yes<span class="p">|</span>no&gt;        <span class="c1"># adds a private server of type &lt;web|mysql&gt;</span>
</span></span><span class="line"><span class="cl">ps:pending                         <span class="c1"># list private servers scheduled to be created</span>
</span></span><span class="line"><span class="cl">ps:reboots &lt;name&gt;                  <span class="c1"># list historical reboots for &lt;name&gt;</span>
</span></span><span class="line"><span class="cl">ps:reboot &lt;name&gt; now!              <span class="c1"># reboot &lt;name&gt; now! (proceed with caution)</span>
</span></span><span class="line"><span class="cl">ps:remove                          <span class="c1"># removes all pending private servers</span>
</span></span><span class="line"><span class="cl">ps:settings &lt;name&gt;                 <span class="c1"># list settings for private server &lt;name&gt;</span>
</span></span><span class="line"><span class="cl">ps:set &lt;name&gt; &lt;setting&gt; &lt;value&gt;    <span class="c1"># change &lt;setting&gt; on &lt;name&gt; to &lt;value&gt;</span>
</span></span><span class="line"><span class="cl">ps:size &lt;name&gt;                     <span class="c1"># list historical memory sizes for &lt;name&gt;</span>
</span></span><span class="line"><span class="cl">ps:size &lt;name&gt; &lt;value&gt;             <span class="c1"># set new memory &lt;value&gt; for &lt;name&gt;</span>
</span></span><span class="line"><span class="cl">ps:usage &lt;name&gt;                    <span class="c1"># list historical memory &amp; CPU usage for &lt;name&gt;</span>
</span></span></code></pre></div><p>You&rsquo;ll need version 0.4.2 of the Dreamy gem to use the new functionality. To upgrade your gem, simply run the following:</p>]]></description>
      
      <content:encoded><![CDATA[<p>The folks at DreamHost were gracious enough to provide me with a complimentary Private Server account so I could expand <a href="http://github.com/jerodsanto/dreamy">Dreamy&rsquo;s</a> functionality. What a nice surprise that was!</p>
<p>So, to ensure they got their money&rsquo;s worth, I added all the Private Server API calls to Dreamy! This means that Dreamy now covers the entire DreamHost API. Check out all the new commands added to &ldquo;dh&rdquo; (the accompanying command-line tool):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ps                                 <span class="c1"># list private servers</span>
</span></span><span class="line"><span class="cl">ps:add &lt;web<span class="p">|</span>mysql&gt; &lt;yes<span class="p">|</span>no&gt;        <span class="c1"># adds a private server of type &lt;web|mysql&gt;</span>
</span></span><span class="line"><span class="cl">ps:pending                         <span class="c1"># list private servers scheduled to be created</span>
</span></span><span class="line"><span class="cl">ps:reboots &lt;name&gt;                  <span class="c1"># list historical reboots for &lt;name&gt;</span>
</span></span><span class="line"><span class="cl">ps:reboot &lt;name&gt; now!              <span class="c1"># reboot &lt;name&gt; now! (proceed with caution)</span>
</span></span><span class="line"><span class="cl">ps:remove                          <span class="c1"># removes all pending private servers</span>
</span></span><span class="line"><span class="cl">ps:settings &lt;name&gt;                 <span class="c1"># list settings for private server &lt;name&gt;</span>
</span></span><span class="line"><span class="cl">ps:set &lt;name&gt; &lt;setting&gt; &lt;value&gt;    <span class="c1"># change &lt;setting&gt; on &lt;name&gt; to &lt;value&gt;</span>
</span></span><span class="line"><span class="cl">ps:size &lt;name&gt;                     <span class="c1"># list historical memory sizes for &lt;name&gt;</span>
</span></span><span class="line"><span class="cl">ps:size &lt;name&gt; &lt;value&gt;             <span class="c1"># set new memory &lt;value&gt; for &lt;name&gt;</span>
</span></span><span class="line"><span class="cl">ps:usage &lt;name&gt;                    <span class="c1"># list historical memory &amp; CPU usage for &lt;name&gt;</span>
</span></span></code></pre></div><p>You&rsquo;ll need version 0.4.2 of the Dreamy gem to use the new functionality. To upgrade your gem, simply run the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">jerod@mbp:~$ sudo gem update jerodsanto-dreamy
</span></span></code></pre></div><p>That&rsquo;s all for now!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Clean WP Dashboard</title>
      <link>https://jerodsanto.net/2009/04/clean-wp-dashboard/</link>
      <pubDate>Thu, 23 Apr 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/04/clean-wp-dashboard/</guid>
      
      
      <description><![CDATA[<p>I just published my 2nd WordPress plugin, <a href="http://wordpress.org/extend/plugins/clean-wp-dashboard/">Clean WP Dashboard</a>.</p>
<p>WordPress 2.7&rsquo;s dashboard is a huge improvement over previous versions, but when you&rsquo;re running a site with many users sometimes you just want to simplify things. Enter <a href="http://wordpress.org/extend/plugins/clean-wp-dashboard/">Clean WP Dashboard</a>.</p>
<p>This plugin adds a simple administration page called &ldquo;Dashboard Settings&rdquo; which allows you to select/de-select which of the default WordPress widgets are available on the dashboard.</p>
<p>Source code is on my <a href="http://github.com/jerodsanto">GitHub account</a>, as usual.</p>]]></description>
      
      <content:encoded><![CDATA[<p>I just published my 2nd WordPress plugin, <a href="http://wordpress.org/extend/plugins/clean-wp-dashboard/">Clean WP Dashboard</a>.</p>
<p>WordPress 2.7&rsquo;s dashboard is a huge improvement over previous versions, but when you&rsquo;re running a site with many users sometimes you just want to simplify things. Enter <a href="http://wordpress.org/extend/plugins/clean-wp-dashboard/">Clean WP Dashboard</a>.</p>
<p>This plugin adds a simple administration page called &ldquo;Dashboard Settings&rdquo; which allows you to select/de-select which of the default WordPress widgets are available on the dashboard.</p>
<p>Source code is on my <a href="http://github.com/jerodsanto">GitHub account</a>, as usual.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Tweetie Has Arrived</title>
      <link>https://jerodsanto.net/2009/04/tweetie-has-arrived/</link>
      <pubDate>Tue, 21 Apr 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/04/tweetie-has-arrived/</guid>
      
      
      <description><![CDATA[<p>The anointing of an OS X application:</p>
<img class="alignleft size-full wp-image-236" title="dockit" src="https://jerodsanto.net/wp-content/uploads/2009/04/dockit.png" height="206" alt="dockit" width="261" />]]></description>
      
      <content:encoded><![CDATA[<p>The anointing of an OS X application:</p>
<img class="alignleft size-full wp-image-236" title="dockit" src="https://jerodsanto.net/wp-content/uploads/2009/04/dockit.png" height="206" alt="dockit" width="261" />
]]></content:encoded>
    </item>
    
    <item>
      <title>So Dreamy!</title>
      <link>https://jerodsanto.net/2009/04/so-dreamy/</link>
      <pubDate>Sat, 18 Apr 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/04/so-dreamy/</guid>
      
      
      <description><![CDATA[<p>DreamHost is running a <a href="http://blog.dreamhost.com/2009/04/09/big-boy-time/">kick-off competition</a> in support of their newly released <a href="http://wiki.dreamhost.com/API">API</a>. It seems like their developer community could use a little productivity boost, so I thought I&rsquo;d create a Ruby library to interact with their API.</p>
<p>The resulting Ruby Gem, <a href="http://github.com/jerodsanto/dreamy">available on GitHub</a>, is called &ldquo;<strong>Dreamy</strong>&rdquo; and includes a library and a command-line tool.</p>
<p>Why a command-line tool? Because sometimes you wanna see what&rsquo;s going on with your DH account without having to pop open the control panel (also, as an example of how to use the library). Here is some sample output returning all DNS records for domains matching the string &ldquo;rachel&rdquo; (my wife&rsquo;s name):</p>]]></description>
      
      <content:encoded><![CDATA[<p>DreamHost is running a <a href="http://blog.dreamhost.com/2009/04/09/big-boy-time/">kick-off competition</a> in support of their newly released <a href="http://wiki.dreamhost.com/API">API</a>. It seems like their developer community could use a little productivity boost, so I thought I&rsquo;d create a Ruby library to interact with their API.</p>
<p>The resulting Ruby Gem, <a href="http://github.com/jerodsanto/dreamy">available on GitHub</a>, is called &ldquo;<strong>Dreamy</strong>&rdquo; and includes a library and a command-line tool.</p>
<p>Why a command-line tool? Because sometimes you wanna see what&rsquo;s going on with your DH account without having to pop open the control panel (also, as an example of how to use the library). Here is some sample output returning all DNS records for domains matching the string &ldquo;rachel&rdquo; (my wife&rsquo;s name):</p>
<p><a href="https://jerodsanto.net/wp-content/uploads/2009/04/dreamy_output.png"><img class="aligncenter size-full wp-image-202" title="dreamy_output" src="https://jerodsanto.net/wp-content/uploads/2009/04/dreamy_output.png" height="227" alt="dreamy_output" width="443" /></a></p>
<p>Isn&rsquo;t that table-based formatting <em>dreamy</em>?!?! Next up, a listing of all domains on the account and their availability. If one of the domains is down, it will ping the domain&rsquo;s host server to determine if the problem is system-wide or not:
<a href="https://jerodsanto.net/wp-content/uploads/2009/04/dh_domain_status.png"><img class="aligncenter size-full wp-image-231" title="dh_domain_status" src="https://jerodsanto.net/wp-content/uploads/2009/04/dh_domain_status.png" height="110" alt="dh_domain_status" width="443" /></a></p>
<p>There are many more things you can do with the command-line tool, but you can check out those by installing the gem and running:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">dh <span class="nb">help</span>
</span></span></code></pre></div><p>How do you install the gem? It&rsquo;s super-simple (if you already have Ruby and RubyGems installed):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gem install dreamy
</span></span></code></pre></div><p>Booyah!</p>
<p>How do you use the library? Well, you just require it into your Ruby program and hit the ground running!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rubygems&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;dreamy&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">account</span> <span class="o">=</span> <span class="no">Dreamy</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="n">api_key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># get an array of Domain objects</span>
</span></span><span class="line"><span class="cl"><span class="n">account</span><span class="o">.</span><span class="n">domains</span>
</span></span><span class="line"><span class="cl"><span class="c1"># get an array of User objects</span>
</span></span><span class="line"><span class="cl"><span class="n">account</span><span class="o">.</span><span class="n">users</span>
</span></span><span class="line"><span class="cl"><span class="c1"># get an array of DNS objects</span>
</span></span><span class="line"><span class="cl"><span class="n">account</span><span class="o">.</span><span class="n">dns</span>
</span></span><span class="line"><span class="cl"><span class="c1"># get an array of Subcribers to &#34;my_list@example.com&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">account</span><span class="o">.</span><span class="n">announce_list</span><span class="p">(</span><span class="s2">&#34;my_list&#34;</span><span class="p">,</span><span class="s2">&#34;example.com&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># add a subscriber to &#34;my_list@example.com&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">account</span><span class="o">.</span><span class="n">announce_add</span><span class="p">(</span><span class="s2">&#34;my_list&#34;</span><span class="p">,</span><span class="s2">&#34;example.com&#34;</span><span class="p">,</span><span class="s2">&#34;guy@gmail.com&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># remove a subscriber to &#34;my_list@example.com&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">account</span><span class="o">.</span><span class="n">announce_remove</span><span class="p">(</span><span class="s2">&#34;my_list&#34;</span><span class="p">,</span><span class="s2">&#34;example.com&#34;</span><span class="p">,</span><span class="s2">&#34;guy@gmail.com&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>The Domain, User, DNS, and Subscriber objects that are returned in the arrays include all the data that DreamHost exposes about them from their API. For instance, I can print the attributes of the Domain object like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">account</span><span class="o">.</span><span class="n">domains</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">instance_variables</span>
</span></span></code></pre></div><p>Which returns:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">[</span><span class="s2">&#34;@www_or_not&#34;</span><span class="p">,</span> <span class="s2">&#34;@user&#34;</span><span class="p">,</span> <span class="s2">&#34;@php&#34;</span><span class="p">,</span> <span class="s2">&#34;@path&#34;</span><span class="p">,</span> <span class="s2">&#34;@fastcgi&#34;</span><span class="p">,</span> <span class="s2">&#34;@unique_ip&#34;</span><span class="p">,</span> <span class="s2">&#34;@passenger&#34;</span><span class="p">,</span> <span class="s2">&#34;@type&#34;</span><span class="p">,</span> <span class="s2">&#34;@account_id&#34;</span><span class="p">,</span> <span class="s2">&#34;@security&#34;</span><span class="p">,</span> <span class="s2">&#34;@home&#34;</span><span class="p">,</span> <span class="s2">&#34;@domain&#34;</span><span class="p">,</span> <span class="s2">&#34;@outside_url&#34;</span><span class="p">,</span> <span class="s2">&#34;@xcache&#34;</span><span class="p">,</span> <span class="s2">&#34;@php_fcgid&#34;</span><span class="p">,</span> <span class="s2">&#34;@hosting_type&#34;</span><span class="o">]</span>
</span></span></code></pre></div><p>To find out more about the library, head over to the project&rsquo;s <a href="http://github.com/jerodsanto/dreamy/">GitHub page</a> and check out the README.</p>
<p>Let me know what you think!</p>
<p><strong>UPDATE:</strong> Dreamy is now 100% API Compatible (and a whole lot cooler!) Check out <a href="https://jerodsanto.net/2009/05/dreamy-now-with-100-api-coverage">my second blog post outlining the changes</a></p>
<p><strong>NOTE #1: The DreamHost API is still young and in heavy development. Dreamy is nowhere near a comprehensive library, but I wanted to get it into the public so other developers could use it for their competition projects. Please, if you find any bugs or want to add functionality&hellip; please, please, please fork the project and help out!</strong></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>New Gem: Rack-noIE6</title>
      <link>https://jerodsanto.net/2009/04/new-gem-rack-noie6/</link>
      <pubDate>Sat, 18 Apr 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/04/new-gem-rack-noie6/</guid>
      
      
      <description><![CDATA[<p>Many web developers are discontinuing support for IE6. I, happily, am one of them (unless a client demands it). The other day I went searching for an IE6 detection and redirect solution to aide in my un-support of the browser. What I found was pretty rad.</p>
<p>Now that Rails is on Rack, dozens of useful middleware apps are being developed and can be plugged into Rails with ease. Thanks to a simple <a href="http://github.com">GitHub</a> search, I found the <a href="http://github.com/juliocesar/rack-noie/tree/master">rack-noie</a> project by Julio Cesar.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Many web developers are discontinuing support for IE6. I, happily, am one of them (unless a client demands it). The other day I went searching for an IE6 detection and redirect solution to aide in my un-support of the browser. What I found was pretty rad.</p>
<p>Now that Rails is on Rack, dozens of useful middleware apps are being developed and can be plugged into Rails with ease. Thanks to a simple <a href="http://github.com">GitHub</a> search, I found the <a href="http://github.com/juliocesar/rack-noie/tree/master">rack-noie</a> project by Julio Cesar.</p>
<p>His middleware did almost exactly what I wanted except for a few small things. First, I prefer using gems with Rails so dependencies can easily be managed using config.gem. Second, we&rsquo;re just hating on IE6, not IE in general. Therefore, the name is a bit misleading.</p>
<p>So, in the spirit of open-source, I forked his project and molded it to my liking. You can see the shiny new rack-noie6 gem&rsquo;s GitHub page <a href="http://github.com/jerodsanto/rack-noie6/">here</a>.</p>
<p>Its dead simple to integrate. First, install the gem</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gem install rack-noie6
</span></span></code></pre></div><p>Next (if you&rsquo;re using Rails), add the following to environment.rb inside the Rails::Initializer.run block:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">config</span><span class="o">.</span><span class="n">gem</span> <span class="s1">&#39;rack-noie6&#39;</span><span class="p">,</span> <span class="ss">:lib</span> <span class="o">=&gt;</span> <span class="s1">&#39;noie6&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span><span class="o">.</span><span class="n">middleware</span><span class="o">.</span><span class="n">use</span> <span class="s2">&#34;Rack::NoIE6&#34;</span>
</span></span></code></pre></div><p>As the IE6-BraveHeart would proclaim: FREEEEDOMMMM!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>2&gt;&amp;1</title>
      <link>https://jerodsanto.net/2009/03/21/</link>
      <pubDate>Tue, 17 Mar 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/03/21/</guid>
      
      
      <description><![CDATA[<p>A common practice when adding entries to crontab is to end the entry like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;/dev/null 2&gt;<span class="p">&amp;</span><span class="m">1</span>
</span></span></code></pre></div><p>The purpose of this is to suppress any output from the command itself, because we&rsquo;re not interested. I picked up this syntax years ago because it just works, but I never knew what the <code>2&gt;&amp;1</code> actually meant, until today.</p>
<p>The first part:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;/dev/null
</span></span></code></pre></div><p>Means redirect <code>STDOUT</code> (the standard output stream) to <code>/dev/null</code> (which is basically a blackhole for bits). That&rsquo;s easy enough.</p>]]></description>
      
      <content:encoded><![CDATA[<p>A common practice when adding entries to crontab is to end the entry like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;/dev/null 2&gt;<span class="p">&amp;</span><span class="m">1</span>
</span></span></code></pre></div><p>The purpose of this is to suppress any output from the command itself, because we&rsquo;re not interested. I picked up this syntax years ago because it just works, but I never knew what the <code>2&gt;&amp;1</code> actually meant, until today.</p>
<p>The first part:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt;/dev/null
</span></span></code></pre></div><p>Means redirect <code>STDOUT</code> (the standard output stream) to <code>/dev/null</code> (which is basically a blackhole for bits). That&rsquo;s easy enough.</p>
<p>The second part:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">2&gt;<span class="p">&amp;</span><span class="m">1</span>
</span></span></code></pre></div><p>Means redirect <code>STDERR</code> (standard error stream) to the same place as <code>STDOUT</code> (which was just specified). <code>STDOUT</code> has the assigned number 1 and <code>STDERR</code> has the assigned number 2.</p>
<p>This way both <code>STDOUT</code> (1) and <code>STDERR</code> (2) are directed to <code>/dev/null</code> and all output of the cronned command is suppressed.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Rsnapshot (Rsync) Gotcha</title>
      <link>https://jerodsanto.net/2009/03/rsnapshot-rsync-gotcha/</link>
      <pubDate>Mon, 09 Mar 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/03/rsnapshot-rsync-gotcha/</guid>
      
      
      <description><![CDATA[<p>If you&rsquo;re trying to backup a remote host using <a href="http://rsnapshot.org/">rsnapshot</a> (or rsync by itself) and run into one of the following ambiguous errors:</p>
<p><strong>rsnapshot version:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">ERROR: /usr/bin/rsync returned 12 while processing ...
</span></span></span></code></pre></div><p><strong>rsync version:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">rsync error: error in rsync protocol data stream (code 12)
</span></span></span></code></pre></div><p>It&rsquo;s probably because you don&rsquo;t have rsync installed on the remote host (doh!)</p>]]></description>
      
      <content:encoded><![CDATA[<p>If you&rsquo;re trying to backup a remote host using <a href="http://rsnapshot.org/">rsnapshot</a> (or rsync by itself) and run into one of the following ambiguous errors:</p>
<p><strong>rsnapshot version:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">ERROR: /usr/bin/rsync returned 12 while processing ...
</span></span></span></code></pre></div><p><strong>rsync version:</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">rsync error: error in rsync protocol data stream (code 12)
</span></span></span></code></pre></div><p>It&rsquo;s probably because you don&rsquo;t have rsync installed on the remote host (doh!)</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Bash array and loops in FireHOL configuration</title>
      <link>https://jerodsanto.net/2009/02/bash-array-and-loops-in-firehol-configuration/</link>
      <pubDate>Thu, 26 Feb 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/02/bash-array-and-loops-in-firehol-configuration/</guid>
      
      
      <description><![CDATA[<p>IPTables is a powerful but cryptic firewall solution. <a href="http://firehol.sourceforge.net/">FireHOL</a> is an IPTables configurator that flat out rocks. One of FireHOL&rsquo;s strengths is that it uses standard <a href="http://www.gnu.org/software/bash/bash.html">BASH</a> syntax inside its configuration file, so you get all the power of BASH to configure your firewall.</p>
<p>Let&rsquo;s see how a BASH array and for loop can help clean up our FireHOL config:</p>
<p>You have 3 machines that need SSH access to the server. First, you can setup variable names to reference the IP addresses (or DNS names) of the machines. Put these declarations at the top of your FireHOL config for easy maintenance.</p>]]></description>
      
      <content:encoded><![CDATA[<p>IPTables is a powerful but cryptic firewall solution. <a href="http://firehol.sourceforge.net/">FireHOL</a> is an IPTables configurator that flat out rocks. One of FireHOL&rsquo;s strengths is that it uses standard <a href="http://www.gnu.org/software/bash/bash.html">BASH</a> syntax inside its configuration file, so you get all the power of BASH to configure your firewall.</p>
<p>Let&rsquo;s see how a BASH array and for loop can help clean up our FireHOL config:</p>
<p>You have 3 machines that need SSH access to the server. First, you can setup variable names to reference the IP addresses (or DNS names) of the machines. Put these declarations at the top of your FireHOL config for easy maintenance.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">srv1</span><span class="o">=</span><span class="s2">&#34;205.205.205.1&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">srv2</span><span class="o">=</span><span class="s2">&#34;srv2.example.com&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">srv3</span><span class="o">=</span><span class="s2">&#34;143.32.2.44&#34;</span>
</span></span></code></pre></div><p>Now lets see what the SSH allow declaration will look like using these variables on interface eth0:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">interface eth0 public
</span></span><span class="line"><span class="cl">  policy reject
</span></span><span class="line"><span class="cl">  server ssh accept src <span class="nv">$srv1</span>
</span></span><span class="line"><span class="cl">  server ssh accept src <span class="nv">$srv2</span>
</span></span><span class="line"><span class="cl">  server ssh accept src <span class="nv">$srv3</span>
</span></span></code></pre></div><p>Notice how each host you want to allow SSH access adds another line to your configuration. This may seem trivial in my example but can add a lot of complexity as your environment grows. Is there a better way to implement? You bet.</p>
<p>First, lets create an array to house all of the hosts we want to provide SSH access to:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">ssh_list</span><span class="o">=(</span><span class="nv">$srv1</span> <span class="nv">$srv2</span> <span class="nv">$srv3</span><span class="o">)</span>
</span></span></code></pre></div><p>Next, we change the declaration on our interface to simply loop through this list of hosts and allow SSH access:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">interface eth0 public
</span></span><span class="line"><span class="cl">  policy reject
</span></span><span class="line"><span class="cl">  <span class="k">for</span> host in <span class="si">${</span><span class="nv">ssh_list</span><span class="p">[@]</span><span class="si">}</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    server ssh accept <span class="nv">$host</span>
</span></span><span class="line"><span class="cl">  <span class="k">done</span>
</span></span></code></pre></div><p>Much better! Now we can simply add/remove hosts from our <code>ssh_list</code> array (at the top of the config file where all our variables are declared) and let BASH do the rest. The key here is the <code>${ssh_list[@]}</code> which returns the evaluated list of hosts inside the <code>ssh_list</code> array. Enjoy.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Extending TMail::Mail for HTML and plaintext only</title>
      <link>https://jerodsanto.net/2009/02/extending-tmailmail-for-html-and-plaintext-only/</link>
      <pubDate>Sat, 21 Feb 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/02/extending-tmailmail-for-html-and-plaintext-only/</guid>
      
      
      <description><![CDATA[<p>A project I&rsquo;m working on required a little more from Ruby&rsquo;s TMail library than it offers out of the box. One of the things that make Ruby great is how you can dynamically extend classes.</p>
<p>TMail can parse a raw email and provide you with the headers, subject, body, etc. But what it doesn&rsquo;t do is parse the body and pull out the html and/or plaintext from multi-part emails. Well, now it can.</p>]]></description>
      
      <content:encoded><![CDATA[<p>A project I&rsquo;m working on required a little more from Ruby&rsquo;s TMail library than it offers out of the box. One of the things that make Ruby great is how you can dynamically extend classes.</p>
<p>TMail can parse a raw email and provide you with the headers, subject, body, etc. But what it doesn&rsquo;t do is parse the body and pull out the html and/or plaintext from multi-part emails. Well, now it can.</p>
<p>I found a great little piece of code on <a href="http://code.google.com">Google Code</a> called <a href="http://code.google.com/p/tmail-html-body-extractor/">tmail-html-body-extractor</a>. It was written by <a href="http://fernandoguillen.info">Fernando Guillen</a> and released under the Apache License 2.0.</p>
<p>Fernando&rsquo;s code was great, but its intent was returning just the html from the email body. For my current project, I am more interested in just the plaintext from the email body, so I added this functionality.</p>
<p>Thanks to the openness of the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>, I was able to extend his script and re-release it on GitHub with my modifications.</p>
<p>Now its trivial to return just the html from an email body, or just the plaintext:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;rubygems&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;action_mailer&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;tmail_mail_extension&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">mail</span> <span class="o">=</span> <span class="no">TMail</span><span class="o">::</span><span class="no">Mail</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">raw_email</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">mail</span><span class="o">.</span><span class="n">body_html</span>  <span class="c1"># returns just html if available or nil</span>
</span></span><span class="line"><span class="cl"><span class="n">mail</span><span class="o">.</span><span class="n">body_plain</span> <span class="c1"># returns just plaintext if available or nil</span>
</span></span></code></pre></div><p>You can get the source on <a href="http://github.com/jerodsanto/tmail_body_extractors/tree/master">GitHub</a>!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>A simple Ruby method to send email</title>
      <link>https://jerodsanto.net/2009/02/a-simple-ruby-method-to-send-email/</link>
      <pubDate>Tue, 17 Feb 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/02/a-simple-ruby-method-to-send-email/</guid>
      
      
      <description><![CDATA[<p>I have tried many different Ruby mailers and they all have their problems. The <a href="http://github.com/adamwiggins/pony/tree/master">Pony gem</a> by Adam Wiggins is right up my alley but even that has given me a hard time sending emails. Plus, sometimes you just don&rsquo;t want your little Ruby script having to require rubygems.</p>
<p>I always end up reverting to a simple method I wrote awhile back and it just works. Feel free to use it and adjust to your needs:</p>]]></description>
      
      <content:encoded><![CDATA[<p>I have tried many different Ruby mailers and they all have their problems. The <a href="http://github.com/adamwiggins/pony/tree/master">Pony gem</a> by Adam Wiggins is right up my alley but even that has given me a hard time sending emails. Plus, sometimes you just don&rsquo;t want your little Ruby script having to require rubygems.</p>
<p>I always end up reverting to a simple method I wrote awhile back and it just works. Feel free to use it and adjust to your needs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;net/smtp&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">send_email</span><span class="p">(</span><span class="n">to</span><span class="p">,</span><span class="n">opts</span><span class="o">=</span><span class="p">{})</span>
</span></span><span class="line"><span class="cl">  <span class="n">opts</span><span class="o">[</span><span class="ss">:server</span><span class="o">]</span>      <span class="o">||=</span> <span class="s1">&#39;localhost&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">opts</span><span class="o">[</span><span class="ss">:from</span><span class="o">]</span>        <span class="o">||=</span> <span class="s1">&#39;email@example.com&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">opts</span><span class="o">[</span><span class="ss">:from_alias</span><span class="o">]</span>  <span class="o">||=</span> <span class="s1">&#39;Example Emailer&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">opts</span><span class="o">[</span><span class="ss">:subject</span><span class="o">]</span>     <span class="o">||=</span> <span class="s2">&#34;You need to see this&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">opts</span><span class="o">[</span><span class="ss">:body</span><span class="o">]</span>        <span class="o">||=</span> <span class="s2">&#34;Important stuff!&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">msg</span> <span class="o">=</span> <span class="s">&lt;&lt;END_OF_MESSAGE
</span></span></span><span class="line"><span class="cl"><span class="ss">From</span><span class="p">:</span> <span class="c1">#{opts[:from_alias]} &lt;#{opts[:from]}&gt;</span>
</span></span><span class="line"><span class="cl"><span class="ss">To</span><span class="p">:</span> <span class="o">&lt;</span><span class="c1">#{to}&gt;</span>
</span></span><span class="line"><span class="cl"><span class="ss">Subject</span><span class="p">:</span> <span class="c1">#{opts[:subject]}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#{opts[:body]}</span>
</span></span><span class="line"><span class="cl"><span class="no">END_OF_MESSAGE</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="no">Net</span><span class="o">::</span><span class="no">SMTP</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="n">opts</span><span class="o">[</span><span class="ss">:server</span><span class="o">]</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">smtp</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="n">smtp</span><span class="o">.</span><span class="n">send_message</span> <span class="n">msg</span><span class="p">,</span> <span class="n">opts</span><span class="o">[</span><span class="ss">:from</span><span class="o">]</span><span class="p">,</span> <span class="n">to</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Everything but the <code>to</code> argument is optional. You can invoke the method like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">send_email</span> <span class="s2">&#34;admnistrator@example.com&#34;</span><span class="p">,</span> <span class="ss">:body</span> <span class="o">=&gt;</span> <span class="s2">&#34;This was easy to send&#34;</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Great Article on Aptitude vs Apt-Get</title>
      <link>https://jerodsanto.net/2009/02/great-article-on-aptitude-vs-apt-get/</link>
      <pubDate>Mon, 16 Feb 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/02/great-article-on-aptitude-vs-apt-get/</guid>
      
      
      <description><![CDATA[<p>If you need some convincing to start using aptitude for all your Debian/Ubuntu software management needs, Aaron Toponce&rsquo;s article will do the job. It&rsquo;s a few years old (at the time of this post) but still relevant.</p>
<p>Don&rsquo;t take my word for it. <a href="http://pthree.org/2007/08/12/aptitude-vs-apt-get/">Read it for yourself.</a></p>]]></description>
      
      <content:encoded><![CDATA[<p>If you need some convincing to start using aptitude for all your Debian/Ubuntu software management needs, Aaron Toponce&rsquo;s article will do the job. It&rsquo;s a few years old (at the time of this post) but still relevant.</p>
<p>Don&rsquo;t take my word for it. <a href="http://pthree.org/2007/08/12/aptitude-vs-apt-get/">Read it for yourself.</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Getting ready for Lenny</title>
      <link>https://jerodsanto.net/2009/02/getting-ready-for-lenny/</link>
      <pubDate>Mon, 16 Feb 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/02/getting-ready-for-lenny/</guid>
      
      
      <description><![CDATA[<p>You don&rsquo;t need to fire up an editor to switch your APT repositories from Debian 4 (Etch) to Debian 5 (Lenny):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">ruby</span> <span class="o">-</span><span class="n">i</span> <span class="o">-</span><span class="n">pe</span> <span class="s1">&#39;$_.gsub!(&#34;etch&#34;,&#34;lenny&#34;)&#39;</span> <span class="sr">/etc/</span><span class="n">apt</span><span class="o">/</span><span class="n">sources</span><span class="o">.</span><span class="n">lst</span>
</span></span></code></pre></div>]]></description>
      
      <content:encoded><![CDATA[<p>You don&rsquo;t need to fire up an editor to switch your APT repositories from Debian 4 (Etch) to Debian 5 (Lenny):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">ruby</span> <span class="o">-</span><span class="n">i</span> <span class="o">-</span><span class="n">pe</span> <span class="s1">&#39;$_.gsub!(&#34;etch&#34;,&#34;lenny&#34;)&#39;</span> <span class="sr">/etc/</span><span class="n">apt</span><span class="o">/</span><span class="n">sources</span><span class="o">.</span><span class="n">lst</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Debian 5.0 (Lenny) Released</title>
      <link>https://jerodsanto.net/2009/02/debian-5.0-lenny-released/</link>
      <pubDate>Sun, 15 Feb 2009 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2009/02/debian-5.0-lenny-released/</guid>
      
      
      <description><![CDATA[<p>Its been almost 2 years since Debian 4.0 (Etch) was first released. Debian release cycles are so long (because of their commitment to stable releases, amongst other reasons) that they often fly off my radar.</p>
<p>I was completely surprised by this release, and almost every software package in my favorite server operating system has been upgraded!</p>
<p>Check out the <a href="http://lists.debian.org/debian-announce/2009/msg00002.html">Release announcement</a>. The list of <a href="http://wiki.debian.org/NewInLenny">new packages</a>. And the <a href="http://www.debian-administration.org/articles/626">Debian Administration</a> post which includes instructions on how to upgrade</p>]]></description>
      
      <content:encoded><![CDATA[<p>Its been almost 2 years since Debian 4.0 (Etch) was first released. Debian release cycles are so long (because of their commitment to stable releases, amongst other reasons) that they often fly off my radar.</p>
<p>I was completely surprised by this release, and almost every software package in my favorite server operating system has been upgraded!</p>
<p>Check out the <a href="http://lists.debian.org/debian-announce/2009/msg00002.html">Release announcement</a>. The list of <a href="http://wiki.debian.org/NewInLenny">new packages</a>. And the <a href="http://www.debian-administration.org/articles/626">Debian Administration</a> post which includes instructions on how to upgrade</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>fix your Debian VMware image&#39;s network after offline copy</title>
      <link>https://jerodsanto.net/2008/09/fix-your-debian-vmware-images-network-after-offline-copy/</link>
      <pubDate>Wed, 17 Sep 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/09/fix-your-debian-vmware-images-network-after-offline-copy/</guid>
      
      
      <description><![CDATA[<p>If you copy and move a VMware virtual machine that runs Debian, you’ll find that the network adapter is no longer available, which sucks.</p>
<p>I implemented a simple fix using Ruby. Just make it start at boot by adding it to your <code>/etc/rc.local</code> and it should be all good.</p>
<p><strong>NOTICE:</strong> automatically reboots machine</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># 2&gt;&amp;1 redirects stderr to stdout so we can capture it</span>
</span></span><span class="line"><span class="cl"><span class="n">if_status</span> <span class="o">=</span> <span class="sb">`ifconfig eth0 2&gt;&amp;1`</span>
</span></span><span class="line"><span class="cl"><span class="n">config_file</span> <span class="o">=</span> <span class="s2">&#34;/etc/udev/rules.d/z25_persistent-net.rules&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">if_status</span> <span class="o">=~</span> <span class="sr">/Device not found/</span>
</span></span><span class="line"><span class="cl">  <span class="n">config_text</span> <span class="o">=</span> <span class="nb">Array</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">  <span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">config_file</span><span class="p">,</span><span class="s2">&#34;r&#34;</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span> <span class="n">config_text</span> <span class="o">=</span> <span class="n">file</span><span class="o">.</span><span class="n">readlines</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">relevant_text</span> <span class="o">=</span> <span class="n">config_text</span><span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span> <span class="n">line</span> <span class="o">=~</span> <span class="sr">/^SUBSYSTEM==/</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">output</span> <span class="o">=</span> <span class="n">relevant_text</span><span class="o">.</span><span class="n">last</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/ethd/</span><span class="p">,</span><span class="s2">&#34;eth0&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">config_file</span><span class="p">,</span><span class="s2">&#34;w&#34;</span><span class="p">){</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span> <span class="n">file</span><span class="o">.</span><span class="n">puts</span> <span class="n">output</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="nb">system</span><span class="p">(</span><span class="s2">&#34;reboot&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p><a href="http://jerodsanto.net/src/ruby/vm_mac_fixer.rb">Download</a> and use it if you’d like.</p>]]></description>
      
      <content:encoded><![CDATA[<p>If you copy and move a VMware virtual machine that runs Debian, you’ll find that the network adapter is no longer available, which sucks.</p>
<p>I implemented a simple fix using Ruby. Just make it start at boot by adding it to your <code>/etc/rc.local</code> and it should be all good.</p>
<p><strong>NOTICE:</strong> automatically reboots machine</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># 2&gt;&amp;1 redirects stderr to stdout so we can capture it</span>
</span></span><span class="line"><span class="cl"><span class="n">if_status</span> <span class="o">=</span> <span class="sb">`ifconfig eth0 2&gt;&amp;1`</span>
</span></span><span class="line"><span class="cl"><span class="n">config_file</span> <span class="o">=</span> <span class="s2">&#34;/etc/udev/rules.d/z25_persistent-net.rules&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">if_status</span> <span class="o">=~</span> <span class="sr">/Device not found/</span>
</span></span><span class="line"><span class="cl">  <span class="n">config_text</span> <span class="o">=</span> <span class="nb">Array</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">  <span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">config_file</span><span class="p">,</span><span class="s2">&#34;r&#34;</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span> <span class="n">config_text</span> <span class="o">=</span> <span class="n">file</span><span class="o">.</span><span class="n">readlines</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">relevant_text</span> <span class="o">=</span> <span class="n">config_text</span><span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span> <span class="n">line</span> <span class="o">=~</span> <span class="sr">/^SUBSYSTEM==/</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">output</span> <span class="o">=</span> <span class="n">relevant_text</span><span class="o">.</span><span class="n">last</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/ethd/</span><span class="p">,</span><span class="s2">&#34;eth0&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">config_file</span><span class="p">,</span><span class="s2">&#34;w&#34;</span><span class="p">){</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span> <span class="n">file</span><span class="o">.</span><span class="n">puts</span> <span class="n">output</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="nb">system</span><span class="p">(</span><span class="s2">&#34;reboot&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p><a href="http://jerodsanto.net/src/ruby/vm_mac_fixer.rb">Download</a> and use it if you’d like.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Starting Asterisk on Boot in Debian</title>
      <link>https://jerodsanto.net/2008/09/starting-asterisk-on-boot-in-debian/</link>
      <pubDate>Wed, 03 Sep 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/09/starting-asterisk-on-boot-in-debian/</guid>
      
      
      <description><![CDATA[<p>Here’s a quickie. You just compiled <a href="http://asterisk.org/">Asterisk</a> on your <a href="http://debian.org/">Debian</a> server and you want to make sure it starts when you reboot. Here’s how:</p>
<p>Look in the <code>/contrib/init.d</code> folder of your Asterisk source directory. You’ll see a file called <code>rc.debian.asterisk</code>. If you installed Asterisk to the default location, don’t worry about editing this file. If you installed to a different location (eg - /usr/local), change the following line in the file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">DAEMON</span><span class="o">=</span>/usr/sbin/asterisk
</span></span></code></pre></div><p>Point this at your Asterisk binary. Not sure where it is? Just type <code>which asterisk</code> from the command line and it will show you the full path.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Here’s a quickie. You just compiled <a href="http://asterisk.org/">Asterisk</a> on your <a href="http://debian.org/">Debian</a> server and you want to make sure it starts when you reboot. Here’s how:</p>
<p>Look in the <code>/contrib/init.d</code> folder of your Asterisk source directory. You’ll see a file called <code>rc.debian.asterisk</code>. If you installed Asterisk to the default location, don’t worry about editing this file. If you installed to a different location (eg - /usr/local), change the following line in the file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">DAEMON</span><span class="o">=</span>/usr/sbin/asterisk
</span></span></code></pre></div><p>Point this at your Asterisk binary. Not sure where it is? Just type <code>which asterisk</code> from the command line and it will show you the full path.</p>
<p>Next, copy the file into the <code>/etc/init.d/</code> directory like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cp rc.debian.asterisk /etc/init.d/asterisk
</span></span></code></pre></div><p>(NOTE: I am renaming the file on purpose)</p>
<p>Now you can control Asterisk by executing this script. Make sure it starts and stops before continuing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">/etc/init.d/asterisk start
</span></span><span class="line"><span class="cl">Starting Asterisk PBX: asterisk.
</span></span><span class="line"><span class="cl">/etc/init.d/asterisk stop
</span></span><span class="line"><span class="cl">Stopping Asterisk PBX: asterisk.
</span></span></code></pre></div><p>Finally, make the system run this script during the boot process:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">update-rc.d asterisk defaults
</span></span></code></pre></div><p>Done and done. Reboot and check the process list just to be sure!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Adhearsion TextMate Bundle</title>
      <link>https://jerodsanto.net/2008/08/adhearsion-textmate-bundle/</link>
      <pubDate>Sat, 30 Aug 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/08/adhearsion-textmate-bundle/</guid>
      
      
      <description><![CDATA[<p>I began hacking on Asterisk using the <a href="http://adhearsion.com/">Adhearsion</a> framework a few weeks ago. Despite initial setbacks, I am now having an absolute blast.</p>
<p>I put together a little <a href="http://macromates.com/">TextMate</a> bundle to aide in my development and figured I’d share it out as a starting point for others. I am not a TextMate or Adhearsion pro (yet) so I am sure there are a ton of improvements to be made.</p>
<p><a href="https://jerodsanto.net/uploads/2008/08/adhearsion-tmbundle.png"><img class="aligncenter size-full wp-image-81" title="adhearsion-tmbundle" src="https://jerodsanto.net/wp-content/uploads/2008/08/adhearsion-tmbundle.png" height="131" alt="adhearsion-tmbundle" width="495" /></a></p>]]></description>
      
      <content:encoded><![CDATA[<p>I began hacking on Asterisk using the <a href="http://adhearsion.com/">Adhearsion</a> framework a few weeks ago. Despite initial setbacks, I am now having an absolute blast.</p>
<p>I put together a little <a href="http://macromates.com/">TextMate</a> bundle to aide in my development and figured I’d share it out as a starting point for others. I am not a TextMate or Adhearsion pro (yet) so I am sure there are a ton of improvements to be made.</p>
<p><a href="https://jerodsanto.net/uploads/2008/08/adhearsion-tmbundle.png"><img class="aligncenter size-full wp-image-81" title="adhearsion-tmbundle" src="https://jerodsanto.net/wp-content/uploads/2008/08/adhearsion-tmbundle.png" height="131" alt="adhearsion-tmbundle" width="495" /></a></p>
<p>So, please fork my <a href="http://github.com/jerodsanto/adhearsion-tmbundle/tree/master">GitHub repo</a> and make improvements. We will all benefit from them!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Date Range Goodies in Rails</title>
      <link>https://jerodsanto.net/2008/08/date-range-goodies-in-rails/</link>
      <pubDate>Wed, 27 Aug 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/08/date-range-goodies-in-rails/</guid>
      
      
      <description><![CDATA[<p>Sometimes Rails just blows me away, and it just happened a few moments ago. When it comes to dealing with dates and their formats between different databases, Ruby, time zones, etc, etc…it can get pretty nasty. I can’t believe the amount of help Rails delivers in this arena.</p>
<p>Let’s assume I’m trying to track phone calls across time ranges; daily, monthly, yearly. This can usually become troublesome as I’ll have to query the database to find only records inside those ranges. I will spare you all the lame ways I tried to implement this by hand and just show you the code of how to create a named scope and call it using Rails 2.1.0.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Sometimes Rails just blows me away, and it just happened a few moments ago. When it comes to dealing with dates and their formats between different databases, Ruby, time zones, etc, etc…it can get pretty nasty. I can’t believe the amount of help Rails delivers in this arena.</p>
<p>Let’s assume I’m trying to track phone calls across time ranges; daily, monthly, yearly. This can usually become troublesome as I’ll have to query the database to find only records inside those ranges. I will spare you all the lame ways I tried to implement this by hand and just show you the code of how to create a named scope and call it using Rails 2.1.0.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># in call.rb</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Call</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">named_scope</span> <span class="ss">:by_month</span><span class="p">,</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="n">d</span><span class="o">|</span> <span class="p">{</span> <span class="ss">:conditions</span>  <span class="o">=&gt;</span> <span class="p">{</span> <span class="ss">:date</span>  <span class="o">=&gt;</span> <span class="n">d</span><span class="o">.</span><span class="n">beginning_of_month</span><span class="o">..</span><span class="n">d</span><span class="o">.</span><span class="n">end_of_month</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span>
</span></span></code></pre></div><p>What this does is create a new scope called <code>by_month</code> that lets me query the database…by month :) and pass in a Date object as a single parameter. Rails/ActiveRecord will take the Date object and pass it to the lambda block, which calls the two Rails helpers ( <code>beginning_of_month</code> and <code>end_of_month</code> ) to find the date range for the month my Date object is in. It then queries the database using that date range!</p>
<p>Awesome.</p>
<p>Now I can access the records I want like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">this_months_calls</span> <span class="o">=</span> <span class="no">Call</span><span class="o">.</span><span class="n">by_month</span> <span class="no">Date</span><span class="o">.</span><span class="n">today</span>
</span></span></code></pre></div><p>And Rails will return all the calls that were recorded during the current month. Love it.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Configure Network Time Protocol (NTP) in Debian</title>
      <link>https://jerodsanto.net/2008/08/configure-network-time-protocol-ntp-in-debian/</link>
      <pubDate>Wed, 27 Aug 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/08/configure-network-time-protocol-ntp-in-debian/</guid>
      
      
      <description><![CDATA[<p>Ok, this bugs the crap out of me. I set up a shiny new Debian 4.0 base install and go on my merry way, meanwhile the system time is off by a long shot! I only notice when it starts to hurt…</p>
<p>Here’s how to synchronize your Debian system’s time with network time servers, in a few simple steps (use sudo as needed):</p>
<ol>
<li>Install the necessary packages:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apt-get install ntp ntpdate
</span></span></code></pre></div><ol start="2">
<li>Stop the NTP daemon for now (apt-get will start the service upon successful installation):</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">/etc/init.d/ntp stop
</span></span></code></pre></div><ol start="3">
<li>Manually synchronize the system clock to the NTP server pool:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ntpdate pool.ntp.org
</span></span></code></pre></div><p>3.5) An explanation: the NTP server daemon will fail to sync with the NTP servers if the system time is too far from the NTP servers time so its best to manually synchronize once to get your system time close enough.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Ok, this bugs the crap out of me. I set up a shiny new Debian 4.0 base install and go on my merry way, meanwhile the system time is off by a long shot! I only notice when it starts to hurt…</p>
<p>Here’s how to synchronize your Debian system’s time with network time servers, in a few simple steps (use sudo as needed):</p>
<ol>
<li>Install the necessary packages:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apt-get install ntp ntpdate
</span></span></code></pre></div><ol start="2">
<li>Stop the NTP daemon for now (apt-get will start the service upon successful installation):</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">/etc/init.d/ntp stop
</span></span></code></pre></div><ol start="3">
<li>Manually synchronize the system clock to the NTP server pool:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ntpdate pool.ntp.org
</span></span></code></pre></div><p>3.5) An explanation: the NTP server daemon will fail to sync with the NTP servers if the system time is too far from the NTP servers time so its best to manually synchronize once to get your system time close enough.</p>
<ol start="4">
<li>Restart the NTP daemon:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">/etc/init.d/ntp start
</span></span></code></pre></div><ol start="5">
<li>(Optionally) Verify that the NTP daemon is synchronized by checking the syslog:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">grep ntpd /var/log/syslog <span class="p">|</span> tail
</span></span><span class="line"><span class="cl">ntpd<span class="o">[</span>6348<span class="o">]</span>: synchronized to 74.53.198.146, stratum <span class="m">2</span>
</span></span><span class="line"><span class="cl">ntpd<span class="o">[</span>6348<span class="o">]</span>: kernel <span class="nb">time</span> sync enabled <span class="m">0001</span>
</span></span><span class="line"><span class="cl">ntpd<span class="o">[</span>6348<span class="o">]</span>: synchronized to 128.10.19.24, stratum <span class="m">1</span>
</span></span></code></pre></div><p>Your output may vary, but it should look similar to mine.</p>
<p>That’s all for now!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Introducing lil tom peep</title>
      <link>https://jerodsanto.net/2008/08/introducing-lil-tom-peep/</link>
      <pubDate>Sat, 23 Aug 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/08/introducing-lil-tom-peep/</guid>
      
      
      <description><![CDATA[<p><a href="http://liltompeep.heroku.com/">lil tom peep</a> is a new web application I wrote to fix a problem I had with web surfing. I hate having to manually check up on semi-static web pages like blog comment streams and firmware update pages (basically anything missing RSS).</p>
<p>Now I just submit the page url and my email address to <a href="http://liltompeep.heroku.com/">lil tom peep</a> and he does the checking for me. Pretty basic, but very useful to me.</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="http://liltompeep.heroku.com/">lil tom peep</a> is a new web application I wrote to fix a problem I had with web surfing. I hate having to manually check up on semi-static web pages like blog comment streams and firmware update pages (basically anything missing RSS).</p>
<p>Now I just submit the page url and my email address to <a href="http://liltompeep.heroku.com/">lil tom peep</a> and he does the checking for me. Pretty basic, but very useful to me.</p>
<p><a href="https://jerodsanto.net/wp-content/uploads/2008/08/ltp.png"><img class="aligncenter size-full wp-image-83" title="ltp" src="https://jerodsanto.net/wp-content/uploads/2008/08/ltp.png" height="116" alt="ltp" width="406" /></a></p>
<p><a href="http://liltompeep.heroku.com/">ltp</a> is powered by <a href="http://sinatrarb.com/">Sinatra</a>. Check out the <a href="http://liltompeep.com/props">props</a> page to see all the technologies involved. Possible future features include RSS-integration and a bookmarklet. Let me know if you have any feature requests or encounter any bugs while using the service. Enjoy!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Debian Etch: Default Repositories</title>
      <link>https://jerodsanto.net/2008/08/debian-etch-default-repositories/</link>
      <pubDate>Fri, 22 Aug 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/08/debian-etch-default-repositories/</guid>
      
      
      <description><![CDATA[<p>In case you muck up your <code>/etc/apt/sources.list</code> and want to set it back to the defaults, just copy and paste this in:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">deb http://ftp.debian.org/debian/ etch main
</span></span><span class="line"><span class="cl">deb-src http://ftp.debian.org/debian/ etch main
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">deb http://security.debian.org/ etch/updates main contrib
</span></span><span class="line"><span class="cl">deb-src http://security.debian.org/ etch/updates main contrib
</span></span></code></pre></div>]]></description>
      
      <content:encoded><![CDATA[<p>In case you muck up your <code>/etc/apt/sources.list</code> and want to set it back to the defaults, just copy and paste this in:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">deb http://ftp.debian.org/debian/ etch main
</span></span><span class="line"><span class="cl">deb-src http://ftp.debian.org/debian/ etch main
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">deb http://security.debian.org/ etch/updates main contrib
</span></span><span class="line"><span class="cl">deb-src http://security.debian.org/ etch/updates main contrib
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Easy Configuration with Ruby and Yaml</title>
      <link>https://jerodsanto.net/2008/08/easy-configuration-with-ruby-and-yaml/</link>
      <pubDate>Tue, 19 Aug 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/08/easy-configuration-with-ruby-and-yaml/</guid>
      
      
      <description><![CDATA[<p>Even trivial apps need to be configured. I used to simply define my app config somewhere near the top of the file, as many others do.</p>
<p>However, this becomes troublesome in a few common scenarios:</p>
<ol>
<li>You want to share your source code with somebody else, but not your super-secret password</li>
<li>Your application becomes more complex and multiple areas need access to configuration variables</li>
</ol>
<p>Abstracting configuration out of your Ruby app and into a separate <a href="http://www.yaml.org/">Yaml</a> file is super-simple. Here’s some codey code to use as an example:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Even trivial apps need to be configured. I used to simply define my app config somewhere near the top of the file, as many others do.</p>
<p>However, this becomes troublesome in a few common scenarios:</p>
<ol>
<li>You want to share your source code with somebody else, but not your super-secret password</li>
<li>Your application becomes more complex and multiple areas need access to configuration variables</li>
</ol>
<p>Abstracting configuration out of your Ruby app and into a separate <a href="http://www.yaml.org/">Yaml</a> file is super-simple. Here’s some codey code to use as an example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># this is &#39;myapp.rb&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;yaml&#39;</span>
</span></span><span class="line"><span class="cl"><span class="no">CONFIG</span> <span class="o">=</span> <span class="no">Yaml</span><span class="o">.</span><span class="n">load_file</span><span class="p">(</span><span class="s2">&#34;config.yml&#34;</span><span class="p">)</span> <span class="k">unless</span> <span class="n">defined?</span> <span class="no">CONFIG</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="s2">&#34;Your super-secret password is </span><span class="si">#{</span><span class="no">CONFIG</span><span class="o">[</span><span class="s1">&#39;password&#39;</span><span class="o">]</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span></code></pre></div><p>Can it get any easier than that? I submit that it, in fact, cannot get any easier. You probably want to know what the <code>config.yml</code> file looks like, huh:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># this is my &#39;config.yml&#39;</span>
</span></span><span class="line"><span class="cl"><span class="ss">username</span><span class="p">:</span> <span class="n">jerodsanto</span>
</span></span><span class="line"><span class="cl"><span class="ss">password</span><span class="p">:</span> <span class="n">awesome</span>
</span></span></code></pre></div><p>Now if you want to share your source with a friend, perhaps via git, you can just add <code>config.yml</code> to the <code>.gitignore</code> file in your repository and create a <code>config-sample.yml</code> which holds dummy values.</p>
<p>Any questions?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Ruby to the Rescue</title>
      <link>https://jerodsanto.net/2008/08/ruby-to-the-rescue/</link>
      <pubDate>Mon, 18 Aug 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/08/ruby-to-the-rescue/</guid>
      
      
      <description><![CDATA[<p>Ruby makes handling exceptions super simple. Lets assume you’re about to run a sketchy bit of code, like requesting a remote web site. It might look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;open-uri&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;http://slashdot.org/&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">doc</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span></code></pre></div><p>Now, what if <a href="http://slashdot.org/">SlashDot</a> is down for maintenance, overloaded with traffic, or has blocked your IP for scraping their site every 30 seconds? This code nugget will fail and your program will explode like a piñata during Puerto Rico Days. Let’s account for this and terminate our program gracefully:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Ruby makes handling exceptions super simple. Lets assume you’re about to run a sketchy bit of code, like requesting a remote web site. It might look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;open-uri&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;http://slashdot.org/&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">doc</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span></code></pre></div><p>Now, what if <a href="http://slashdot.org/">SlashDot</a> is down for maintenance, overloaded with traffic, or has blocked your IP for scraping their site every 30 seconds? This code nugget will fail and your program will explode like a piñata during Puerto Rico Days. Let’s account for this and terminate our program gracefully:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;open-uri&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;http://slashdot.org/&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">begin</span>
</span></span><span class="line"><span class="cl">   <span class="n">doc</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">rescue</span>
</span></span><span class="line"><span class="cl">   <span class="nb">puts</span> <span class="s2">&#34;The request for a page at </span><span class="si">#{</span><span class="n">url</span><span class="si">}</span><span class="s2"> timed out...exiting.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">exit</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Ahh, that’s better! But what if we’re iterating a list of url’s and scraping pages? Maybe we don’t want to exit the program at all, but continue with the next url in the list. I’ll demo with 3 urls:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;open-uri&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">urls</span> <span class="o">=</span> <span class="sx">%w[ http://slashdot.org/ http://news.ycombinator.com/ http://www.techmeme.com/ ]</span>
</span></span><span class="line"><span class="cl"><span class="n">urls</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">url</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="k">begin</span>
</span></span><span class="line"><span class="cl">    <span class="n">doc</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">rescue</span>
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;The request for a page at </span><span class="si">#{</span><span class="n">url</span><span class="si">}</span><span class="s2"> timed out...skipping.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">next</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Now we safely output the offending error and continue with the next value in the urls array. That’s naace!</p>
<p>The truth is that this will not catch all exceptions. The problem is that when using the open-uri class you can receive an <code>OpenURI::Error</code> if the site you’re trying to access returns a 404, 403, 500 or other “not good” response, and you can receive a <code>Timeout::Error</code> if the site simply doesn’t respond at all and the request times out. Let’s modify our nugget to handle both scenarios:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;open-uri&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">urls</span> <span class="o">=</span> <span class="sx">%w[ http://slashdot.org/ http://news.ycombinator.com/ http://www.techmeme.com/ ]</span>
</span></span><span class="line"><span class="cl"><span class="n">urls</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">url</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="k">begin</span>
</span></span><span class="line"><span class="cl">    <span class="n">doc</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">rescue</span> <span class="no">Timeout</span><span class="o">::</span><span class="no">Error</span>
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;The request for a page at </span><span class="si">#{</span><span class="n">url</span><span class="si">}</span><span class="s2"> timed out...skipping.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">next</span>
</span></span><span class="line"><span class="cl">  <span class="k">rescue</span> <span class="no">OpenURI</span><span class="o">::</span><span class="no">Error</span> <span class="o">=&gt;</span> <span class="n">e</span>
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;The request for a page at </span><span class="si">#{</span><span class="n">url</span><span class="si">}</span><span class="s2"> returned an error. </span><span class="si">#{</span><span class="n">e</span><span class="o">.</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">next</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>You can use multiple <code>rescue</code> blocks to handle multiple exception classes. Now we should have a bullet-proof code nugget. See any chinks in the armor? Let me know in the comments.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Great jQuery Reference</title>
      <link>https://jerodsanto.net/2008/08/great-jquery-reference/</link>
      <pubDate>Thu, 14 Aug 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/08/great-jquery-reference/</guid>
      
      
      <description><![CDATA[<p>I don’t usually post links on this blog unless they accompany a tutorial, but this reference is so good I can’t help but throw it up here for others. Yes, I am linking to a list of links. How meta of me :P</p>
<p>If you are writing any <a href="http://jquery.com/">jQuery</a> whatsoever and want to polish your skills, check this:</p>
<p><a href="http://effectize.com/jquery-developer-guide">The Complete Guide For you to Become an Almighty jQuery Developer</a></p>]]></description>
      
      <content:encoded><![CDATA[<p>I don’t usually post links on this blog unless they accompany a tutorial, but this reference is so good I can’t help but throw it up here for others. Yes, I am linking to a list of links. How meta of me :P</p>
<p>If you are writing any <a href="http://jquery.com/">jQuery</a> whatsoever and want to polish your skills, check this:</p>
<p><a href="http://effectize.com/jquery-developer-guide">The Complete Guide For you to Become an Almighty jQuery Developer</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Debian Quickie: MailUtils</title>
      <link>https://jerodsanto.net/2008/08/debian-quickie-mailutils/</link>
      <pubDate>Tue, 12 Aug 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/08/debian-quickie-mailutils/</guid>
      
      
      <description><![CDATA[<p>All (except for one RHEL4 box) of the servers I run use <a href="http://debian.org/">Debian</a>. I have become accustomed to using the default mail client <code>/usr/bin/mail</code> that ships with the O/S for reading local email coming in from cron jobs.</p>
<p>Well, it turns out that this handy little tool doesn’t <em>actually</em> ship with a base Debian Etch install.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bash: mail: <span class="nb">command</span> not found
</span></span></code></pre></div><p>Grrrr!</p>
<p>Turns out you have to have mailutils installed to get <code>/usr/bin/mail</code>. I always forget this package name so I figured I’d post it here for easy access.</p>]]></description>
      
      <content:encoded><![CDATA[<p>All (except for one RHEL4 box) of the servers I run use <a href="http://debian.org/">Debian</a>. I have become accustomed to using the default mail client <code>/usr/bin/mail</code> that ships with the O/S for reading local email coming in from cron jobs.</p>
<p>Well, it turns out that this handy little tool doesn’t <em>actually</em> ship with a base Debian Etch install.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bash: mail: <span class="nb">command</span> not found
</span></span></code></pre></div><p>Grrrr!</p>
<p>Turns out you have to have mailutils installed to get <code>/usr/bin/mail</code>. I always forget this package name so I figured I’d post it here for easy access.</p>
<p>To install the necessary packages under <a href="http://debian.org/">Debian</a> just issue the following command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apt-get install mailutils
</span></span></code></pre></div><p>Enjoy the fresh maily goodness.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>jQuery Wizard Redux</title>
      <link>https://jerodsanto.net/2008/08/jquery-wizard-redux/</link>
      <pubDate>Sat, 09 Aug 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/08/jquery-wizard-redux/</guid>
      
      
      <description><![CDATA[<p>I was interested in creating a step-by-step form wizard for a Rails app I’ve been working on, so naturally I began searching for a jQuery plugin or tutorial. The best thing I could find was a ‘plugin’ written back in June of 2007 on <a href="http://worcesterwideweb.com/2007/06/04/jquery-wizard-plugin/">this blog</a>.</p>
<p>The <a href="http://worcesterwideweb.com/jquery/wizard/">demo</a> was quite attractive so I immediately began trying to work this implementation into my app. Upon reviewing the source code, I became a little less excited because the markup and styling left a lot to be desired. So instead of just complaining about it in the comments of the blog post (as had become my unfortunate custom), I figured I’d just re-write the thang until I was happy with it.</p>]]></description>
      
      <content:encoded><![CDATA[<p>I was interested in creating a step-by-step form wizard for a Rails app I’ve been working on, so naturally I began searching for a jQuery plugin or tutorial. The best thing I could find was a ‘plugin’ written back in June of 2007 on <a href="http://worcesterwideweb.com/2007/06/04/jquery-wizard-plugin/">this blog</a>.</p>
<p>The <a href="http://worcesterwideweb.com/jquery/wizard/">demo</a> was quite attractive so I immediately began trying to work this implementation into my app. Upon reviewing the source code, I became a little less excited because the markup and styling left a lot to be desired. So instead of just complaining about it in the comments of the blog post (as had become my unfortunate custom), I figured I’d just re-write the thang until I was happy with it.</p>
<p>A few of my redesign goals:</p>
<ul>
<li>Eliminate redundant step titles and descriptions</li>
<li>Remove unneeded styles from <a href="http://codylindley.com/CSS/325/css-step-menu">Cody Lindley’s CSS step menu</a></li>
<li>More jQuery, less HTML</li>
<li>Use valid markup</li>
<li>Reduce LOC dramatically</li>
<li>Comment code well to increase learning/understanding potential</li>
</ul>
<p>So How’d it turn out? I validated the markup, removed all redundancy, reduced the HTML required from 84 to 21 LOC, the CSS from 191 to 108, and made the JavaScript unobtrusive. As far as the commenting and quality of the JavaScript used, I’ll leave that for you to judge.</p>
<p>To see the wizard in action, follow <a href="http://jerodsanto.net/src/wizard/demo/">this link</a>. You can also download all the source files as a <a href="http://jerodsanto.net/src/wizard/wizard.zip">zip</a> or <a href="http://jerodsanto.net/src/wizard/wizard.tgz">tarball</a>.</p>
<p>Feel free to give suggestions in the comments section. Or do as I did and modify my version to make it even better!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Rails &#43; Screen = Awesome</title>
      <link>https://jerodsanto.net/2008/07/rails--screen-awesome/</link>
      <pubDate>Thu, 24 Jul 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/07/rails--screen-awesome/</guid>
      
      
      <description><![CDATA[<p>Unix’s <code>screen</code> utility is like pow-pow-power wheels for remote shells. I created a short screencast awhile back showing how to use screen to ease your Rails development.</p>
<p>The server that was hosted on is gonzo so I am reposting it here for your enjoyment:</p>
<p><a href="http://jerodsanto.net/embed/screen.swf">Link to Embedded Screencast</a></p>]]></description>
      
      <content:encoded><![CDATA[<p>Unix’s <code>screen</code> utility is like pow-pow-power wheels for remote shells. I created a short screencast awhile back showing how to use screen to ease your Rails development.</p>
<p>The server that was hosted on is gonzo so I am reposting it here for your enjoyment:</p>
<p><a href="http://jerodsanto.net/embed/screen.swf">Link to Embedded Screencast</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Pass Optional Arguments to Ruby Method</title>
      <link>https://jerodsanto.net/2008/07/pass-optional-arguments-to-ruby-method/</link>
      <pubDate>Thu, 24 Jul 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/07/pass-optional-arguments-to-ruby-method/</guid>
      
      
      <description><![CDATA[<p>This is the <em>Ruby way</em> of passing optional arguments with default values into a method:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">awesomeness</span> <span class="n">options</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">  <span class="c1">#sensible defaults</span>
</span></span><span class="line"><span class="cl">  <span class="n">opts</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="ss">:name</span>   <span class="o">=&gt;</span> <span class="s2">&#34;Jerod&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="ss">:handle</span> <span class="o">=&gt;</span> <span class="s2">&#34;jerodsanto&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="ss">:blog</span>   <span class="o">=&gt;</span> <span class="s2">&#34;Standard Deviations&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span><span class="o">.</span><span class="n">merge</span> <span class="n">options</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">opts</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">key</span><span class="p">,</span><span class="n">value</span><span class="o">|</span> <span class="nb">puts</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">key</span><span class="si">}</span><span class="s2"> = </span><span class="si">#{</span><span class="n">value</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>When called sans arguments this function will print the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">awesomeness</span>
</span></span><span class="line"><span class="cl"><span class="n">handle</span> <span class="o">=</span> <span class="n">jerodsanto</span>
</span></span><span class="line"><span class="cl"><span class="nb">name</span> <span class="o">=</span> <span class="no">Jerod</span>
</span></span><span class="line"><span class="cl"><span class="n">blog</span> <span class="o">=</span> <span class="no">Standard</span> <span class="no">Deviations</span>
</span></span></code></pre></div><p>When called with arguments this function will merge them into the opts variable and print the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">awesomeness</span> <span class="ss">:name</span> <span class="o">=&gt;</span> <span class="s2">&#34;Santo&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">handle</span> <span class="o">=</span> <span class="n">jerodsanto</span>
</span></span><span class="line"><span class="cl"><span class="nb">name</span> <span class="o">=</span> <span class="no">Santo</span>
</span></span><span class="line"><span class="cl"><span class="n">blog</span> <span class="o">=</span> <span class="no">Standard</span> <span class="no">Deviations</span>
</span></span></code></pre></div><p>The defaults are used unless you specify an override in the method call, in which case the override is merged into the opts hash.</p>]]></description>
      
      <content:encoded><![CDATA[<p>This is the <em>Ruby way</em> of passing optional arguments with default values into a method:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">awesomeness</span> <span class="n">options</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">  <span class="c1">#sensible defaults</span>
</span></span><span class="line"><span class="cl">  <span class="n">opts</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="ss">:name</span>   <span class="o">=&gt;</span> <span class="s2">&#34;Jerod&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="ss">:handle</span> <span class="o">=&gt;</span> <span class="s2">&#34;jerodsanto&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="ss">:blog</span>   <span class="o">=&gt;</span> <span class="s2">&#34;Standard Deviations&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span><span class="o">.</span><span class="n">merge</span> <span class="n">options</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">opts</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">key</span><span class="p">,</span><span class="n">value</span><span class="o">|</span> <span class="nb">puts</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">key</span><span class="si">}</span><span class="s2"> = </span><span class="si">#{</span><span class="n">value</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>When called sans arguments this function will print the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">awesomeness</span>
</span></span><span class="line"><span class="cl"><span class="n">handle</span> <span class="o">=</span> <span class="n">jerodsanto</span>
</span></span><span class="line"><span class="cl"><span class="nb">name</span> <span class="o">=</span> <span class="no">Jerod</span>
</span></span><span class="line"><span class="cl"><span class="n">blog</span> <span class="o">=</span> <span class="no">Standard</span> <span class="no">Deviations</span>
</span></span></code></pre></div><p>When called with arguments this function will merge them into the opts variable and print the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">awesomeness</span> <span class="ss">:name</span> <span class="o">=&gt;</span> <span class="s2">&#34;Santo&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">handle</span> <span class="o">=</span> <span class="n">jerodsanto</span>
</span></span><span class="line"><span class="cl"><span class="nb">name</span> <span class="o">=</span> <span class="no">Santo</span>
</span></span><span class="line"><span class="cl"><span class="n">blog</span> <span class="o">=</span> <span class="no">Standard</span> <span class="no">Deviations</span>
</span></span></code></pre></div><p>The defaults are used unless you specify an override in the method call, in which case the override is merged into the opts hash.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Symlink Your Samba Shares</title>
      <link>https://jerodsanto.net/2008/07/symlink-your-samba-shares/</link>
      <pubDate>Wed, 23 Jul 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/07/symlink-your-samba-shares/</guid>
      
      
      <description><![CDATA[<p>Lets face it, oftentimes a symbolic link is just the quickest/easiest solution to the task at hand.</p>
<p>To configure <a href="http://www.samba.org">Samba</a> to allow symlinking directories/files into your shared directories, add the following three lines to the global section of <code>smb.conf</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">follow <span class="nv">symlinks</span> <span class="o">=</span> yes
</span></span><span class="line"><span class="cl">wide <span class="nv">symlinks</span> <span class="o">=</span> yes
</span></span><span class="line"><span class="cl">unix <span class="nv">extensions</span> <span class="o">=</span> no
</span></span></code></pre></div><p>Easy peasy lemon squeezy.</p>]]></description>
      
      <content:encoded><![CDATA[<p>Lets face it, oftentimes a symbolic link is just the quickest/easiest solution to the task at hand.</p>
<p>To configure <a href="http://www.samba.org">Samba</a> to allow symlinking directories/files into your shared directories, add the following three lines to the global section of <code>smb.conf</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">follow <span class="nv">symlinks</span> <span class="o">=</span> yes
</span></span><span class="line"><span class="cl">wide <span class="nv">symlinks</span> <span class="o">=</span> yes
</span></span><span class="line"><span class="cl">unix <span class="nv">extensions</span> <span class="o">=</span> no
</span></span></code></pre></div><p>Easy peasy lemon squeezy.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>ip2loc</title>
      <link>https://jerodsanto.net/2008/07/ip2loc/</link>
      <pubDate>Sat, 19 Jul 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/07/ip2loc/</guid>
      
      
      <description><![CDATA[<p><a href="https://jerodsanto.net/wp-content/uploads/2008/07/ip2loc.png"><img class="aligncenter size-full wp-image-120" title="ip2loc" src="https://jerodsanto.net/wp-content/uploads/2008/07/ip2loc.png" height="211" alt="ip2loc" width="318" /></a></p>
<p><a href="http://ip2loc.jerodsanto.net/">ip2loc</a> is a simple tool which visualizes location data for a given IP address using the Google Maps API. I created it for a few reasons:</p>
<ol>
<li>Most sites that do this charge or are clunky</li>
<li>I wanted to write a simple application with <a href="http://sinatrarb.com/">Sinatra</a></li>
<li>It was a good excuse to sharpen my <a href="http://jquery.com/">jQuery</a> skills</li>
<li>Boredom</li>
</ol>
<p><a href="http://ip2loc.jerodsanto.net/">ip2loc</a> is powered by <a href="http://ruby-lang.org/">Ruby</a>, written with <a href="http://macromates.com/">TextMate</a>, and deployed with <a href="http://www.modrails.com/">Passenger</a> on <a href="http://www.dreamhost.com/r.cgi?143763">DreamHost</a>.</p>]]></description>
      
      <content:encoded><![CDATA[<p><a href="https://jerodsanto.net/wp-content/uploads/2008/07/ip2loc.png"><img class="aligncenter size-full wp-image-120" title="ip2loc" src="https://jerodsanto.net/wp-content/uploads/2008/07/ip2loc.png" height="211" alt="ip2loc" width="318" /></a></p>
<p><a href="http://ip2loc.jerodsanto.net/">ip2loc</a> is a simple tool which visualizes location data for a given IP address using the Google Maps API. I created it for a few reasons:</p>
<ol>
<li>Most sites that do this charge or are clunky</li>
<li>I wanted to write a simple application with <a href="http://sinatrarb.com/">Sinatra</a></li>
<li>It was a good excuse to sharpen my <a href="http://jquery.com/">jQuery</a> skills</li>
<li>Boredom</li>
</ol>
<p><a href="http://ip2loc.jerodsanto.net/">ip2loc</a> is powered by <a href="http://ruby-lang.org/">Ruby</a>, written with <a href="http://macromates.com/">TextMate</a>, and deployed with <a href="http://www.modrails.com/">Passenger</a> on <a href="http://www.dreamhost.com/r.cgi?143763">DreamHost</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>My Perfect OS X FTP Server</title>
      <link>https://jerodsanto.net/2008/07/my-perfect-os-x-ftp-server/</link>
      <pubDate>Thu, 17 Jul 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/07/my-perfect-os-x-ftp-server/</guid>
      
      
      <description><![CDATA[<p>I don’t like FTP. Reasons abound, but to list a couple:</p>
<ol>
<li>Too complex to configure</li>
<li>Insecure by default</li>
</ol>
<p>But lets face it, sometimes you need to set up a quick and dirty FTP server for one-time use. Sure, OS X supports FTP file sharing natively but its a bit clunky because you have to either A) allow Anonymous access, or B) create a user account on your system and set up sharing on it. Lame.</p>]]></description>
      
      <content:encoded><![CDATA[<p>I don’t like FTP. Reasons abound, but to list a couple:</p>
<ol>
<li>Too complex to configure</li>
<li>Insecure by default</li>
</ol>
<p>But lets face it, sometimes you need to set up a quick and dirty FTP server for one-time use. Sure, OS X supports FTP file sharing natively but its a bit clunky because you have to either A) allow Anonymous access, or B) create a user account on your system and set up sharing on it. Lame.</p>
<p>There are many freeware,and shareware FTP clients but not too many servers. After a cursory review of all the offerings on <a href="http://osx.iusethis.com">IUseThis</a>, I settled on <a href="http://jeanmatthieu.free.fr/pureftpd/">PureFTPd Manager</a> which is a free and open-source front-end for <a href="http://www.pureftpd.org/project/pure-ftpd">PureFTPd</a>…and what a great choice I made.</p>
<p>Why does this app rock my socks off?</p>
<ol>
<li>Dead simple configuration - nice GUI walks you through everything</li>
<li>Virtual users - one new system account and endless virtual users it can represent</li>
<li>Logs and live status updates from GUI</li>
<li>Uninstalls in a few clicks</li>
<li>SSL/TLS support</li>
</ol>
]]></content:encoded>
    </item>
    
    <item>
      <title>Traversing Directories with Ruby</title>
      <link>https://jerodsanto.net/2008/07/traversing-directories-with-ruby/</link>
      <pubDate>Tue, 15 Jul 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/07/traversing-directories-with-ruby/</guid>
      
      
      <description><![CDATA[<p>If you want to shove filenames of all files in a directory into an array, do:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># (absolute path)</span>
</span></span><span class="line"><span class="cl"><span class="n">files</span> <span class="o">=</span> <span class="no">Dir</span><span class="o">[</span><span class="s2">&#34;/Users/jerod/src/**&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (relative path)</span>
</span></span><span class="line"><span class="cl"><span class="n">files</span> <span class="o">=</span> <span class="no">Dir</span><span class="o">[</span><span class="no">File</span><span class="o">.</span><span class="n">expand_path</span><span class="p">(</span><span class="s2">&#34;~/src&#34;</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&#34;/**&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (in ENV[&#34;PWD&#34;], aka current directory)</span>
</span></span><span class="line"><span class="cl"><span class="n">files</span> <span class="o">=</span> <span class="no">Dir</span><span class="o">[</span><span class="s2">&#34;**&#34;</span><span class="o">]</span>
</span></span></code></pre></div><p>If you want to shove filenames of all files in a directory <strong>recursively</strong> into an array, do:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># (absolute path)</span>
</span></span><span class="line"><span class="cl"><span class="n">files</span> <span class="o">=</span> <span class="no">Dir</span><span class="o">[</span><span class="s2">&#34;/Users/jerod/src/**/**&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (relative path)</span>
</span></span><span class="line"><span class="cl"><span class="n">files</span> <span class="o">=</span> <span class="no">Dir</span><span class="o">[</span><span class="no">File</span><span class="o">.</span><span class="n">expand_path</span><span class="p">(</span><span class="s2">&#34;~/src&#34;</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&#34;/**/**&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (in ENV[&#34;PWD&#34;], aka current directory)</span>
</span></span><span class="line"><span class="cl"><span class="n">files</span> <span class="o">=</span> <span class="no">Dir</span><span class="o">[</span><span class="s2">&#34;**/**&#34;</span><span class="o">]</span>
</span></span></code></pre></div><p>It doesn’t get much easier than that.</p>]]></description>
      
      <content:encoded><![CDATA[<p>If you want to shove filenames of all files in a directory into an array, do:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># (absolute path)</span>
</span></span><span class="line"><span class="cl"><span class="n">files</span> <span class="o">=</span> <span class="no">Dir</span><span class="o">[</span><span class="s2">&#34;/Users/jerod/src/**&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (relative path)</span>
</span></span><span class="line"><span class="cl"><span class="n">files</span> <span class="o">=</span> <span class="no">Dir</span><span class="o">[</span><span class="no">File</span><span class="o">.</span><span class="n">expand_path</span><span class="p">(</span><span class="s2">&#34;~/src&#34;</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&#34;/**&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (in ENV[&#34;PWD&#34;], aka current directory)</span>
</span></span><span class="line"><span class="cl"><span class="n">files</span> <span class="o">=</span> <span class="no">Dir</span><span class="o">[</span><span class="s2">&#34;**&#34;</span><span class="o">]</span>
</span></span></code></pre></div><p>If you want to shove filenames of all files in a directory <strong>recursively</strong> into an array, do:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># (absolute path)</span>
</span></span><span class="line"><span class="cl"><span class="n">files</span> <span class="o">=</span> <span class="no">Dir</span><span class="o">[</span><span class="s2">&#34;/Users/jerod/src/**/**&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (relative path)</span>
</span></span><span class="line"><span class="cl"><span class="n">files</span> <span class="o">=</span> <span class="no">Dir</span><span class="o">[</span><span class="no">File</span><span class="o">.</span><span class="n">expand_path</span><span class="p">(</span><span class="s2">&#34;~/src&#34;</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&#34;/**/**&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># (in ENV[&#34;PWD&#34;], aka current directory)</span>
</span></span><span class="line"><span class="cl"><span class="n">files</span> <span class="o">=</span> <span class="no">Dir</span><span class="o">[</span><span class="s2">&#34;**/**&#34;</span><span class="o">]</span>
</span></span></code></pre></div><p>It doesn’t get much easier than that.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>New GitHub Repo: AWS Ruby Miscellany</title>
      <link>https://jerodsanto.net/2008/07/new-github-repo-aws-ruby-miscellany/</link>
      <pubDate>Mon, 14 Jul 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/07/new-github-repo-aws-ruby-miscellany/</guid>
      
      
      <description><![CDATA[<p>I decided to source out a few random Ruby scripts I use to interface with Amazon’s Simple Storage Service <a href="http://www.amazon.com/gp/browse.html?node=16427261">(S3)</a> from the command line. Right now there are scripts which allow:</p>
<ul>
<li>Duplicating a local folder in a specified S3 bucket (recursively)</li>
<li>Listing all buckets and files stored on an S3 account</li>
<li>Uploading a specified file to a specified S3 bucket</li>
<li>Downloading a specified file from a specified S3 bucket.</li>
</ul>
<p>I’m sure the functionality will grow as needs arise. For now, check out the <a href="http://github.com/jerodsanto/aws-ruby-miscellany/tree/master">repo on GitHub</a></p>]]></description>
      
      <content:encoded><![CDATA[<p>I decided to source out a few random Ruby scripts I use to interface with Amazon’s Simple Storage Service <a href="http://www.amazon.com/gp/browse.html?node=16427261">(S3)</a> from the command line. Right now there are scripts which allow:</p>
<ul>
<li>Duplicating a local folder in a specified S3 bucket (recursively)</li>
<li>Listing all buckets and files stored on an S3 account</li>
<li>Uploading a specified file to a specified S3 bucket</li>
<li>Downloading a specified file from a specified S3 bucket.</li>
</ul>
<p>I’m sure the functionality will grow as needs arise. For now, check out the <a href="http://github.com/jerodsanto/aws-ruby-miscellany/tree/master">repo on GitHub</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Capify - public key deployment</title>
      <link>https://jerodsanto.net/2008/07/capify-public-key-deployment/</link>
      <pubDate>Fri, 11 Jul 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/07/capify-public-key-deployment/</guid>
      
      
      <description><![CDATA[<p>I’ve been playing with <a href="http://capify.org/">Capistrano</a> a lot lately and loving it. Here is an example of how easy it is to write tasks and use them on multiple remote servers.</p>
<p>This task installs your SSH public key on the remote machine to allow key-based authentication:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">set</span> <span class="ss">:key_file</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Capistrano</span><span class="o">::</span><span class="no">CLI</span><span class="o">.</span><span class="n">ui</span><span class="o">.</span><span class="n">ask</span> <span class="s2">&#34;enter public key to push: &#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">desc</span> <span class="s2">&#34;configures key-based SSH administration&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">task</span> <span class="ss">:push_key</span><span class="p">,</span> <span class="ss">:roles</span>  <span class="o">=&gt;</span> <span class="ss">:all</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">key_location</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">expand_path</span><span class="p">(</span><span class="n">key_file</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">unless</span> <span class="no">File</span><span class="o">.</span><span class="n">exist?</span><span class="p">(</span><span class="n">key_location</span><span class="p">)</span> <span class="ow">and</span> <span class="n">key_file</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sr">/\.pub$/</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;Couldn&#39;t locate public key. Try again&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">key_file_name</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">key_location</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">upload</span> <span class="n">key_location</span><span class="p">,</span> <span class="s2">&#34;/tmp/</span><span class="si">#{</span><span class="n">key_file_name</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">run</span> <span class="s2">&#34;if [ ! -e ~/.ssh ];then mkdir ~/.ssh; fi&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">run</span> <span class="s2">&#34;cat /tmp/</span><span class="si">#{</span><span class="n">key_file_name</span><span class="si">}</span><span class="s2"> &gt;&gt; ~/.ssh/authorized_keys&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">run</span> <span class="s2">&#34;rm /tmp/</span><span class="si">#{</span><span class="n">key_file_name</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">run</span> <span class="s2">&#34;chmod 600 ~/.ssh/authorized_keys&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Silky smooth.</p>]]></description>
      
      <content:encoded><![CDATA[<p>I’ve been playing with <a href="http://capify.org/">Capistrano</a> a lot lately and loving it. Here is an example of how easy it is to write tasks and use them on multiple remote servers.</p>
<p>This task installs your SSH public key on the remote machine to allow key-based authentication:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">set</span> <span class="ss">:key_file</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Capistrano</span><span class="o">::</span><span class="no">CLI</span><span class="o">.</span><span class="n">ui</span><span class="o">.</span><span class="n">ask</span> <span class="s2">&#34;enter public key to push: &#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">desc</span> <span class="s2">&#34;configures key-based SSH administration&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">task</span> <span class="ss">:push_key</span><span class="p">,</span> <span class="ss">:roles</span>  <span class="o">=&gt;</span> <span class="ss">:all</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">key_location</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">expand_path</span><span class="p">(</span><span class="n">key_file</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">unless</span> <span class="no">File</span><span class="o">.</span><span class="n">exist?</span><span class="p">(</span><span class="n">key_location</span><span class="p">)</span> <span class="ow">and</span> <span class="n">key_file</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sr">/\.pub$/</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;Couldn&#39;t locate public key. Try again&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">key_file_name</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">key_location</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">upload</span> <span class="n">key_location</span><span class="p">,</span> <span class="s2">&#34;/tmp/</span><span class="si">#{</span><span class="n">key_file_name</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">run</span> <span class="s2">&#34;if [ ! -e ~/.ssh ];then mkdir ~/.ssh; fi&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">run</span> <span class="s2">&#34;cat /tmp/</span><span class="si">#{</span><span class="n">key_file_name</span><span class="si">}</span><span class="s2"> &gt;&gt; ~/.ssh/authorized_keys&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">run</span> <span class="s2">&#34;rm /tmp/</span><span class="si">#{</span><span class="n">key_file_name</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">run</span> <span class="s2">&#34;chmod 600 ~/.ssh/authorized_keys&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>Silky smooth.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>SliceHost &#43; Debian &#43; FireHOL gotchas</title>
      <link>https://jerodsanto.net/2008/06/slicehost--debian--firehol-gotchas/</link>
      <pubDate>Fri, 20 Jun 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/06/slicehost--debian--firehol-gotchas/</guid>
      
      
      <description><![CDATA[<p>Securing your <a href="http://www.slicehost.com">slice</a> with <a href="http://firehol.sourceforge.net">FireHOL</a> is a really, really good move. Here are a few notes that may save you some time:</p>
<p>FireHOL requires a kernel config to know which modules to load, SliceHost uses Xen, so to get the kernel configuration in the right place, execute the following commands:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">/proc/config.gz ~ <span class="o">&amp;&amp;</span> <span class="nb">cd</span> ~
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">gunzip config.gz <span class="o">&amp;&amp;</span> mv config /boot/config-<span class="sb">`</span>uname -r<span class="sb">`</span>
</span></span></code></pre></div><p>FireHOL ships with a safety net configured in <code>/etc/defaults/firehol</code>. It will not start until you edit this file and change this:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Securing your <a href="http://www.slicehost.com">slice</a> with <a href="http://firehol.sourceforge.net">FireHOL</a> is a really, really good move. Here are a few notes that may save you some time:</p>
<p>FireHOL requires a kernel config to know which modules to load, SliceHost uses Xen, so to get the kernel configuration in the right place, execute the following commands:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">/proc/config.gz ~ <span class="o">&amp;&amp;</span> <span class="nb">cd</span> ~
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">gunzip config.gz <span class="o">&amp;&amp;</span> mv config /boot/config-<span class="sb">`</span>uname -r<span class="sb">`</span>
</span></span></code></pre></div><p>FireHOL ships with a safety net configured in <code>/etc/defaults/firehol</code>. It will not start until you edit this file and change this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">START_FIREHOL</span><span class="o">=</span>NO
</span></span></code></pre></div><p>to this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">START_FIREHOL</span><span class="o">=</span>YES
</span></span></code></pre></div><p>That should do it for gotchas. Now you can lock down your machine to assure you’re only serving what you expect. Fore more on configuring FireHOL, check out their <a href="http://firehol.sourceforge.net/tutorial.html">online tutorial</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Rails: Nested Layouts</title>
      <link>https://jerodsanto.net/2008/06/rails-nested-layouts/</link>
      <pubDate>Fri, 20 Jun 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/06/rails-nested-layouts/</guid>
      
      
      <description><![CDATA[<p>Sometimes one layout (application.html.erb) just doesn’t cut it, but you don’t want a separate layout for each controller in your app. You can use the following technique to nest your Rails app’s layouts:</p>
<p>In this example, there are two controllers for a simple todo list manager; <code>lists</code> and <code>tasks</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1">#  application.html.erb</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="n">html</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="n">head</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="n">title</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">TODO</span> <span class="o">&gt;&gt;</span> <span class="o">&lt;</span><span class="s">%= yield(:title) || &#34;Get things done!&#34; %&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">    &lt;/title&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">    &lt;%=</span> <span class="n">stylesheet_link_tag</span> <span class="s1">&#39;todo&#39;</span> <span class="s">%&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">  &lt;/head&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="n">body</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="n">div</span> <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;container&#34;</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">&lt;</span><span class="nb">p</span> <span class="n">style</span><span class="o">=</span><span class="s2">&#34;color: green&#34;</span><span class="o">&gt;&lt;</span><span class="s">%= flash[:notice] %&gt;&lt;/p&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">      &lt;%=</span> <span class="k">yield</span> <span class="ss">:main</span> <span class="s">%&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">    &lt;/div&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="sr">/body&gt;
</span></span></span><span class="line"><span class="cl"><span class="sr">&lt;/</span><span class="n">html</span><span class="o">&gt;</span>
</span></span></code></pre></div><p>The key here is the <code>yield :main</code> which means we can use <code>content_for</code> to put our nested layouts’ output inside the yield. Here is the <code>lists</code> layout:</p>]]></description>
      
      <content:encoded><![CDATA[<p>Sometimes one layout (application.html.erb) just doesn’t cut it, but you don’t want a separate layout for each controller in your app. You can use the following technique to nest your Rails app’s layouts:</p>
<p>In this example, there are two controllers for a simple todo list manager; <code>lists</code> and <code>tasks</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1">#  application.html.erb</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="n">html</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="n">head</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="n">title</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">TODO</span> <span class="o">&gt;&gt;</span> <span class="o">&lt;</span><span class="s">%= yield(:title) || &#34;Get things done!&#34; %&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">    &lt;/title&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">    &lt;%=</span> <span class="n">stylesheet_link_tag</span> <span class="s1">&#39;todo&#39;</span> <span class="s">%&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">  &lt;/head&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="n">body</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="n">div</span> <span class="nb">id</span><span class="o">=</span><span class="s2">&#34;container&#34;</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">&lt;</span><span class="nb">p</span> <span class="n">style</span><span class="o">=</span><span class="s2">&#34;color: green&#34;</span><span class="o">&gt;&lt;</span><span class="s">%= flash[:notice] %&gt;&lt;/p&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">      &lt;%=</span> <span class="k">yield</span> <span class="ss">:main</span> <span class="s">%&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">    &lt;/div&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="sr">/body&gt;
</span></span></span><span class="line"><span class="cl"><span class="sr">&lt;/</span><span class="n">html</span><span class="o">&gt;</span>
</span></span></code></pre></div><p>The key here is the <code>yield :main</code> which means we can use <code>content_for</code> to put our nested layouts’ output inside the yield. Here is the <code>lists</code> layout:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># lists.html.erb</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="sx">% content_for </span> <span class="ss">:main</span> <span class="k">do</span> <span class="s">%&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">  &lt;div id=&#34;lists_container&#34;&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="s">%= yield %&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">  &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">&lt;% end %&gt;
</span></span></span><span class="line"><span class="cl"><span class="s">&lt;%=</span> <span class="n">render</span> <span class="ss">:file</span>  <span class="o">=&gt;</span> <span class="s2">&#34;layouts/application.html.erb&#34;</span> <span class="o">%&gt;</span>
</span></span></code></pre></div><p>Now your view gets rendered inside this layout’s <code>yield</code> and its all good in the hood. Cheers!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>htop: like top, but awesome</title>
      <link>https://jerodsanto.net/2008/06/htop-like-top-but-awesome/</link>
      <pubDate>Tue, 03 Jun 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/06/htop-like-top-but-awesome/</guid>
      
      
      <description><![CDATA[<p>If you’ve been hacking at your linux CLI for a little while, you’re probably familiar with the pic below. If not, just type <code>top</code> and your terminal will spring to life with an ever-updating process and usage display.</p>
<p><img src="https://jerodsanto.net/wp-content/uploads/2008/06/top.jpg" alt="top"></p>
<p>While very useful, <code>top</code>’s output is oogly. It’s the 21st century already! Let’s use some colors!!</p>
<p>Thankfully, <code>htop</code> is here to save the day. To install on a Debian-based linux, simply type:</p>]]></description>
      
      <content:encoded><![CDATA[<p>If you’ve been hacking at your linux CLI for a little while, you’re probably familiar with the pic below. If not, just type <code>top</code> and your terminal will spring to life with an ever-updating process and usage display.</p>
<p><img src="https://jerodsanto.net/wp-content/uploads/2008/06/top.jpg" alt="top"></p>
<p>While very useful, <code>top</code>’s output is oogly. It’s the 21st century already! Let’s use some colors!!</p>
<p>Thankfully, <code>htop</code> is here to save the day. To install on a Debian-based linux, simply type:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt-get install htop
</span></span></code></pre></div><p>now we should have <code>htop</code> installed. Let’s add a quick alias so we don’t accidentally launch <code>top</code> anymore. Open your <code>.bashrc</code> in your favorite editor and add the following code to it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -f /usr/bin/htop <span class="o">]</span><span class="p">;</span><span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="nb">alias</span> <span class="nv">top</span><span class="o">=</span><span class="s1">&#39;htop&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span></code></pre></div><p>Now whenever you start a terminal session and type <code>top</code> you’ll launch <code>htop</code> instead (but only if <code>htop</code> is installed on the system). Quickly tell bash to re-read your configuration file and try launching <code>htop</code></p>
<p><img src="https://jerodsanto.net/wp-content/uploads/2008/06/htop.jpg" alt="htop"></p>
<p>And there’s the <strong>awesome</strong></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>10.5.3 Fixes Spaces</title>
      <link>https://jerodsanto.net/2008/05/10.5.3-fixes-spaces/</link>
      <pubDate>Sat, 31 May 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/05/10.5.3-fixes-spaces/</guid>
      
      
      <description><![CDATA[<p>If you’re like me then one desktop just doesn’t cut it anymore. Leopard’s Spaces is Apple’s attempt at a virtual desktop management app and it has lacked one very crucial feature until the .3 release of the operating system which was pushed last week.</p>
<p>Its a difficult feature to explain but it basically allows your to divide your spaces by task instead of by application. I was dying for this fix because everytime I would switch to an application in a space that didn’t have it open already, I would be whisked away to another space that I did not want to be in.</p>]]></description>
      
      <content:encoded><![CDATA[<p>If you’re like me then one desktop just doesn’t cut it anymore. Leopard’s Spaces is Apple’s attempt at a virtual desktop management app and it has lacked one very crucial feature until the .3 release of the operating system which was pushed last week.</p>
<p>Its a difficult feature to explain but it basically allows your to divide your spaces by task instead of by application. I was dying for this fix because everytime I would switch to an application in a space that didn’t have it open already, I would be whisked away to another space that I did not want to be in.</p>
<p>To activate this feature just open the <strong>Exposé and Spaces</strong> preferences pane and unclick the following checkbox:</p>
<p>Read <a href="http://daringfireball.net/2008/05/spaces">Gruber’s post</a> on this for a more eloquent and thorough explanation.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>MySQL backup and restore</title>
      <link>https://jerodsanto.net/2008/05/mysql-backup-and-restore/</link>
      <pubDate>Mon, 19 May 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/05/mysql-backup-and-restore/</guid>
      
      
      <description><![CDATA[<h2 id="create-the-backup">Create the backup</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mysqldump --add-drop-table -h <span class="o">[</span>host<span class="o">]</span> -u <span class="o">[</span>user<span class="o">]</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">-p <span class="o">[</span>databasename<span class="o">]</span> &gt; <span class="o">[</span>backupfile<span class="o">]</span>.sql
</span></span></code></pre></div><p><strong>Optionally</strong></p>
<p>Specify a table -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"> ...<span class="o">[</span>databasename<span class="o">]</span> <span class="o">(</span>tablename tablename tablename<span class="o">)</span> ...
</span></span></code></pre></div><p>Add compression -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"> ...<span class="o">[</span>databasename<span class="o">]</span> <span class="p">|</span> bzip2 -c &gt; backupfile.sql.bz2
</span></span></code></pre></div><h2 id="restore-from-backup">Restore from backup</h2>
<p>Create database if it doesn&rsquo;t already exist (from inside mysql client)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mysql&gt; create database <span class="o">[</span>databasename<span class="o">]</span>
</span></span></code></pre></div><p>Run the restore</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mysql -h <span class="o">[</span>host<span class="o">]</span> -u <span class="o">[</span>user<span class="o">]</span> -p <span class="o">[</span>databasename<span class="o">]</span> &lt; <span class="o">[</span>backupfile<span class="o">]</span>.sql
</span></span></code></pre></div><p><strong>Optionally</strong></p>
<p>Uncompress before restoring -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bzip2 -d backupfile.sql.bz2
</span></span></code></pre></div>]]></description>
      
      <content:encoded><![CDATA[<h2 id="create-the-backup">Create the backup</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mysqldump --add-drop-table -h <span class="o">[</span>host<span class="o">]</span> -u <span class="o">[</span>user<span class="o">]</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">-p <span class="o">[</span>databasename<span class="o">]</span> &gt; <span class="o">[</span>backupfile<span class="o">]</span>.sql
</span></span></code></pre></div><p><strong>Optionally</strong></p>
<p>Specify a table -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"> ...<span class="o">[</span>databasename<span class="o">]</span> <span class="o">(</span>tablename tablename tablename<span class="o">)</span> ...
</span></span></code></pre></div><p>Add compression -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"> ...<span class="o">[</span>databasename<span class="o">]</span> <span class="p">|</span> bzip2 -c &gt; backupfile.sql.bz2
</span></span></code></pre></div><h2 id="restore-from-backup">Restore from backup</h2>
<p>Create database if it doesn&rsquo;t already exist (from inside mysql client)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mysql&gt; create database <span class="o">[</span>databasename<span class="o">]</span>
</span></span></code></pre></div><p>Run the restore</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mysql -h <span class="o">[</span>host<span class="o">]</span> -u <span class="o">[</span>user<span class="o">]</span> -p <span class="o">[</span>databasename<span class="o">]</span> &lt; <span class="o">[</span>backupfile<span class="o">]</span>.sql
</span></span></code></pre></div><p><strong>Optionally</strong></p>
<p>Uncompress before restoring -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bzip2 -d backupfile.sql.bz2
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>git clone &amp; pull without changing directories</title>
      <link>https://jerodsanto.net/2008/05/git-clone-pull-without-changing-directories/</link>
      <pubDate>Mon, 19 May 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/05/git-clone-pull-without-changing-directories/</guid>
      
      
      <description><![CDATA[<p>If you’re trying to configure git commands in a directory that isn’t <code>pwd</code>, you’ll have to deal with <code>clone</code> and <code>pull</code> a little differently. Clone works like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone <span class="o">[</span><span class="nb">source</span> location<span class="o">]</span> <span class="o">[</span>destination location<span class="o">]</span>
</span></span></code></pre></div><p>An example clone into my application&rsquo;s vendor directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">clone git://github.com/rails/rails.git ~/rails/myapp/vendor/rails
</span></span></code></pre></div><p>Pull works like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git-dir<span class="o">=</span>/path/to/destination/.git pull
</span></span></code></pre></div><p>An example pull in the already initiated local rails repository:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git-dir<span class="o">=</span>~/rails/myapp/vendor/rails/.git pull
</span></span></code></pre></div><p>Using these approaches, you can simplify your <a href="http://capify.org/">capistrano</a> recipes. here is an example snippet:</p>]]></description>
      
      <content:encoded><![CDATA[<p>If you’re trying to configure git commands in a directory that isn’t <code>pwd</code>, you’ll have to deal with <code>clone</code> and <code>pull</code> a little differently. Clone works like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone <span class="o">[</span><span class="nb">source</span> location<span class="o">]</span> <span class="o">[</span>destination location<span class="o">]</span>
</span></span></code></pre></div><p>An example clone into my application&rsquo;s vendor directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">clone git://github.com/rails/rails.git ~/rails/myapp/vendor/rails
</span></span></code></pre></div><p>Pull works like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git-dir<span class="o">=</span>/path/to/destination/.git pull
</span></span></code></pre></div><p>An example pull in the already initiated local rails repository:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git-dir<span class="o">=</span>~/rails/myapp/vendor/rails/.git pull
</span></span></code></pre></div><p>Using these approaches, you can simplify your <a href="http://capify.org/">capistrano</a> recipes. here is an example snippet:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">set</span> <span class="ss">:rails_source</span><span class="p">,</span> <span class="s2">&#34;git://github.com/rails/rails.git&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">desc</span> <span class="s2">&#34;git the latest rails&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">task</span> <span class="ss">:git_rails</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">run</span> <span class="s2">&#34;mkdir -p </span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">/vendor&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">result</span> <span class="o">=</span> <span class="n">run_and_return</span> <span class="s2">&#34;ls </span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">/vendor&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="n">result</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sr">/rails/</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">run</span> <span class="s2">&#34;git --git-dir=</span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">/vendor/rails/.git pull&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="n">run</span> <span class="s2">&#34;git clone </span><span class="si">#{</span><span class="n">rails_source</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">shared_path</span><span class="si">}</span><span class="s2">/vendor/rails&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>See <code>man git-clone</code> and <code>man git-pull</code> for more details.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Debian: Disable Sytem Beep</title>
      <link>https://jerodsanto.net/2008/05/debian-disable-sytem-beep/</link>
      <pubDate>Mon, 19 May 2008 00:00:00 &#43;0000</pubDate>
      
      <guid>https://jerodsanto.net/2008/05/debian-disable-sytem-beep/</guid>
      
      
      <description><![CDATA[<p>Here’s how to turn off that annoying system beep at your Debian CLI:</p>
<p>Just edit <code>/etc/modprobe.d/blacklist</code> and append this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">blacklist pcspkr
</span></span></code></pre></div><p>Then reboot. Can’t wait for reboot? type this command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo rmmod pcspkr
</span></span></code></pre></div>]]></description>
      
      <content:encoded><![CDATA[<p>Here’s how to turn off that annoying system beep at your Debian CLI:</p>
<p>Just edit <code>/etc/modprobe.d/blacklist</code> and append this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">blacklist pcspkr
</span></span></code></pre></div><p>Then reboot. Can’t wait for reboot? type this command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo rmmod pcspkr
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
  </channel>
</rss>
