Code-ChimpTales of coding and other geekery.https://code-chimp.orgSat, 04 Oct 2025 08:07:32 +0000Sat, 04 Oct 2025 08:07:32 +0000Hugo -- gohugo.ioen-usA Useful Caps Lock KeyFri, 26 Sep 2014 20:51:00 +0200https://code-chimp.org/2014/09/26/a-useful-caps-lock-key/https://code-chimp.org/2014/09/26/a-useful-caps-lock-key/<p>Caps Lock is such a useless - and at times annoying - key with such a prominent position that it just begs for a remap. If you happen to be a <a href="http://www.vim.org">vim</a> user, like I recently became, it&rsquo;s just the perfect place for an escape key.</p> <p>It&rsquo;s pretty easy to do with a little utility called <a href="https://pqrs.org/osx/karabiner/seil.html.en">Seil</a>. First we need to deactivate the the Caps Lock. In the <strong>System Preferences</strong> under <strong>Keyboard</strong> you click on <strong>Modifier Keys</strong> in the bottom right and set <strong>Caps Lock Key</strong> to <code>No Action</code>.</p> <p> <picture> <source srcset="https://code-chimp.org/images/keyboard_preferences.avif, https://code-chimp.org/images/keyboard_preferences@2x.avif 2x" type="image/avif"> <source srcset="https://code-chimp.org/images/keyboard_preferences.webp, https://code-chimp.org/images/keyboard_preferences@2x.webp 2x" type="image/webp"> <source srcset="https://code-chimp.org/images/keyboard_preferences.png, https://code-chimp.org/images/keyboard_preferences@2x.png 2x"> <img src="https://code-chimp.org/images/keyboard_preferences.png" alt="Keyboard Preferences" height="547" width="600" loading="lazy" decoding="async" /> </picture> </p> <p>Having <a href="https://pqrs.org/osx/karabiner/seil.html.en">Seil</a> installed and running we select the box next to <strong>Change Caps Lock</strong> and set the code to <strong>53</strong> for the escape key.</p> <p> <picture> <source srcset="https://code-chimp.org/images/seil.avif, https://code-chimp.org/images/seil@2x.avif 2x" type="image/avif"> <source srcset="https://code-chimp.org/images/seil.webp, https://code-chimp.org/images/seil@2x.webp 2x" type="image/webp"> <source srcset="https://code-chimp.org/images/seil.png, https://code-chimp.org/images/seil@2x.png 2x"> <img src="https://code-chimp.org/images/seil.png" alt="Seil" height="565" width="600" loading="lazy" decoding="async" /> </picture> </p> <p>And that&rsquo;s it!</p>CSS Columns and HyphenationFri, 14 Feb 2014 21:17:00 +0100https://code-chimp.org/2014/02/14/css-columns-and-hyphenation/https://code-chimp.org/2014/02/14/css-columns-and-hyphenation/<p>Whilst I generally do not condone making websites look like newspapers there are in fact some instances where columns with justification do look kind of nice<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and there are lots of people who seem to like it. Some time ago I had to do a site with columns at work and was delighted to learn columns and hyphenation can, in theory, be done with CSS alone. Unfortunately not all browser suppot those features yet. I&rsquo;ll first describe how one would do this with CSS alone and then we&rsquo;ll use JavaScript to help the older browsers out.</p> <h2 id="css-only">CSS Only</h2> <p>Let&rsquo;s for example take this piece of HTML:</p> <div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html"><span class="p">&lt;</span><span class="nt">p</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-justify hyphens three-columns&#34;</span><span class="p">&gt;</span><span class="c">&lt;!-- some text --&gt;</span><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> </code></pre></div><p>As you can see my paragraph has three CSS classes. The first one just justifies the text:</p> <div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">.</span><span class="nc">text-justify</span> <span class="p">{</span> <span class="k">text-align</span><span class="p">:</span> <span class="kc">justify</span> <span class="p">}</span> </code></pre></div><p>The next one makes sure there is hyphenation in as much browsers as possible:</p> <div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">.</span><span class="nc">hyphens</span> <span class="p">{</span> <span class="kp">-ms-</span><span class="k">word-break</span><span class="p">:</span> <span class="n">break-all</span><span class="p">;</span> <span class="k">word-break</span><span class="p">:</span> <span class="n">break-all</span><span class="p">;</span> <span class="kp">-webkit-</span><span class="k">hyphens</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span> <span class="kp">-moz-</span><span class="k">hyphens</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span> <span class="k">hyphens</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span> <span class="p">}</span> </code></pre></div><p>With the last one I tell the browser to segment the paragraph in three columns and define the gap between those columns:</p> <div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">.</span><span class="nc">three-columns</span> <span class="p">{</span> <span class="kp">-moz-</span><span class="k">column-count</span><span class="p">:</span> <span class="mi">3</span><span class="p">;</span> <span class="c">/* Firefox */</span> <span class="kp">-webkit-</span><span class="k">column-count</span><span class="p">:</span> <span class="mi">3</span><span class="p">;</span> <span class="c">/* Safari and Chrome */</span> <span class="k">column-count</span><span class="p">:</span> <span class="mi">3</span><span class="p">;</span> <span class="kp">-moz-</span><span class="k">column-gap</span><span class="p">:</span> <span class="mi">30</span><span class="kt">px</span><span class="p">;</span> <span class="c">/* Firefox */</span> <span class="kp">-webkit-</span><span class="k">column-gap</span><span class="p">:</span> <span class="mi">30</span><span class="kt">px</span><span class="p">;</span> <span class="c">/* Safari and Chrome */</span> <span class="k">column-gap</span><span class="p">:</span> <span class="mi">30</span><span class="kt">px</span><span class="p">;</span> <span class="p">}</span> </code></pre></div><p>In a perfect world this would be all one needed to do but Opera doesn&rsquo;t support hyphenation and IE9 or earlier neither supports columns nor hyphenation.</p> <h2 id="javascript-to-the-rescue">JavaScript to the Rescue</h2> <p>Let&rsquo;s start with the hyphenation. <a href="https://code.google.com/p/hyphenator/">Hyphenator.js</a> is a script that can do the hyphenation for you. It offers an <a href="http://hyphenator.googlecode.com/svn/tags/Version%203.3.0/mergeAndPack.html">mergeAndPack-Tool</a> where you can click together the languages and settings for you&rsquo;re site and get a minified script. To get it running with our HTML you have to specify <code>hyphens</code> as the classname under <em>Element Selection</em>. Now create the scrip, just copy and paste it into a new text-file and give it the name <code>hyphenate.js</code> and include it in your website:</p> <div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;hyphenate.js&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> </code></pre></div><p>That&rsquo;s pretty much all you have to do to get it running but I&rsquo;d do one more thing. Almost at the end of the script you can find the bit <code>Hyphenator.config({classname:'hyphens'});</code>. Change that to <code>Hyphenator.config({classname:'hyphens',useCSS3hyphenation:true});</code>. This tells the script to let the browser do the hyphenation via CSS if it is able, which is faster then doing it with JavaScript. With this in place you can safely remove the CSS for the class <code>hyphens</code>. The script will do that for you now.</p> <p>All that&rsquo;s left is the columns in IE9 or earlier. For that we&rsquo;ll use the jQuery plugin <a href="http://welcome.totheinter.net/columnizer-jquery-plugin/">Columnizer</a>. Since IE supports conditional comments, we&rsquo;ll use this to load the script only in the browsers we need to:</p> <div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html"><span class="c">&lt;!--[if lte IE 9]&gt; </span><span class="c">&lt;script src=&#34;jquery.columnizer.js&#34; type=&#34;text/javascript&#34;&gt;&lt;/script&gt; </span><span class="c">&lt;script&gt; </span><span class="c"> $(function(){ </span><span class="c"> $(&#39;.three-columns&#39;).columnize({columns: 3}); </span><span class="c"> }); </span><span class="c">&lt;/script&gt; </span><span class="c">&lt;![endif]--&gt;</span> </code></pre></div><p>With this only IE&rsquo;s that are 9 or earlier will load the content and all other browser will think it&rsquo;s just a comment.</p> <p>That&rsquo;s it. Now you should be set for pretty much all the recent browsers that are still being used.</p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>That&rsquo;s it. You won&rsquo;t here anything nicer from me about columns in the internet. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section>Customizing the DockMon, 27 Jan 2014 20:43:00 +0100https://code-chimp.org/2014/01/27/customizing-the-dock/https://code-chimp.org/2014/01/27/customizing-the-dock/<p>Between setting up a work iMac, buying a new MacBook and reinstalling my iMac after it&rsquo;s harddrive gave up, I&rsquo;ve set up quite a few Macs for me to work on lately. Without really thinking about it I&rsquo;ve always done the same thing when I was waiting for some development dependencies to install: pin my dock to the corner and add a divider to it. I find it makes the dock look so much cleaner and is easily done with a few Terminal commands.</p> <p> <picture> <source srcset="https://code-chimp.org/images/dock.avif, https://code-chimp.org/images/dock@2x.avif 2x" type="image/avif"> <source srcset="https://code-chimp.org/images/dock.webp, https://code-chimp.org/images/dock@2x.webp 2x" type="image/webp"> <source srcset="https://code-chimp.org/images/dock.jpg, https://code-chimp.org/images/dock@2x.jpg 2x"> <img src="https://code-chimp.org/images/dock.jpg" alt="Dock" height="164" width="700" loading="lazy" decoding="async" /> </picture> </p> <h2 id="put-your-dock-in-the-corner">Put Your Dock In The Corner</h2> <p>By default, the dock is pinned to the <em>middle</em> of either the bottom or the side - wherever you chose to put your dock. You can however pin it to the <em>start</em> or <em>end</em> with a simple Terminal command:</p> <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="c1"># Pin it to the start</span> defaults write com.apple.dock pinning -string start <span class="c1"># Pin it to the end</span> defaults write com.apple.dock pinning -string end <span class="c1"># Return it to the middle</span> defaults write com.apple.dock pinning -string middle </code></pre></div><p>Once you&rsquo;ve changed the setting just restart your dock with your Terminal:</p> <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="c1"># Reset the dock</span> killall Dock </code></pre></div><h2 id="dock-dividers">Dock Dividers</h2> <p>You can create dividers on the <em>left</em> or <em>right</em> side of your dock. Just type one of the following commands:</p> <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="c1"># Left side</span> defaults write com.apple.dock persistent-apps -array-add <span class="s1">&#39;{tile-data={}; tile-type=&#34;spacer-tile&#34;;}&#39;</span> <span class="c1"># Right Side</span> defaults write com.apple.dock persistent-others -array-add <span class="s1">&#39;{tile-data={}; tile-type=&#34;spacer-tile&#34;;}&#39;</span> </code></pre></div><p>You can add as many dividers as you like but you&rsquo;ll need to restart the dock again to see the changes:</p> <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="c1"># Reset the dock</span> killall Dock </code></pre></div><p>Once the dock is reset, you&rsquo;ll see dividers which you&rsquo;ll be able to drag throughout the dock. I mainly use it to seperate those apps I always keep in the dock from other currently opened apps.</p>Steal Bluetooth Keyboard With AlfredTue, 12 Nov 2013 20:04:00 +0100https://code-chimp.org/2013/11/12/steal-bluetooth-keyboard-with-alfred/https://code-chimp.org/2013/11/12/steal-bluetooth-keyboard-with-alfred/<p> <picture class="right"> <source srcset="https://code-chimp.org/images/bluetooth.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/bluetooth.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/bluetooth.png"> <img src="https://code-chimp.org/images/bluetooth.png" alt="BT Steal" height="128" width="128" loading="lazy" decoding="async" /> </picture> </p> <p>I recently bought one of the new Retina MacBook Pros and wanted to use my old 27&rdquo; iMac as display. I mean, it would be a sin to waste that big gorgeous screen, right? Fortunately my iMac is the first model to be able to be used in <a href="http://support.apple.com/kb/HT3924">Target Display Mode</a>. So I got a Mini-Display-Port cable and voilà, I now have a nice big display that doubles as a media server in the background, which makes the 250GB SSD in my rMBP more then big enough.</p> <p>Since I wanted the iMac to be usable when it&rsquo;s not used in TDM, this setup would require two sets of keyboards and trackpads, which was just a no-go for me. After a bit of googling I found exactly what I needed in this <a href="http://wherenow.org/imac_target_display_bluetooth_keyboard_mouse/">blog post</a>. Basically you use a script on your MB to toggle off the Bluetooth on the iMac and turn your MB&rsquo;s BT on to essentially steal the iMacs keyboard and trackpad. Before you leave with your MB, you do it the other way around and the BT devices can reconnect with the iMac. To get this running you first need to configure password-less ssh login between the two macs. But fortunately there a plenty <a href="http://osxdaily.com/2012/05/25/how-to-set-up-a-password-less-ssh-login/">instructions</a> to find. Then you need to fill in the your iMac&rsquo;s network name, which you can find under System Preferences -&gt; Sharing, in the ssh commands of the scripts and you are good to go.</p> <p> <picture> <source srcset="https://code-chimp.org/images/sharing.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/sharing.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/sharing.png"> <img src="https://code-chimp.org/images/sharing.png" alt="Network Name" height="556" width="676" loading="lazy" decoding="async" /> </picture> </p> <p>Even though I&rsquo;ve almost always a terminal window opened somewhere, I found it a bit tedious to trigger the scripts there. That&rsquo;s why I made an <a href="http://www.alfredapp.com">Alfred</a> workflow with them. Now I only have to type <code>steal keyboard</code> or <code>release keyboard</code> into Alfred. So much nicer.</p> <p><a href="https://code-chimp.org/downloads/BT-Steal.alfredworkflow">Download Alfred Workflow</a></p>XML Import in ActiveAdmin (Part 2)Sat, 13 Jul 2013 12:00:00 +0200https://code-chimp.org/2013/07/13/xml-import-in-activeadmin-part-2/https://code-chimp.org/2013/07/13/xml-import-in-activeadmin-part-2/<p>&hellip; or a journey to the wonderful world of <a href="http://en.wikipedia.org/wiki/Regular_expression">regular expressions</a>.</p> <p>In <a href="https://code-chimp.org/xml-import-in-activeadmin-part-1/">Part 1</a> I showed you how to build a simple XML import form in <a href="http://activeadmin.info">ActiveAdmin</a>. But what if our app isn&rsquo;t quite that simple as I described there. It may look something like this:</p> <p> <picture> <source srcset="https://code-chimp.org/images/podcast_erd.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/podcast_erd.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/podcast_erd.png"> <img src="https://code-chimp.org/images/podcast_erd.png" alt="Podcast ERD" height="230" width="493" loading="lazy" decoding="async" /> </picture> </p> <p>Since we have multiple podcasts I need a <code>podcast</code> model with name, description and artwork. The artwork columns were generated by <a href="https://github.com/thoughtbot/paperclip">Paperclip</a> and the slug is used by <a href="https://github.com/FriendlyId/friendly_id">FriendlyId</a>. The <code>episode</code> model is pretty self-explanatory, I think.</p> <h3 id="parse-podcast-episode--and-title">Parse Podcast, Episode # and Title</h3> <p>The XML I got from <a href="http://wordpress.org">Wordpress</a> looks something like this:</p> <div class="highlight"><pre class="chroma"><code class="language-xml" data-lang="xml"><span class="nt">&lt;channel&gt;</span> <span class="c">&lt;!-- Some stuff --&gt;</span> <span class="nt">&lt;item&gt;</span> <span class="nt">&lt;title&gt;</span>Celluloid 018: Guy Ritchie<span class="nt">&lt;/title&gt;</span> <span class="nt">&lt;link&gt;</span>http://www.talesofinterest.de/celluloid-018-guy-ritchie/<span class="nt">&lt;/link&gt;</span> <span class="nt">&lt;pubDate&gt;</span>Fri, 19 Oct 2012 07:48:18 +0000<span class="nt">&lt;/pubDate&gt;</span> <span class="nt">&lt;dc:creator&gt;</span>code-chimp<span class="nt">&lt;/dc:creator&gt;</span> <span class="nt">&lt;guid</span> <span class="na">isPermaLink=</span><span class="s">&#34;false&#34;</span><span class="nt">&gt;</span>http://www.talesofinterest.de/?p=745<span class="nt">&lt;/guid&gt;</span> <span class="nt">&lt;description&gt;&lt;/description&gt;</span> <span class="nt">&lt;content:encoded&gt;</span>A bunch of Important stuff<span class="nt">&lt;/content:encoded&gt;</span> <span class="c">&lt;!-- Some meta stuff --&gt;</span> <span class="nt">&lt;/item&gt;</span> <span class="c">&lt;!-- Some more items --&gt;</span> <span class="nt">&lt;/channel&gt;</span> </code></pre></div><p>We need to figure out how to get the podcast name, episode number and episode title out of this. The easy way would be, of course, if we had tagged the posts of each podcast with each own category. Unfortunately we didn&rsquo;t do that. But fortunately we titled our posts all following the same formula: <code>podcast_name episode_nr: episode_title</code>. So we can take the <code>title</code> and scan it with regular expressions<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> for our three components:</p> <p>For our podcast name we <code>scan</code> for any amount of characters which have a whitespace and three digits behind them.</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="n">str</span> <span class="o">=</span> <span class="s2">&#34;Celluloid 018: Guy Ritchie&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;Celluloid 018: Guy Ritchie&#34;</span> <span class="n">str</span><span class="o">.</span><span class="n">scan</span> <span class="sr">%r{ </span><span class="sr"> ( # start capture </span><span class="sr"> .+ # one or more of any single character </span><span class="sr"> ) # end caputure </span><span class="sr"> \s\d # followed by a whitespace and a digit </span><span class="sr">}x</span> <span class="o">=&gt;</span> <span class="o">[[</span><span class="s2">&#34;Celluloid&#34;</span><span class="o">]]</span> </code></pre></div><p>In short that would be:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="n">str</span><span class="o">.</span><span class="n">scan</span><span class="p">(</span><span class="sr">/(.+) \d+/</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="o">[[</span><span class="s2">&#34;Celluloid&#34;</span><span class="o">]]</span> </code></pre></div><p>And since <code>scan</code> returns an array and <code>capture</code> wraps another array around the matches, we have to specify which element of the array we want to get the string:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="n">str</span><span class="o">.</span><span class="n">scan</span><span class="p">(</span><span class="sr">/(.+) \d+/</span><span class="p">)</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="mi">0</span><span class="o">]</span> <span class="o">=&gt;</span> <span class="s2">&#34;Celluloid&#34;</span> </code></pre></div><p>For the episode number we only have to <code>scan</code> for the first match of three digits followed by a colon and turn the match into an integer.</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="n">str</span><span class="o">.</span><span class="n">scan</span><span class="p">(</span><span class="sr">/(\d+):/</span><span class="p">)</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="mi">0</span><span class="o">].</span><span class="n">to_i</span> <span class="o">=&gt;</span> <span class="mi">18</span> </code></pre></div><p>And for the episode title we <code>scan</code> for everything after a colon and a whitespace:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="n">str</span><span class="o">.</span><span class="n">scan</span><span class="p">(</span><span class="sr">/:\s(.+)/</span><span class="p">)</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="mi">0</span><span class="o">]</span> <span class="o">=&gt;</span> <span class="s2">&#34;Guy Ritchie&#34;</span> </code></pre></div><p>Including this into our <code>import_xml</code> action, it now looks like this:<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="n">collection_action</span> <span class="ss">:import_xml</span><span class="p">,</span> <span class="nb">method</span><span class="p">:</span> <span class="ss">:post</span> <span class="k">do</span> <span class="n">items</span> <span class="o">=</span> <span class="no">Nokogiri</span><span class="o">::</span><span class="no">XML</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:import</span><span class="o">][</span><span class="ss">:file</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">xpath</span><span class="p">(</span><span class="s2">&#34;//channel//item&#34;</span><span class="p">)</span> <span class="n">items</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span> <span class="n">podcast_name</span> <span class="o">=</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;title&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">scan</span><span class="p">(</span><span class="sr">/(.+) \d+/</span><span class="p">)</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="mi">0</span><span class="o">]</span> <span class="n">podcast</span> <span class="o">=</span> <span class="no">Podcast</span><span class="o">.</span><span class="n">find_by_name</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span> <span class="o">||</span> <span class="no">Podcast</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span> <span class="nb">name</span><span class="p">)</span> <span class="no">Episode</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="ss">podcast</span><span class="p">:</span> <span class="n">podcast</span><span class="p">,</span> <span class="ss">number</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;title&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">scan</span><span class="p">(</span><span class="sr">/\d+/</span><span class="p">)</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">to_i</span><span class="p">,</span> <span class="ss">title</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;title&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">scan</span><span class="p">(</span><span class="sr">/:\D(.+)/</span><span class="p">)</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="mi">0</span><span class="o">]</span><span class="p">,</span> <span class="ss">description</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;content:encoded&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="ss">created_at</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;pubDate&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">)</span> <span class="k">end</span> <span class="n">redirect_to</span> <span class="n">admin_podcasts_path</span><span class="p">,</span> <span class="ss">notice</span><span class="p">:</span> <span class="s2">&#34;Episodes imported successfully!&#34;</span> <span class="k">end</span> </code></pre></div><h3 id="converting-the-episode-description-to-markdown">Converting the Episode Description to Markdown</h3> <p>Like in this blog, I&rsquo;m doing all the writing on the podcast page in Markdown and while most Markdown parser will let HTML pass through, I&rsquo;d rather have all my podcast descriptions in the same format. I&rsquo;ve tried a few gems that do HTML to Markdown conversion and none of them were perfect for this use-case.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> The one getting closest was <a href="https://github.com/29decibel/html2markdown">html2markdown</a>, to which I made a few changes until I was satisfied. Let&rsquo;s add the gem to our Gemfile:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="n">gem</span> <span class="s1">&#39;html2markdown&#39;</span><span class="p">,</span> <span class="ss">git</span><span class="p">:</span> <span class="s1">&#39;git@github.com:coding-chimp/html2markdown.git&#39;</span> </code></pre></div><p>Run the <code>bundle install</code> command and use it in the import method:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="n">collection_action</span> <span class="ss">:import_xml</span><span class="p">,</span> <span class="nb">method</span><span class="p">:</span> <span class="ss">:post</span> <span class="k">do</span> <span class="n">items</span> <span class="o">=</span> <span class="no">Nokogiri</span><span class="o">::</span><span class="no">XML</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:import</span><span class="o">][</span><span class="ss">:file</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">xpath</span><span class="p">(</span><span class="s2">&#34;//channel//item&#34;</span><span class="p">)</span> <span class="n">items</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span> <span class="n">podcast_name</span> <span class="o">=</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;title&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">scan</span><span class="p">(</span><span class="sr">/(.+) \d+/</span><span class="p">)</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="mi">0</span><span class="o">]</span> <span class="n">podcast</span> <span class="o">=</span> <span class="no">Podcast</span><span class="o">.</span><span class="n">find_by_name</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span> <span class="o">||</span> <span class="no">Podcast</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span> <span class="nb">name</span><span class="p">)</span> <span class="n">description</span> <span class="o">=</span> <span class="no">HTMLPage</span><span class="o">.</span><span class="n">new</span> <span class="ss">:contents</span> <span class="o">=&gt;</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;content:encoded&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span> <span class="n">description</span> <span class="o">=</span> <span class="n">description</span><span class="o">.</span><span class="n">markdown</span> <span class="no">Episode</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="ss">podcast</span><span class="p">:</span> <span class="n">podcast</span><span class="p">,</span> <span class="ss">number</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;title&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">scan</span><span class="p">(</span><span class="sr">/\d+/</span><span class="p">)</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">to_i</span><span class="p">,</span> <span class="ss">title</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;title&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">scan</span><span class="p">(</span><span class="sr">/:\D(.+)/</span><span class="p">)</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="mi">0</span><span class="o">]</span><span class="p">,</span> <span class="ss">description</span><span class="p">:</span> <span class="n">description</span><span class="p">,</span> <span class="ss">created_at</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;pubDate&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">)</span> <span class="k">end</span> <span class="n">redirect_to</span> <span class="n">admin_podcasts_path</span><span class="p">,</span> <span class="ss">notice</span><span class="p">:</span> <span class="s2">&#34;Episodes imported successfully!&#34;</span> <span class="k">end</span> </code></pre></div><p>That&rsquo;s it. Pretty easy, wasn&rsquo;t it?</p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p><a href="http://rubular.com">Rubular</a> is a very cool site on which you can test your regular expressions. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>Due to the structure of my app, the form actually is in my podcasts index and not the episodes index as said in the last post. That doesn’t change the code, though. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:3" role="doc-endnote"> <p>It’s probably just because we’re not working with clean HTML here. <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section>The Seinfeld CalendarMon, 10 Jun 2013 12:00:00 +0200https://code-chimp.org/2013/06/10/the-seinfeld-calendar/https://code-chimp.org/2013/06/10/the-seinfeld-calendar/<p>Software Developer <a href="http://www.persistenceunlimited.com">Brad Isaac</a> wrote in 2007 on Lifehacker about <a href="http://lifehacker.com/281626/jerry-seinfelds-productivity-secret">Jerry Seinfeld&rsquo;s Productivity Secret</a>. Seinfeld told him the best way to write better jokes was to write every day. To maintain this habit, you buy yourself one of those big wall calendars that have the whole year printed on it and hang it on a prominent wall. Every day after doing some writing, you get to put a big red X over that day.</p> <blockquote> <p>&ldquo;After a few days you&rsquo;ll have a chain. Just keep at it and the chain will grow longer every day. You&rsquo;ll like seeing that chain, especially when you get a few weeks under your belt. Your only job next is to not break the chain.&rdquo;</p> </blockquote> <blockquote> <p>&ldquo;Don&rsquo;t break the chain.&rdquo;</p> </blockquote> <p> <picture> <source srcset="https://code-chimp.org/images/GitHub_Contributions_small_.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/GitHub_Contributions_small_.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/GitHub_Contributions_small_.png"> <img src="https://code-chimp.org/images/GitHub_Contributions_small_.png" alt="GitHub Contributions" height="213" width="500" loading="lazy" decoding="async" /> </picture> </p> <p>I read about this in january when <a href="https://github.com/blog/1360-introducing-contributions">GitHub introduced Contributions</a> and with it the Contributions Calendar, which pretty much follows this method. Every time you commit to a project&rsquo;s default branch or the gh-pages branch, open an issue, or propose a pull request it get&rsquo;s marked in the calendar. I especially like that they show the current and longest streak. Somehow seeing a streak of 0 days makes me even more bummed then seeing the cain broken.</p> <p>In the last few months I moved more and more stuff over to GitHub and even my workplace moved all their repositories over. Since literally all my code is now landing on GitHub, I&rsquo;ve seen this calendar having a big impact on me. In the last two months there were only three days I didn&rsquo;t commit any code.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p> <p>And because I like it so much, I of course added a GitHub Streak endpoint to my <a href="https://github.com/coding-chimp/status-board-widgets">status-board-widgets</a> app. Check it out.</p> <p> <picture> <source srcset="https://code-chimp.org/images/GitHub_Streak_small_.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/GitHub_Streak_small_.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/GitHub_Streak_small_.png"> <img src="https://code-chimp.org/images/GitHub_Streak_small_.png" alt="GitHub Streak on Status Board" height="227" width="225" loading="lazy" decoding="async" /> </picture> </p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>One of those days, I actually committed something to a not-master branch. I don’t really get, why they only count the master and gh-pages branches. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section>Website Monitoring With VigilMon, 27 May 2013 12:00:00 +0200https://code-chimp.org/2013/05/27/website-monitoring-with-vigil/https://code-chimp.org/2013/05/27/website-monitoring-with-vigil/<p>I read about <a href="http://vigil-app.com">Vigil</a> on the <a href="http://panic.com/statusboard/sources.html">Status Board Sources</a> page and am using it for about a week now. It&rsquo;s mainly an iPhone app that does two things: it monitors websites and sends a push notification if they are not responding. It has a free one-month trial and after that costs $9.99 a year for an unlimited number of websites. I subscribed after a few days because I really liked the app and was delighted to see the year I paid for will nonetheless begin after the one-month trial is over. Classy move.</p> <div class="left" style="margin-left:30px"> <p> <picture> <source srcset="https://code-chimp.org/images/vigil_screenshot_home_smaller_.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/vigil_screenshot_home_smaller_.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/vigil_screenshot_home_smaller_.png"> <img src="https://code-chimp.org/images/vigil_screenshot_home_smaller_.png" alt="Home" height="533" width="300" loading="lazy" decoding="async" /> </picture> </p> </div> <div class="right" style="margin-right:30px"> <p> <picture> <source srcset="https://code-chimp.org/images/vigil_screenshot_graph_smaller_.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/vigil_screenshot_graph_smaller_.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/vigil_screenshot_graph_smaller_.png"> <img src="https://code-chimp.org/images/vigil_screenshot_graph_smaller_.png" alt="Website Panel" height="533" width="300" loading="lazy" decoding="async" /> </picture> </p> </div> <div class="clearfix"></div> <p>On the home screen of the app is a list of all your websites with HTTP status codes and ping time. Tapping on a website a side panel will slide over and show some more detailed data like the download speed and a graph for the ping times of the last hour.</p> <p>And finally they even <a href="http://status.vigil-app.com">generate you a link</a> for a Status Board table showing the status codes, names, ping time and speed of all your websites.</p> <p> <picture> <source srcset="https://code-chimp.org/images/vigilStatusBoardScreenshot_smaller_.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/vigilStatusBoardScreenshot_smaller_.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/vigilStatusBoardScreenshot_smaller_.png"> <img src="https://code-chimp.org/images/vigilStatusBoardScreenshot_smaller_.png" alt="Standard Table" height="172" width="686" loading="lazy" decoding="async" /> </picture> </p> <p>As you can see, it uses the standard Status Board table layout, which I find humongous. So I added Vigil to my <a href="https://github.com/coding-chimp/status-board-widgets">Sinatra Status Board app</a> and made a table with a more reasonable size.</p> <p> <picture> <source srcset="https://code-chimp.org/images/vigil_status_board_widgets.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/vigil_status_board_widgets.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/vigil_status_board_widgets.png"> <img src="https://code-chimp.org/images/vigil_status_board_widgets.png" alt="Remodelled Table" height="205" width="529" loading="lazy" decoding="async" /> </picture> </p>Status Board WidgetsTue, 21 May 2013 12:00:00 +0200https://code-chimp.org/2013/05/21/status-board-widgets/https://code-chimp.org/2013/05/21/status-board-widgets/<p> <picture class="left"> <source srcset="https://code-chimp.org/images/StatusBoard.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/StatusBoard.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/StatusBoard.png"> <img src="https://code-chimp.org/images/StatusBoard.png" alt="Status Board" height="125" width="125" loading="lazy" decoding="async" /> </picture> </p> <p>As you may have noticed in the last two posts, I&rsquo;m really loving Panic&rsquo;s <a href="http://panic.com/statusboard/">Status Board</a>. The only drawback is I&rsquo;m not quite pleased with my setup and am always fiddling with it. Currently my Status Board is displaying the URI.LV Subscriber table I wrote about <a href="http://code-chimp.org/uri-dot-lv-status-board-panel/">earlier</a>, a table of my urgent tasks from <a href="http://culturedcode.com/things/">Things</a> as described in this <a href="http://blog.g-design.net/post/47693093316/integrating-things-with-panics-status-board">blog post</a> and a traffic graph of my <a href="https://gaug.es">gaug.es</a>. All of them were generated by scripts on my home computer, that write CSV or JSON files in my public <a href="https://www.dropbox.com">Dropbox</a> folder. My computer always needing to be on and scheduling the scripts or having to edit the script every time I wanted to see something slightly different was starting to bug me, which is why I decided to write a little <a href="http://www.sinatrarb.com">Sinatra</a> app to generate the data for my Status Board.</p> <p> <picture class="right"> <source srcset="https://code-chimp.org/images/gauges.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/gauges.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/gauges.png"> <img src="https://code-chimp.org/images/gauges.png" alt="Gaug.es" height="125" width="125" loading="lazy" decoding="async" /> </picture> </p> <p>The App is on <a href="https://github.com/coding-chimp/status-board-widgets">GitHub</a> and you can feel free to do with it whatever you like. How to set it up or use it, is described in the readme. As of now it has endpoints for traffic graphs of one or multiple gauges and graphs or tables for one or multiple feed statistics from <a href="http://uri.lv">URI.LV</a>. I&rsquo;m sure there will be more in the future, when I get new ideas for my Status Board. I would, of course, also be delighted, if you want to add a feature by yourself.</p>Simple Rails API for GeekTool and Status BoardWed, 08 May 2013 12:00:00 +0200https://code-chimp.org/2013/05/08/simple-rails-api-for-geektool-and-status-board/https://code-chimp.org/2013/05/08/simple-rails-api-for-geektool-and-status-board/<p> <picture class="right"> <source srcset="https://code-chimp.org/images/GeekTool_icon.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/GeekTool_icon.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/GeekTool_icon.png"> <img src="https://code-chimp.org/images/GeekTool_icon.png" alt="GeekTool" height="180" width="180" loading="lazy" decoding="async" /> </picture> </p> <p>Inspired by Brett Terpstra&rsquo;s series of <a href="http://brettterpstra.com/topic/geektool/">posts about GeekTool</a> I&rsquo;ve been using the <a href="http://www.iheartquotes.com/api">iheartquotes API</a> to display quotes on my desktop for quite some time now. So long, as a matter of fact, that I was starting to get bored by seeing the same quotes all the time. In comes this <a href="http://movietriviaoftheday.com">movie-trivia page</a> I made a long time ago. It&rsquo;s basically the first rails app I ever wrote, so really nothing special, but since I&rsquo;ve been posting those little titbits almost daily for over two years now, they amassed to quite a nice count. The ideal target for my Geeklet.</p> <p>I pretty much modelled the API for my page after the iheartquotes API, but since I&rsquo;m currently <a href="http://code-chimp.org/uri-dot-lv-status-board-panel/">playing with Status Board</a> I also added an API call for status board.</p> <p>We&rsquo;re starting by adding the needed routes and controller actions:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="no">MTotD</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 class="n">get</span> <span class="s1">&#39;api/random&#39;</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;trivia#random&#39;</span><span class="p">,</span> <span class="ss">defaults</span><span class="p">:</span> <span class="p">{</span> <span class="ss">:format</span> <span class="o">=&gt;</span> <span class="s1">&#39;text&#39;</span> <span class="p">}</span> <span class="n">get</span> <span class="s1">&#39;api/status_board&#39;</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;trivia#status_board&#39;</span> <span class="k">end</span> </code></pre></div><div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">TriviaController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span> <span class="k">def</span> <span class="nf">random</span> <span class="vi">@trivia</span> <span class="o">=</span> <span class="no">Trivia</span><span class="o">.</span><span class="n">published</span><span class="o">.</span><span class="n">order</span><span class="p">(</span><span class="s2">&#34;random()&#34;</span><span class="p">)</span> <span class="k">if</span> <span class="n">params</span><span class="o">[</span><span class="ss">:min_characters</span><span class="o">]</span> <span class="vi">@trivia</span> <span class="o">=</span> <span class="vi">@trivia</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="s2">&#34;length(body) &gt; ?&#34;</span><span class="p">,</span> <span class="n">params</span><span class="o">[</span><span class="ss">:min_characters</span><span class="o">]</span><span class="p">)</span> <span class="k">end</span> <span class="k">if</span> <span class="n">params</span><span class="o">[</span><span class="ss">:max_characters</span><span class="o">]</span> <span class="vi">@trivia</span> <span class="o">=</span> <span class="vi">@trivia</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="s2">&#34;? &gt; length(body)&#34;</span><span class="p">,</span> <span class="n">params</span><span class="o">[</span><span class="ss">:max_characters</span><span class="o">]</span><span class="p">)</span> <span class="k">end</span> <span class="k">if</span> <span class="n">params</span><span class="o">[</span><span class="ss">:min_lines</span><span class="o">]</span> <span class="vi">@trivia</span> <span class="o">=</span> <span class="vi">@trivia</span><span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">trivium</span><span class="o">|</span> <span class="n">word_wrap</span><span class="p">(</span><span class="n">trivium</span><span class="o">.</span><span class="n">body</span><span class="p">,</span> <span class="ss">:line_width</span> <span class="o">=&gt;</span> <span class="mi">70</span><span class="p">)</span><span class="o">.</span><span class="n">lines</span><span class="o">.</span><span class="n">count</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&gt;=</span> <span class="n">params</span><span class="o">[</span><span class="ss">:min_lines</span><span class="o">].</span><span class="n">to_i</span> <span class="p">}</span> <span class="k">end</span> <span class="k">if</span> <span class="n">params</span><span class="o">[</span><span class="ss">:max_lines</span><span class="o">]</span> <span class="vi">@trivia</span> <span class="o">=</span> <span class="vi">@trivia</span><span class="o">.</span><span class="n">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">trivium</span><span class="o">|</span> <span class="n">word_wrap</span><span class="p">(</span><span class="n">trivium</span><span class="o">.</span><span class="n">body</span><span class="p">,</span> <span class="ss">:line_width</span> <span class="o">=&gt;</span> <span class="mi">70</span><span class="p">)</span><span class="o">.</span><span class="n">lines</span><span class="o">.</span><span class="n">count</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&lt;=</span> <span class="n">params</span><span class="o">[</span><span class="ss">:max_lines</span><span class="o">].</span><span class="n">to_i</span> <span class="p">}</span> <span class="k">end</span> <span class="vi">@trivia</span> <span class="o">=</span> <span class="vi">@trivia</span><span class="o">.</span><span class="n">first</span> <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 class="nb">format</span><span class="o">.</span><span class="n">text</span> <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">layout</span><span class="p">:</span> <span class="kp">false</span> <span class="p">}</span> <span class="k">end</span> <span class="k">end</span> <span class="k">def</span> <span class="nf">status_board</span> <span class="n">render</span> <span class="ss">layout</span><span class="p">:</span> <span class="kp">false</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div><p>With the <code>random</code> action we are giving the user a few options to filter the trivia and are then loading a random one of the remaining. For the min_lines and max_lines filter we&rsquo;re calling <code>word_wrap</code> on our text, which inserts a line break every 70 characters.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> This is to ensure that every line has about the same length. The action defaults to text format because it&rsquo;s more useful for GeekTool, but can also be called as HTML, which we&rsquo;ll use for the Status Board API call. Furthermore we have to make sure that our two HTML views won&rsquo;t render the application layout, if we want them to look shiny in Status Board.</p> <p>Now we create our <code>random.text.erb</code> view:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="o">&lt;</span><span class="sx">% if </span> <span class="vi">@trivia</span><span class="o">.</span><span class="n">present?</span> <span class="s">%&gt; </span><span class="s"> &lt;%= raw word_wrap(@trivia.body.gsub(/\r\n\r\n/, &#34;\r\n&#34;).gsub(/[*,_]/, &#39;&#39;), :line_width =&gt;</span> <span class="mi">70</span><span class="p">)</span> <span class="s">%&gt; </span><span class="s"> — &lt;%= raw @trivia.film.title %&gt;</span> <span class="o">&lt;</span><span class="sx">% end </span> <span class="o">%&gt;</span> </code></pre></div><p>First we&rsquo;re making sure we&rsquo;re not calling the attributes of a nil object,<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> then we just word_wrap the trivia text as mentioned above and add a line with the film title. I&rsquo;m also removing empty lines and some Markdown markup.</p> <p>The <code>random.html.erb</code> view looks pretty similar:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="o">&lt;</span><span class="sx">% if </span> <span class="vi">@trivia</span><span class="o">.</span><span class="n">present?</span> <span class="s">%&gt; </span><span class="s"> &lt;div id=&#34;trivia&#34;&gt;</span> <span class="o">&lt;%=</span> <span class="n">markdown</span><span class="p">(</span><span class="vi">@trivia</span><span class="o">.</span><span class="n">body</span> <span class="o">+</span> <span class="s2">&#34; &lt;span&gt;— &#34;</span> <span class="o">+</span> <span class="vi">@trivia</span><span class="o">.</span><span class="n">film</span><span class="o">.</span><span class="n">title</span> <span class="o">+</span> <span class="s2">&#34;&lt;/span&gt;&#34;</span><span class="p">)</span> <span class="s">%&gt; </span><span class="s"> &lt;/div&gt;</span> <span class="o">&lt;</span><span class="sx">% end </span> <span class="o">%&gt;</span> </code></pre></div><p>The only difference here is, that I don&rsquo;t word_wrap the trivia, but instead call my <code>markdown</code> helper method to parse it to HTML.</p> <p>All that&rsquo;s left is the <code>status_board.html.erb</code> view. Since Status Board won&rsquo;t refresh our side automatically, we&rsquo;ll have to handle this ourselves:</p> <div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html"><span class="cp">&lt;!DOCTYPE HTML&gt;</span> <span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">http-equiv</span><span class="o">=</span><span class="s">&#34;Cache-control&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;no-cache&#34;</span> <span class="p">/&gt;</span> <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>Trivia<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">style</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/css&#34;</span><span class="p">&gt;</span> <span class="o">...</span> <span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;</span> <span class="kd">function</span> <span class="nx">refresh</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">req</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span> <span class="nx">req</span><span class="p">.</span><span class="nx">onreadystatechange</span><span class="o">=</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">req</span><span class="p">.</span><span class="nx">readyState</span><span class="o">==</span><span class="mi">4</span> <span class="o">&amp;&amp;</span> <span class="nx">req</span><span class="p">.</span><span class="nx">status</span><span class="o">==</span><span class="mi">200</span><span class="p">)</span> <span class="p">{</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;trivia-container&#39;</span><span class="p">).</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">responseText</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">req</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s2">&#34;GET&#34;</span><span class="p">,</span> <span class="s1">&#39;http://movietriviaoftheday.com/api/random.html?max_lines=6&#39;</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span> <span class="nx">req</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span> <span class="p">}</span> <span class="kd">function</span> <span class="nx">init</span><span class="p">()</span> <span class="p">{</span> <span class="nx">refresh</span><span class="p">()</span> <span class="kd">var</span> <span class="kr">int</span><span class="o">=</span><span class="nx">self</span><span class="p">.</span><span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span><span class="nx">refresh</span><span class="p">()},</span><span class="mi">600000</span><span class="p">);</span> <span class="p">}</span> <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">body</span> <span class="na">onload</span><span class="o">=</span><span class="s">&#34;init()&#34;</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;main&#34;</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;trivia-container&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span> </code></pre></div><p>So rather than loading a view with trivia in it, we load an empty page, replace the <code>#trivia-container</code> with the contents of our <code>random.html.erb</code> view via JavaScript and refresh them every 10 minutes.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p> <p>And that&rsquo;s our simple API.</p> <p>If you want to try a Geeklet with random trivia from my page just create a new shell Geeklet and use the following one-line command:</p> <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ curl -s <span class="s1">&#39;http://movietriviaoftheday.com/api/random?max_lines=6&#39;</span> </code></pre></div><p>Adjust the font and colour to your liking and the result could look like this:</p> <p> <picture> <source srcset="https://code-chimp.org/images/trivia_geeklet__thumb_.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/trivia_geeklet__thumb_.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/trivia_geeklet__thumb_.png"> <img src="https://code-chimp.org/images/trivia_geeklet__thumb_.png" alt="Trivia Geeklet" height="136" width="665" loading="lazy" decoding="async" /> </picture> </p> <p>On you Status Board you just need to create a new Do-It-Yourself panel and point it at:</p> <div class="highlight"><pre class="chroma"><code class="language-http" data-lang="http"><span class="err">http://movietriviaoftheday.com/api/status_board </span></code></pre></div><p>You can also check out the <a href="http://movietriviaoftheday.com/api">API Documentation</a>.</p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>Wraps the text into lines no longer than line_width width. This method breaks on the first whitespace character that does not exceed line_width (which is 80 by default). <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>Since @trivia could become nil duo to the filter options. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:3" role="doc-endnote"> <p>Or 600000 milliseconds. <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section>URI.LV Status Board PanelThu, 02 May 2013 12:00:00 +0200https://code-chimp.org/2013/05/02/uri-lv-status-board-panel/https://code-chimp.org/2013/05/02/uri-lv-status-board-panel/<p> <picture class="right"> <source srcset="https://code-chimp.org/images/uri.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/uri.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/uri.png"> <img src="https://code-chimp.org/images/uri.png" alt="URI.LV" height="72" width="200" loading="lazy" decoding="async" /> </picture> </p> <p>After my last invoice got paid I rewarded myself with <a href="http://panic.com/statusboard/">Panic&rsquo;s Status Board</a> and were thinking about, what I actually want to put on it. Since Google is sunsetting <a href="http://feedburner.google.com">Google Reader</a> in july, I lost my last bit of faith in <a href="http://feedburner.google.com">Feedburner</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, but since I like me some stats, I stumbled upon <a href="http://uri.lv">URI.LV</a> as an alternative. URI seems much less intrusive as Feedburner, is easier too use, has good documentation and a super simple API. They even generate a JSON file for every feed, which you could use in Status Board for a graph, but since I&rsquo;m not keen on having a panel for every feed, I took the DIY route.</p> <p>First we need to prepare URI so that we can use the API. Click on &lsquo;API&rsquo; in the navigation and then on &lsquo;New app&rsquo; in the sidebar. Having created the app we get an API key and can generate a token, which we&rsquo;ll both need.</p> <p>The return of the API call will look something like that:</p> <div class="highlight"><pre class="chroma"><code class="language-json" data-lang="json"><span class="p">{</span> <span class="nt">&#34;stats&#34;</span><span class="p">:</span> <span class="p">[</span> <span class="p">{</span> <span class="nt">&#34;day&#34;</span><span class="p">:</span> <span class="mi">1364425200</span><span class="p">,</span> <span class="nt">&#34;greader&#34;</span><span class="p">:</span> <span class="mi">742</span><span class="p">,</span> <span class="nt">&#34;other&#34;</span><span class="p">:</span> <span class="mi">13</span><span class="p">,</span> <span class="nt">&#34;direct&#34;</span><span class="p">:</span> <span class="mi">186</span><span class="p">,</span> <span class="nt">&#34;newsletter&#34;</span><span class="p">:</span> <span class="mi">21</span> <span class="p">},</span> <span class="p">],</span> <span class="nt">&#34;code&#34;</span><span class="p">:</span> <span class="mi">1</span> <span class="p">}</span> </code></pre></div><p>We now write a little ruby script, which will do all the heavy lifting:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="nb">require</span> <span class="s1">&#39;net/https&#39;</span> <span class="nb">require</span> <span class="s1">&#39;uri&#39;</span> <span class="nb">require</span> <span class="s1">&#39;json&#39;</span> <span class="nb">require</span> <span class="s1">&#39;csv&#39;</span> <span class="nb">require</span> <span class="s1">&#39;titleize&#39;</span> <span class="no">USERNAME</span> <span class="o">=</span> <span class="s1">&#39;Code-Chimp&#39;</span> <span class="no">API_KEY</span> <span class="o">=</span> <span class="s1">&#39;5b87661cd79e&#39;</span> <span class="no">API_TOKEN</span> <span class="o">=</span> <span class="s1">&#39;b0daafa44009&#39;</span> <span class="n">feeds</span> <span class="o">=</span> <span class="o">[</span><span class="s1">&#39;code-chimp&#39;</span><span class="p">,</span> <span class="s1">&#39;celluloid&#39;</span><span class="p">,</span> <span class="s1">&#39;recordcase&#39;</span><span class="p">,</span> <span class="s1">&#39;tales&#39;</span><span class="p">,</span> <span class="s1">&#39;escaping-to-neverland&#39;</span><span class="o">]</span> <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="s2">&#34;http://api.uri.lv/feeds/subscribers.json&#34;</span><span class="p">)</span> <span class="no">CSV</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s2">&#34;/Users/</span><span class="si">#{</span><span class="no">USERNAME</span><span class="si">}</span><span class="s2">/Dropbox/Public/uri_statusboard.csv&#34;</span><span class="p">,</span> <span class="s2">&#34;w&#34;</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">csv</span><span class="o">|</span> <span class="n">csv</span> <span class="o">&lt;&lt;</span> <span class="o">[</span><span class="s1">&#39;80%&#39;</span><span class="p">,</span> <span class="s1">&#39;20%&#39;</span><span class="o">]</span> <span class="n">feeds</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">feed</span><span class="o">|</span> <span class="n">params</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">:key</span> <span class="o">=&gt;</span> <span class="no">API_KEY</span><span class="p">,</span> <span class="ss">:token</span> <span class="o">=&gt;</span> <span class="no">API_TOKEN</span><span class="p">,</span> <span class="ss">:feed</span> <span class="o">=&gt;</span> <span class="n">feed</span> <span class="p">}</span> <span class="n">uri</span><span class="o">.</span><span class="n">query</span> <span class="o">=</span> <span class="no">URI</span><span class="o">.</span><span class="n">encode_www_form</span><span class="p">(</span><span class="n">params</span><span class="p">)</span> <span class="n">response</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">get_response</span><span class="p">(</span><span class="n">uri</span><span class="p">)</span> <span class="n">stats</span> <span class="o">=</span> <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="o">.</span><span class="n">body</span><span class="p">)</span><span class="o">[</span><span class="s1">&#39;stats&#39;</span><span class="o">].</span><span class="n">first</span> <span class="n">subscribers</span> <span class="o">=</span> <span class="n">stats</span><span class="o">[</span><span class="s1">&#39;greader&#39;</span><span class="o">]</span> <span class="o">+</span> <span class="n">stats</span><span class="o">[</span><span class="s1">&#39;other&#39;</span><span class="o">]</span> <span class="o">+</span> <span class="n">stats</span><span class="o">[</span><span class="s1">&#39;direct&#39;</span><span class="o">]</span> <span class="n">csv</span> <span class="o">&lt;&lt;</span> <span class="o">[</span><span class="n">feed</span><span class="o">.</span><span class="n">titleize</span><span class="p">,</span> <span class="n">subscribers</span><span class="o">]</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div><p>As you can see I first created an array with the URI feed names, then I opened an CSV file in my public Dropbox folder, made an API call for every feed, added the subscribers from the various sources together and wrote them in the CSV file. Notice that you&rsquo;ll have to fill in your own data for the USERNAME, API_KEY and API_TOKEN values.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p> <p>With this we can already load the data in the standard Status Board table. Just add a table to your Status Board and point it at the url of the csv file. All that is left to do is schedule the script to update the file. I just created a new job with <a href="http://www.peterborgapps.com/lingon/">Lingon</a> named it <code>org.code-chimp.uri_statusboard</code> and told it to run <code>/usr/bin/ruby /YOUR/PATH/uri_status_board.rb</code> every 20 minutes.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Of course you could also create a launchd job with in terminal.</p> <p>That&rsquo;s pretty much it. I personally found the table a bit big so I created a HTML file that loads the data from the CSV via JavaScript. Since I&rsquo;m not that good with JavaScript I more or less piggybacked on this <a href="http://blog.g-design.net/post/47693093316/integrating-things-with-panics-status-board">blog post by Gunther Groenwege</a> to load the CSV and this <a href="http://panic.com/statusboard/modules/spacepeople/index.html">example</a> by Panic to get the auto-refresh running.</p> <p>The finished product looks something like this in Status Board:</p> <p> <picture> <source srcset="https://code-chimp.org/images/uri_statusboard.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/uri_statusboard.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/uri_statusboard.png"> <img src="https://code-chimp.org/images/uri_statusboard.png" alt="URI Status Board Panel" height="321" width="318" loading="lazy" decoding="async" /> </picture> </p> <p>Feel free to use the <a href="https://code-chimp.org/downloads/uri_statusboard.html">file</a>.</p> <p>It&rsquo;s kind of nice to have some new feeds in there to see the subscriber count rise right from the beginning&hellip;<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p> <h3 id="update">Update</h3> <p><a href="https://alpha.app.net/maximevalette">@maximevalette</a><sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> told me on APN, that URI.LV&rsquo;s analytics refresh every day and there even is a countdown on the feed&rsquo;s dashboard.<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> So letting the script run every 20 minutes would be a bit redundant. Just check when the next update will be and schedule the script to run a couple of minutes after the update every day.</p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>Not that Google has done much with lately anyway… <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>Lines 7-9 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:3" role="doc-endnote"> <p>URI Premium refreshes the feed every 20 minutes. I’m not really sure how often the statistics are being updated. <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:4" role="doc-endnote"> <p>Or at least I hope they do. <a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:5" role="doc-endnote"> <p>The creator of URI.LV. <a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:6" role="doc-endnote"> <p>Yup, apperently I’m blind. <a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section>Responsive Design BookmarkletWed, 24 Apr 2013 12:00:00 +0200https://code-chimp.org/2013/04/24/responsive-design-bookmarklet/https://code-chimp.org/2013/04/24/responsive-design-bookmarklet/<p>Recently I heard about this <a href="https://web.archive.org/web/20170619001751/http://responsive.victorcoulon.fr">Responsive Design Bookmarklet</a> in <a href="http://brettterpstra.com">Brett Terpstra&rsquo;s</a> podcast <a href="http://5by5.tv/systematic/37">Systematic #37</a> and I since then I used it <strong>a lot</strong>.</p> <p> <picture class="left"> <source srcset="https://code-chimp.org/images/Responsive_Design_Bookmarklet__thumb_.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/Responsive_Design_Bookmarklet__thumb_.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/Responsive_Design_Bookmarklet__thumb_.png"> <img src="https://code-chimp.org/images/Responsive_Design_Bookmarklet__thumb_.png" alt="Responsive Design Bookmarklet" height="196" width="200" loading="lazy" decoding="async" /> </picture> </p> <p>When you click it, it basically adds a little header to the currently open page, which shows you the dimensions of your browser window and let&rsquo;s you simulate screen sizes of an iPad and iPhone.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> The only downside is that it still uses the old iPhone screen size, which isn&rsquo;t really a problem in portrait orientation but can be a bit misleading in landscape orientation.</p> <p>Since doing responsive design can be a bit of a bother, this bookmarklet made it a lot easier on me.</p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>portrait and landscape orientation <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section>Warmer Colors With f.luxTue, 22 Jan 2013 12:00:00 +0100https://code-chimp.org/2013/01/22/warmer-colors-with-f-lux/https://code-chimp.org/2013/01/22/warmer-colors-with-f-lux/<p> <picture class="right"> <source srcset="https://code-chimp.org/images/Flux_icon.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/Flux_icon.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/Flux_icon.png"> <img src="https://code-chimp.org/images/Flux_icon.png" alt="f.lux icon" height="100" width="100" loading="lazy" decoding="async" /> </picture> </p> <p>This is something I really couldn&rsquo;t live without anymore. <a href="http://stereopsis.com/flux/">f.lux</a> is a little utility app available for Mac, Windows, Linux and iOS<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> which makes the color of your display adapt to the time of the day. One hour before sunset it slowly begins to make the colors of your display warmer. It does that so smooth, that you don&rsquo;t even notice it&rsquo;s happening.</p> <p>On their website they are talking about how <a href="http://stereopsis.com/flux/research.html">blue light</a> can keep you up late, hence f.lux actually helps you sleep better. I don&rsquo;t really know about that, but I have definitely noticed my eyes being much less strained since using f.lux. Additionally, turning it off has has become a bit jarring by now. You should really try it out!</p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>if you’re into jailbreaking. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section>Easier Image Markup With RedcarpetSat, 12 Jan 2013 12:00:00 +0100https://code-chimp.org/2013/01/12/easier-image-markup-with-redcarpet/https://code-chimp.org/2013/01/12/easier-image-markup-with-redcarpet/<p>So my girlfriend wants to start a blog and being the awesome boyfriend I am, I offered to write it for her. Writing a simple blog in rails isn&rsquo;t that much work and having an easy to set up blog app lying around can&rsquo;t hurt, right? Today while trying to make posting as frictionless as possible I stumbled upon something really cool I wasn&rsquo;t really aware of before. I&rsquo;m kind of forcing my girlfriend to use <a href="http://daringfireball.net/projects/markdown/">Markdown</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and am using <a href="http://github.com/vmg/redcarpet">Redcarpet</a> to parse it. On the bottom of the post form I&rsquo;m enabling the user to upload pictures with <a href="https://github.com/thoughtbot/paperclip">Paperclip</a> she want&rsquo;s to use in the post but wasn&rsquo;t really sure how to make easy to embed them in the post content. In come custom Redcarpet renderer. They basically enable you to rewrite the methods used by Redcarpet and give them your own spin.</p> <p>But let&rsquo;s start from the beginning and add all the gems we need to the Gemfile:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="n">gem</span> <span class="s1">&#39;redcarpet&#39;</span> <span class="n">gem</span> <span class="s1">&#39;paperclip&#39;</span><span class="p">,</span> <span class="s1">&#39;~&gt; 3.0&#39;</span> <span class="n">gem</span> <span class="s1">&#39;paperclip-meta&#39;</span> </code></pre></div><p>Redcarpet and Paperclip should be clear and <a href="https://github.com/y8/paperclip-meta">Paperclip Meta</a> writes the dimension of the Paperclip styles in an extra column which makes them nicely accessible as you&rsquo;ll see later.</p> <p>Now let&rsquo;s create the models:</p> <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ rails g model Post title:string content:text $ rails g model Image name:string file:attachment file_meta:text post_id:integer </code></pre></div><p>And edit them:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Post</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span> <span class="n">attr_accessible</span> <span class="ss">:content</span><span class="p">,</span> <span class="ss">:title</span><span class="p">,</span> <span class="ss">:images_attributes</span> <span class="n">has_many</span> <span class="ss">:images</span><span class="p">,</span> <span class="ss">:dependent</span> <span class="o">=&gt;</span> <span class="ss">:destroy</span> <span class="n">accepts_nested_attributes_for</span> <span class="ss">:images</span><span class="p">,</span> <span class="ss">allow_destroy</span><span class="p">:</span> <span class="kp">true</span> <span class="k">end</span> </code></pre></div><div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Image</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span> <span class="n">attr_accessible</span> <span class="ss">:file</span><span class="p">,</span> <span class="ss">:post_id</span><span class="p">,</span> <span class="ss">:name</span> <span class="n">belongs_to</span> <span class="ss">:post</span> <span class="n">has_attached_file</span> <span class="ss">:file</span><span class="p">,</span> <span class="ss">:styles</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="ss">:thumb</span> <span class="o">=&gt;</span> <span class="s2">&#34;140&gt;x140&#34;</span> <span class="p">}</span> <span class="k">end</span> </code></pre></div><p>Now normally an image would like like this in Markdown:</p> <pre><code>![title](path/to/image &quot;alt text&quot;) </code></pre><p>I thought it would be cool if the user could give the image a name and I could use this name to generate the link. Furthermore it would be cool if the user could set a size and a class<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> for the image while still using Markdown. That could look something like this:</p> <pre><code>![image.title](thumb|right &quot;alt text&quot;) </code></pre><p>The size &ldquo;thumb&rdquo; of course meaning the Paperclip style thumb nad right being a html class, that floats the image to the right side.</p> <p>Now we&rsquo;ll create our custom Redcarpet renderer to process our new markup. Initially I had a helper method in the ApplicationHelper for Redcarpet:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="k">module</span> <span class="nn">ApplicationHelper</span> <span class="k">def</span> <span class="nf">markdown</span><span class="p">(</span><span class="n">text</span><span class="p">)</span> <span class="no">Redcarpet</span><span class="o">::</span><span class="no">Markdown</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Redcarpet</span><span class="o">::</span><span class="no">Render</span><span class="o">::</span><span class="no">HTML</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:hard_wrap</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">),</span> <span class="ss">:space_after_headers</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">:autolink</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">:strikethrough</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">:superscript</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">)</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">text</span><span class="p">)</span><span class="o">.</span><span class="n">html_safe</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div><p>But now I&rsquo;d rather put this and the custom renderer in it&rsquo;s own helper module.</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="k">module</span> <span class="nn">RedcarpetHelper</span> <span class="k">def</span> <span class="nf">markdown</span><span class="p">(</span><span class="n">text</span><span class="p">)</span> <span class="no">Redcarpet</span><span class="o">::</span><span class="no">Markdown</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">HTMLBlockCode</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:hard_wrap</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">),</span> <span class="ss">:space_after_headers</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">:autolink</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">:strikethrough</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">:superscript</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">)</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">text</span><span class="p">)</span><span class="o">.</span><span class="n">html_safe</span> <span class="k">end</span> <span class="k">end</span> <span class="k">class</span> <span class="nc">HTMLBlockCode</span> <span class="o">&lt;</span> <span class="no">Redcarpet</span><span class="o">::</span><span class="no">Render</span><span class="o">::</span><span class="no">HTML</span> <span class="kp">include</span> <span class="no">Sprockets</span><span class="o">::</span><span class="no">Helpers</span><span class="o">::</span><span class="no">RailsHelper</span> <span class="kp">include</span> <span class="no">Sprockets</span><span class="o">::</span><span class="no">Helpers</span><span class="o">::</span><span class="no">IsolatedHelper</span> <span class="kp">include</span> <span class="no">ActionView</span><span class="o">::</span><span class="no">Helpers</span><span class="o">::</span><span class="no">UrlHelper</span> <span class="k">def</span> <span class="nf">parse_media_link</span><span class="p">(</span><span class="n">link</span><span class="p">)</span> <span class="n">matches</span> <span class="o">=</span> <span class="n">link</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sr">/^(\w+)?\|([\w\s\d]+)?/</span><span class="p">)</span> <span class="p">{</span> <span class="ss">:size</span> <span class="o">=&gt;</span> <span class="p">(</span><span class="n">matches</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">||</span> <span class="s1">&#39;original&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">to_sym</span><span class="p">,</span> <span class="ss">:class</span> <span class="o">=&gt;</span> <span class="n">matches</span><span class="o">[</span><span class="mi">2</span><span class="o">]</span> <span class="p">}</span> <span class="k">if</span> <span class="n">matches</span> <span class="k">end</span> <span class="k">def</span> <span class="nf">image</span><span class="p">(</span><span class="n">link</span><span class="p">,</span> <span class="n">alt_text</span><span class="p">,</span> <span class="n">title</span><span class="p">)</span> <span class="n">size</span> <span class="o">=</span> <span class="kp">nil</span> <span class="n">klass</span> <span class="o">=</span> <span class="kp">nil</span> <span class="k">if</span> <span class="kp">nil</span> <span class="o">!=</span> <span class="p">(</span><span class="n">parse</span> <span class="o">=</span> <span class="n">parse_media_link</span><span class="p">(</span><span class="n">link</span><span class="p">))</span> <span class="n">image</span> <span class="o">=</span> <span class="no">Image</span><span class="o">.</span><span class="n">find_by_name</span><span class="p">(</span><span class="n">title</span><span class="p">)</span> <span class="k">if</span> <span class="n">image</span> <span class="n">size</span> <span class="o">=</span> <span class="n">image</span><span class="o">.</span><span class="n">file</span><span class="o">.</span><span class="n">image_size</span><span class="p">(</span><span class="n">parse</span><span class="o">[</span><span class="ss">:size</span><span class="o">]</span><span class="p">)</span> <span class="n">link</span> <span class="o">=</span> <span class="n">image</span><span class="o">.</span><span class="n">file</span><span class="o">.</span><span class="n">url</span><span class="p">(</span><span class="n">parse</span><span class="o">[</span><span class="ss">:size</span><span class="o">]</span><span class="p">)</span> <span class="n">klass</span> <span class="o">=</span> <span class="n">parse</span><span class="o">[</span><span class="ss">:class</span><span class="o">]</span> <span class="n">image_tag</span><span class="p">(</span><span class="n">link</span><span class="p">,</span> <span class="ss">:size</span> <span class="o">=&gt;</span> <span class="n">size</span><span class="p">,</span> <span class="ss">:title</span> <span class="o">=&gt;</span> <span class="n">title</span><span class="p">,</span> <span class="ss">:alt</span> <span class="o">=&gt;</span> <span class="n">alt_text</span><span class="p">,</span> <span class="ss">:class</span> <span class="o">=&gt;</span> <span class="n">klass</span><span class="p">)</span> <span class="k">else</span> <span class="s2">&#34;&#34;</span> <span class="k">end</span> <span class="k">end</span> <span class="k">end</span> <span class="k">def</span> <span class="nf">link</span><span class="p">(</span><span class="n">link</span><span class="p">,</span> <span class="n">title</span><span class="p">,</span> <span class="n">content</span><span class="p">)</span> <span class="n">klass</span> <span class="o">=</span> <span class="kp">nil</span> <span class="k">if</span> <span class="kp">nil</span> <span class="o">!=</span> <span class="p">(</span><span class="n">parse</span> <span class="o">=</span> <span class="n">parse_media_link</span><span class="p">(</span><span class="n">link</span><span class="p">))</span> <span class="n">image</span> <span class="o">=</span> <span class="no">Image</span><span class="o">.</span><span class="n">find_by_name</span><span class="p">(</span><span class="n">title</span><span class="p">)</span> <span class="k">if</span> <span class="n">image</span> <span class="n">link</span> <span class="o">=</span> <span class="n">image</span><span class="o">.</span><span class="n">file</span><span class="o">.</span><span class="n">url</span><span class="p">(</span><span class="n">parse</span><span class="o">[</span><span class="ss">:size</span><span class="o">]</span><span class="p">)</span> <span class="n">klass</span> <span class="o">=</span> <span class="n">parse</span><span class="o">[</span><span class="ss">:class</span><span class="o">]</span> <span class="n">link_to</span><span class="p">(</span><span class="n">content</span><span class="p">,</span> <span class="n">link</span><span class="p">,</span> <span class="ss">:title</span> <span class="o">=&gt;</span> <span class="n">title</span><span class="p">,</span> <span class="ss">:class</span> <span class="o">=&gt;</span> <span class="n">klass</span><span class="p">)</span> <span class="k">else</span> <span class="s2">&#34;&#34;</span> <span class="k">end</span> <span class="k">end</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div><p>Here we&rsquo;ve overwritten how Redcarpet generates an image_tag. First it&rsquo;s parsing the link for paperclip style<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> and class, than it searches the image by title and generates an image_tag with the url of the image file, the dimensions of the chosen style<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>, the title, alt text and class. Since our new renderer inherits from the existing renderer and only changes one method, we now can just exchange the <code>Redcarpet::Render::HTML</code> bit in the <code>markdown()</code> helper method with our renderer <code>HTMLBlockCode</code>and are done.</p> <p>Helpful links:</p> <ul> <li><a href="http://railscasts.com/episodes/272-markdown-with-redcarpet">Railscasts #272 Markdown with Redcarpet</a></li> <li><a href="http://railscasts.com/episodes/134-paperclip">Railscasts #134 Paperclip</a></li> </ul> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>I see it as an educational journey for her… not sure if she’ll see it the same way. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>For right and left float for instance. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:3" role="doc-endnote"> <p>or the original image size if left blank. <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:4" role="doc-endnote"> <p>Paperclip Meta makes it possible to get the size like this. <a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section>XML Import in ActiveAdmin (Part 1)Tue, 04 Dec 2012 12:00:00 +0100https://code-chimp.org/2012/12/04/xml-import-in-activeadmin-part-1/https://code-chimp.org/2012/12/04/xml-import-in-activeadmin-part-1/<p>I&rsquo;m currently working on a new home for the <a href="www.talesofinterest.de">Podcast</a> I&rsquo;m co-hosting. Since I won&rsquo;t be the only one posting on the page, Octopress isn&rsquo;t really a choice. So I&rsquo;m going with with <a href="http://rubyonrails.org">Rails</a>. The design is almost completely made with <a href="http://twitter.github.com/bootstrap/">Twitter Bootstrap</a>, Since design isn&rsquo;t my strong suit, and I&rsquo;m using <a href="http://activeadmin.info">ActiveAdmin</a> for the admin interface.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> I&rsquo;m thinking about making the finished app available for others, but at the moment I&rsquo;m not really sure how exactly that will be. More about that in the future.</p> <p>One of the troublesome things when moving a page is taking all the existing data with you. Our <a href="www.talesofinterest.de">current side</a> is running on <a href="http://wordpress.org">Wordpress</a>, which allows you to export your posts and pages as XML. In this post I&rsquo;ll explain how we will put two little forms in the admin interface with which we can choose a file to import from, parse the XML and create our pages or episodes. In the next post we&rsquo;ll see, if we can&rsquo;t improve the parsing a bit.</p> <h2 id="page-import-">Page Import</h2> <p>Let&rsquo;s start with the page import.</p> <p>To parse the XML we&rsquo;ll use <a href="http://nokogiri.org">Nokogiri</a>, which we need to add to our Gemfile:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="n">gem</span> <span class="s1">&#39;nokogiri&#39;</span> </code></pre></div><p>And run <code>bundle install</code>.</p> <p>Then we&rsquo;ll create the partial <code>app/views/admin/_import.html.erb</code> with the form we&rsquo;ll use to initiate the import. The form should call the import_xml action, which we&rsquo;ll write later, and should allow us to submit a file. Since <a href="https://github.com/justinfrench/formtastic">Formtastic</a> is already installed for ActiveAdmin, we might as well use it:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="o">&lt;</span><span class="s">%= semantic_form_for :import, :url =</span><span class="o">&gt;</span> <span class="p">{</span> <span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s2">&#34;import_xml&#34;</span> <span class="p">}</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="s">%&gt; </span><span class="s"> &lt;%= f.input :file, :as =&gt;</span> <span class="ss">:file</span><span class="p">,</span> <span class="ss">:label</span> <span class="o">=&gt;</span> <span class="kp">false</span> <span class="s">%&gt; </span><span class="s"> &lt;%= f.actions do %&gt;</span> <span class="o">&lt;</span><span class="s">%= f.action :submit, :as =</span><span class="o">&gt;</span> <span class="ss">:button</span> <span class="s">%&gt; </span><span class="s"> &lt;% end %&gt;</span> <span class="o">&lt;</span><span class="sx">% end </span> <span class="o">%&gt;</span> </code></pre></div><p>Then we render the partial in a sidebar on our pages admin index<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="n">sidebar</span> <span class="s2">&#34;Import&#34;</span><span class="p">,</span> <span class="ss">:only</span> <span class="o">=&gt;</span> <span class="ss">:index</span> <span class="k">do</span> <span class="n">simple_format</span><span class="p">(</span><span class="s2">&#34;Import pages from a XML file.&#34;</span><span class="p">)</span> <span class="o">+</span> <span class="n">render</span><span class="p">(</span><span class="s2">&#34;admin/import&#34;</span><span class="p">)</span> <span class="k">end</span> </code></pre></div><p>And now we need to write the action. The XML for your exported pages (or posts) you get from Wordpress looks something like this:</p> <div class="highlight"><pre class="chroma"><code class="language-xml" data-lang="xml"><span class="nt">&lt;channel&gt;</span> <span class="c">&lt;!-- Some stuff --&gt;</span> <span class="nt">&lt;item&gt;</span> <span class="nt">&lt;title&gt;</span>Impressum<span class="nt">&lt;/title&gt;</span> <span class="nt">&lt;link&gt;</span>http://www.talesofinterest.de/impressum/<span class="nt">&lt;/link&gt;</span> <span class="nt">&lt;pubDate&gt;</span>Wed, 22 Sep 2010 14:16:25 +0000<span class="nt">&lt;/pubDate&gt;</span> <span class="nt">&lt;dc:creator&gt;</span>code-chimp<span class="nt">&lt;/dc:creator&gt;</span> <span class="nt">&lt;guid</span> <span class="na">isPermaLink=</span><span class="s">&#34;false&#34;</span><span class="nt">&gt;</span>http://www.talesofinterest.de/<span class="nt">&lt;/guid&gt;</span> <span class="nt">&lt;description&gt;&lt;/description&gt;</span> <span class="nt">&lt;content:encoded&gt;</span>A bunch of Important stuff<span class="nt">&lt;/content:encoded&gt;</span> <span class="c">&lt;!-- Some meta stuff --&gt;</span> <span class="nt">&lt;/item&gt;</span> <span class="c">&lt;!-- Some more items --&gt;</span> <span class="nt">&lt;/channel&gt;</span> </code></pre></div><p>What we need to do is take the <code>title</code> and <code>content:encoded</code> of every <code>item</code>, create a new page with it and redirect to the pages admin index. In our <code>app/admin/pages.rb</code> we do the following:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="n">collection_action</span> <span class="ss">:import_xml</span><span class="p">,</span> <span class="ss">:method</span> <span class="o">=&gt;</span> <span class="ss">:post</span> <span class="k">do</span> <span class="n">items</span> <span class="o">=</span> <span class="no">Nokogiri</span><span class="o">::</span><span class="no">XML</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:import</span><span class="o">][</span><span class="ss">:file</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">xpath</span><span class="p">(</span><span class="s2">&#34;//channel//item&#34;</span><span class="p">)</span> <span class="n">items</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span> <span class="no">Page</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="ss">:title</span> <span class="o">=&gt;</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;title&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="ss">:content</span> <span class="o">=&gt;</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;content:encoded&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">)</span> <span class="k">end</span> <span class="n">redirect_to</span> <span class="n">admin_pages_path</span><span class="p">,</span> <span class="ss">:notice</span> <span class="o">=&gt;</span> <span class="s2">&#34;Pages imported successfully!&#34;</span> <span class="k">end</span> </code></pre></div><p>That&rsquo;s it for our page import.</p> <h2 id="episode-import">Episode Import</h2> <p>If you have only one podcast and modelled your episodes like a blog post, it&rsquo;s actually pretty straightforward, too.</p> <p>We&rsquo;ll be using the same partial but change the import_xml a bit, so that the <code>created_at</code> stamp of our episodes equals the <code>pubDate</code>. Let&rsquo;s do that in our <code>app/admin/episodes.rb</code>:</p> <div class="highlight"><pre class="chroma"><code class="language-ruby" data-lang="ruby"><span class="n">sidebar</span> <span class="s2">&#34;Import&#34;</span><span class="p">,</span> <span class="ss">:only</span> <span class="o">=&gt;</span> <span class="ss">:index</span> <span class="k">do</span> <span class="n">simple_format</span><span class="p">(</span><span class="s2">&#34;Import episodes from a XML file.&#34;</span><span class="p">)</span> <span class="o">+</span> <span class="n">render</span><span class="p">(</span><span class="s2">&#34;admin/import&#34;</span><span class="p">)</span> <span class="k">end</span> <span class="n">collection_action</span> <span class="ss">:import_xml</span><span class="p">,</span> <span class="ss">:method</span> <span class="o">=&gt;</span> <span class="ss">:post</span> <span class="k">do</span> <span class="n">items</span> <span class="o">=</span> <span class="no">Nokogiri</span><span class="o">::</span><span class="no">XML</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:import</span><span class="o">][</span><span class="ss">:file</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">xpath</span><span class="p">(</span><span class="s2">&#34;//channel//item&#34;</span><span class="p">)</span> <span class="n">items</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span> <span class="no">Episode</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="ss">:title</span> <span class="o">=&gt;</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;title&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="ss">:description</span> <span class="o">=&gt;</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;content:encoded&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="ss">:created_at</span> <span class="o">=&gt;</span> <span class="n">item</span><span class="o">.</span><span class="n">at_xpath</span><span class="p">(</span><span class="s2">&#34;pubDate&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">)</span> <span class="k">end</span> <span class="n">redirect_to</span> <span class="n">admin_podcasts_path</span><span class="p">,</span> <span class="ss">:notice</span> <span class="o">=&gt;</span> <span class="s2">&#34;Episodes imported successfully!&#34;</span> <span class="k">end</span> </code></pre></div><p>That is all.</p> <p>In the following post I will show you, how the episode import could look like, if you app is structured a bit more complex.</p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>Mainly because I used it a ton at work. <a href="https://github.com/sferik/rails_admin">RailsAdmin</a> does look really cool, too. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>Note that it won’t actually work yet. It will return an error because we haven’t written the import_xml action yet. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section>LiveReloadMon, 26 Nov 2012 13:00:00 +0100https://code-chimp.org/2012/11/26/live-reload/https://code-chimp.org/2012/11/26/live-reload/<p> <picture class="right"> <source srcset="https://code-chimp.org/images/LiveReload_icon.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/LiveReload_icon.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/LiveReload_icon.png"> <img src="https://code-chimp.org/images/LiveReload_icon.png" alt="LiveReload icon" height="200" width="200" loading="lazy" decoding="async" /> </picture> </p> <p>I&rsquo;m not even sure how exactly I came across this review from <a href="http://brettterpstra.com/app-review-livereload/">Brett Terpstra</a> about <a href="http://livereload.com">LiveReload</a> since it&rsquo;s almost a year old now, but it stuck with me. Being the poor little student that I am, it took me about a month to actually spent the 8,99€ (US $9.99) for LifeReload, but boy, it is worth it.</p> <p><strong>LiveReload is</strong> a tool for web designers, which reloads your web browser(s) if any change on the site occurs. Just point LiveReload at the site&rsquo;s folder and install the <a href="http://feedback.livereload.com/knowledgebase/articles/86242-how-do-i-install-and-use-the-browser-extensions">browser extension</a> or add a JavaScript snippet to the site to get it working. Sure, editors like <a href="http://panic.com/coda/">Coda</a> or <a href="http://www.macrabbit.com/espresso/">Espresso</a> got that feature build in<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> but they are also a bit more costly. LiveReload brings it to <em>every</em> editor you already own.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p> <p>While it&rsquo;s easier to go with the browser extension, choosing the JavaScript snippet can save you a bunch of time if you&rsquo;re doing anything with responsive design. Let the site run on an apache web server on a virtual host<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, visit it with the browsers of your computer and all mobile devices and you can instantly see how your site behaves on all those devices without having to reload a single page.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> When I fiddled with this site I had it loaded up on my iMac, iPhone and iPad. It worked flawlessly and was really <em>cool</em>.</p> <p>Additionally LiveReload can compile SASS, LESS, CoffeScript and others<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> or run any shell command after changes occurred and before the browser refreshes. This setup certainly bears endless possibilities.</p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>and it is probably a bit faster as well. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>or will own. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:3" role="doc-endnote"> <p>If you don’t know how to do that, I’d recommend you take a look at <a href="http://www.mamp.info/en/index.html">MAMP</a>. Point MAMP at the folder with the site, start the server and the the site is reachable from your network. <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:4" role="doc-endnote"> <p>The code you get from the app works only if your site is running on your local computer. If it’s running on another server, you need to <a href="http://feedback.livereload.com/knowledgebase/articles/86232-how-do-i-use-livereload-on-iphone-ipad-android-etc">specify the IP of your local computer</a>. <a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:5" role="doc-endnote"> <p>Eco, HAML, IcedCoffeeScript, Jade, Compass, Slim &amp; Stylus. <a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section>Pow and AnvilMon, 26 Nov 2012 12:00:00 +0100https://code-chimp.org/2012/11/26/pow-and-anvil/https://code-chimp.org/2012/11/26/pow-and-anvil/<p> <picture class="left"> <source srcset="https://code-chimp.org/images/logo_pow.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/logo_pow.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/logo_pow.png"> <img src="https://code-chimp.org/images/logo_pow.png" alt="Pow logo" height="110" width="150" loading="lazy" decoding="async" /> </picture> </p> <p><a href="http://pow.cx/">Pow</a> is a zero-config and super easy to setup rack server for Mac developed by <a href="http://37signals.com/">37signals</a>. Besides being easy to use, it enables you to have multiple Rails apps<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> running simultaneously, without having to remember all those pesky ports, by giving the app a hostname based on a symlink you chose and a .dev extension.</p> <p> <picture class="right"> <source srcset="https://code-chimp.org/images/Anvil_icon.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/Anvil_icon.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/Anvil_icon.png"> <img src="https://code-chimp.org/images/Anvil_icon.png" alt="Anvil icon" height="50" width="50" loading="lazy" decoding="async" /> </picture> </p> <p>And <a href="http://anvilformac.com/">Anvil</a> makes Pow even easier to use. Just open it up from your menubar, point it at a folder, type in a name and the pow server is running.</p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>Or static HTML sites like your octopress blog. ;) <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section>Footnotes in OctopressThu, 22 Nov 2012 12:00:00 +0100https://code-chimp.org/2012/11/22/footnotes-in-octopress/https://code-chimp.org/2012/11/22/footnotes-in-octopress/<p>Today I went down a little rabbit hole. I wanted to be able to include footnotes in my posts. And though I promptly found the <a href="http://github.com/fmcypriano/footnote-octopress">footnote-octopress</a> plugin to do just that, that wasn&rsquo;t enough for me. I like footnotes, but I don&rsquo;t really want to scroll down on longer posts just to read them. That just takes you out of the reading experience. What I wanted was a JavaScript that generates a popup on mouseover. After a bit of digging I found just that on <a href="http://ignorethecode.net/blog/2010/04/20/footnotes/">Lukas Mathis&rsquo; blog</a>. Unfortunately it took me some time to get it working.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> The problem was that the required <a href="http://jquery.com">jQuery</a> didn&rsquo;t get along with <a href="http://ender.jit.su">Ender</a>, which is used quite a bit in the classic Octopress theme.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> The solution was to override the <code>$</code>-function for jQuery with</p> <div class="highlight"><pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="nx">jQuery</span><span class="p">.</span><span class="nx">noConflict</span><span class="p">()</span> </code></pre></div><p>and exchange all the <code>$</code> with <code>jQuery</code>.</p> <p>I forked the plugin and added the script if you want to give it a try. Check it out on <a href="https://github.com/coding-chimp/footnote-octopress">Github</a>.</p> <p><del>As I was writing this I noticed the markdown syntax isn&rsquo;t working in the footnotes. I&rsquo;ve got to check into that sometime&hellip;</del> - Done.</p> <section class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1" role="doc-endnote"> <p>I <em>really</em> should learn to write JavaScript myself… or at least understand it better. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>Yep, I don’t use the classic theme, but my sidebar is <em>borrowed</em> from it. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </section>Blogging With OctopressMon, 12 Nov 2012 12:00:00 +0100https://code-chimp.org/2012/11/12/blogging-with-octopress/https://code-chimp.org/2012/11/12/blogging-with-octopress/<p> <picture class="right"> <source srcset="https://code-chimp.org/images/octopress.avif" type="image/avif"> <source srcset="https://code-chimp.org/images/octopress.webp" type="image/webp"> <source srcset="https://code-chimp.org/images/octopress.png"> <img src="https://code-chimp.org/images/octopress.png" alt="Octopress logo" height="180" width="180" loading="lazy" decoding="async" /> </picture> </p> <p>While blogging platforms like <a href="http://www.squarespace.com">Squarespace</a> are awesome, they carry a lot baggage. I don&rsquo;t need all the fancy plugins, user management or a database which slows the whole site down and makes it prone to getting <a href="http://23x.net/216/anatomy-of-a-fireballing.html">Fireballed</a>. That&rsquo;s pretty much why I&rsquo;m taking the route many developers seem to be taking lately. <a href="http://pages.github.com">Github Pages</a> and <a href="https://github.com/mojombo/jekyll">Jekyll</a>/<a href="https://github.com/imathis/octopress">Octopress</a>.</p> <h3 id="what-is-jekyll-and-octopress">What is Jekyll and Octopress?</h3> <p>I&rsquo;ll just let the creators explain it themselves:</p> <blockquote> <p>Jekyll is a simple, blog aware, static site generator. It takes a template directory (representing the raw form of a website), runs it through Textile or Markdown and Liquid converters, and spits out a complete, static website suitable for serving with Apache or your favorite web server. This is also the engine behind GitHub Pages, which you can use to host your project’s page or blog right here from GitHub. &ndash; <a href="https://github.com/mojombo/jekyll/wiki">Jekyll wiki</a></p> </blockquote> <blockquote> <p>Octopress is a framework designed by <a href="http://brandonmathis.com">Brandon Mathis</a> for Jekyll [&hellip;]. To start blogging with Jekyll, you have to write your own HTML templates, CSS, Javascripts and set up your configuration. But with Octopress All of that is already taken care of. Simply clone or fork Octopress, install dependencies and the theme, and you’re set. &ndash; <a href="https://github.com/imathis/octopress">Octopress</a></p> </blockquote> <h3 id="first-impression">First Impression</h3> <p>OK, I&rsquo;m admittedly not that far in the game yet, but it looks really slick thus far. <a href="http://octopress.org/docs/setup/">Setting it up</a> was really easy and there are some really nice looking <a href="https://github.com/imathis/octopress/wiki/3rd-Party-Octopress-Themes">3rd party themes</a>. Plus it is mainly written in Ruby, my language of choice, which enables me to fiddle with it, if needed. If you&rsquo;re more comfortable in PHP you might want to check out Marco Arment&rsquo;s <a href="https://github.com/marcoarment/secondcrack">SecondCrack</a>.</p>