<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Byte Friendly]]></title>
  <link href="http://tech.tulentsev.com/atom.xml" rel="self"/>
  <link href="http://tech.tulentsev.com/"/>
  <updated>2019-02-08T12:33:24+03:00</updated>
  <id>http://tech.tulentsev.com/</id>
  <author>
    <name><![CDATA[Sergio Tulentsev]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Running calculator in Javascript]]></title>
    <link href="http://tech.tulentsev.com/2016/08/running-calculator-in-javascript/"/>
    <updated>2016-08-26T14:23:00+03:00</updated>
    <id>http://tech.tulentsev.com/2016/08/running-calculator-in-javascript</id>
    <content type="html"><![CDATA[<p>I&#8217;m into running lately. So quite often I do these calculations in my head &#8220;if I ran 10 secs/km faster, what distance would I have covered?&#8221; As the calculation are quite difficult and I am quite lazy, here&#8217;s a calculator that answers most of my questions. Posting it here for personal use, but maybe it&#8217;ll be useful to someone else too.</p>

<p>Usage: fill in any two textboxes and click &#8220;calculate&#8221; next to the third one.</p>

<div>
  Time ([HH:]MM:SS) 
  <input id='txtTime' type='text' placeholder='How long is the run?' />
  <button class='calculate-btn' for='txtTime'>Calculate</button>
</div>


<p>x</p>

<div>
  Pace in mins/km (MM:SS)
  <input id='txtPace' type='text' placeholder='How fast do you go?' />
  <button class='calculate-btn' for='txtPace'>Calculate</button>
</div>


<p>=</p>

<div>
  Distance in kilometers
  <input id='txtDistance' type='text' placeholder='How far do you go?' />
  <button class='calculate-btn' for='txtDistance'>Calculate</button>
</div>




<script type='text/javascript'>
$(function() {
function padLeft(x) {
var str = "" + x;
return ('00'+str).substring(str.length);
}

function timeToSeconds(timestr) {
var parts = timestr.split(':').reverse();

var result = 0;
for(var i = 0; i < parts.length; i++) {
result += (parts[i] * Math.pow(60, i));
}

return result;
}

function secondsToTime(secs) {
var parts = [];

while(secs > 0){
var mod = secs % 60;
parts.push(padLeft(Math.floor(mod)));

secs = Math.floor(secs / 60);
}

if(parts.length == 1) { parts.push('00'); }

return parts.reverse().join(':');
}

$('.calculate-btn').click(function() {
var time = timeToSeconds($('#txtTime').val());
var pace = timeToSeconds($('#txtPace').val());
var dist = $('#txtDistance').val();

var target = $(this).attr('for');
var result;

if(target === 'txtTime') {
result = dist * pace;
$('#txtTime').val(secondsToTime(result));
}

if(target === 'txtPace') {
result = time / dist;
$('#txtPace').val(secondsToTime(result));
}

if(target === 'txtDistance') {
result = time / pace;
var rounded = Math.round(result * 100) / 100;
$('#txtDistance').val(rounded);
}
})
});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Write data to Google API on example of UrlShortener API]]></title>
    <link href="http://tech.tulentsev.com/2015/01/write-data-to-google-api-on-example-of-urlshortener-api/"/>
    <updated>2015-01-11T19:19:00+03:00</updated>
    <id>http://tech.tulentsev.com/2015/01/write-data-to-google-api-on-example-of-urlshortener-api</id>
    <content type="html"><![CDATA[<p>I had some free time this weekend and wanted to mess around with Google API. Ultimately I wanted to work with Analytics, but decided to start with something simpler (like UrlShortener) to reduce possible beginner&#8217;s pain. I was fortunate to decide so, because I spent a couple of hours in debugging anyway.</p>

<p>Google API documentation is weird. It has this short <a href="https://developers.google.com/api-client-library/ruby/start/get_started#build">Getting Started</a> guide for Ruby, which contains only a few basic steps. <a href="https://developers.google.com/url-shortener/v1/getting_started?csw=1">Actual documentation</a> is expressed in the universal language of GET/POST requests (to be as language-agnostic as possible). A poor ruby newcomer is basically left to his own devices, there will be little to no help from google.</p>

<p>Let me share some things I learned. Basic consumption of an API looks like this:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="nb">require</span> <span class="s1">&#39;google/api_client&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="n">client</span> <span class="o">=</span> <span class="no">Google</span><span class="o">::</span><span class="no">APIClient</span><span class="o">.</span><span class="n">new</span>
</span><span class='line'>
</span><span class='line'><span class="n">urlshortener</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">discovered_api</span><span class="p">(</span><span class="s1">&#39;urlshortener&#39;</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="n">result</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">execute</span><span class="p">({</span>
</span><span class='line'>  <span class="n">api_method</span><span class="p">:</span> <span class="n">urlshortener</span><span class="o">.</span><span class="n">url</span><span class="o">.</span><span class="n">get</span><span class="p">,</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">parameters</span><span class="p">:</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">shortUrl</span><span class="p">:</span> <span class="s1">&#39;http://goo.gl/fbsS&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">})</span>
</span></code></pre></td></tr></table></div></figure>


<p>Looks simple, right? Yeah. It also doesn&#8217;t work. You&#8217;ll get an error when you try to run this.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="no">ArgumentError</span><span class="p">:</span> <span class="no">Missing</span> <span class="n">access</span> <span class="n">token</span><span class="o">.</span>
</span><span class='line'>  <span class="n">from</span> <span class="sr">/Users/se</span><span class="n">rgio</span><span class="o">/.</span><span class="n">gem</span><span class="o">/</span><span class="n">ruby</span><span class="o">/</span><span class="mi">2</span><span class="o">.</span><span class="mi">1</span><span class="o">.</span><span class="mi">5</span><span class="o">/</span><span class="n">gems</span><span class="o">/</span><span class="n">signet</span><span class="o">-</span><span class="mi">0</span><span class="o">.</span><span class="mi">6</span><span class="o">.</span><span class="mi">0</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">signet</span><span class="o">/</span><span class="n">oauth_2</span><span class="o">/</span><span class="n">client</span><span class="o">.</span><span class="n">rb</span><span class="p">:</span><span class="mi">1009</span><span class="ss">:in</span> <span class="sb">`generate_authenticated_request&#39;</span>
</span><span class='line'><span class="sb"> from /Users/sergio/.gem/ruby/2.1.5/gems/google-api-client-0.8.2/lib/google/api_client/request.rb:241:in `</span><span class="n">to_env</span><span class="err">&#39;</span>
</span><span class='line'>  <span class="o">.</span><span class="n">.</span><span class="o">.</span>
</span></code></pre></td></tr></table></div></figure>


<p>Documentation forgot to mention that you need to disable authorization in the client.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">client</span> <span class="o">=</span> <span class="no">Google</span><span class="o">::</span><span class="no">APIClient</span><span class="o">.</span><span class="n">new</span><span class="p">({</span>
</span><span class='line'>  <span class="n">authorization</span><span class="p">:</span> <span class="kp">nil</span><span class="p">,</span>
</span><span class='line'><span class="p">})</span>
</span></code></pre></td></tr></table></div></figure>


<p>Now, let&#8217;s say you want to shorten an url. This is <code>urlshortener.url.insert</code> method. Should be easy.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">client</span> <span class="o">=</span> <span class="no">Google</span><span class="o">::</span><span class="no">APIClient</span><span class="o">.</span><span class="n">new</span><span class="p">({</span>
</span><span class='line'>  <span class="n">authorization</span><span class="p">:</span> <span class="kp">nil</span><span class="p">,</span>
</span><span class='line'><span class="p">})</span>
</span><span class='line'>
</span><span class='line'><span class="n">urlshortener</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">discovered_api</span><span class="p">(</span><span class="s1">&#39;urlshortener&#39;</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="n">result</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">execute</span><span class="p">({</span>
</span><span class='line'>  <span class="n">api_method</span><span class="p">:</span> <span class="n">urlshortener</span><span class="o">.</span><span class="n">url</span><span class="o">.</span><span class="n">insert</span><span class="p">,</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">parameters</span><span class="p">:</span> <span class="p">{</span>
</span><span class='line'>      <span class="n">longUrl</span><span class="p">:</span> <span class="s1">&#39;http://google.com&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">})</span>
</span></code></pre></td></tr></table></div></figure>


<p>But, instead of short url, you get this error in <code>result.data</code></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="p">{</span>
</span><span class='line'>    <span class="s2">&quot;error&quot;</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span><span class='line'>         <span class="s2">&quot;errors&quot;</span> <span class="o">=&gt;</span> <span class="o">[</span>
</span><span class='line'>            <span class="p">{</span>
</span><span class='line'>                      <span class="s2">&quot;domain&quot;</span> <span class="o">=&gt;</span> <span class="s2">&quot;global&quot;</span><span class="p">,</span>
</span><span class='line'>                      <span class="s2">&quot;reason&quot;</span> <span class="o">=&gt;</span> <span class="s2">&quot;required&quot;</span><span class="p">,</span>
</span><span class='line'>                     <span class="s2">&quot;message&quot;</span> <span class="o">=&gt;</span> <span class="s2">&quot;Required&quot;</span><span class="p">,</span>
</span><span class='line'>                <span class="s2">&quot;locationType&quot;</span> <span class="o">=&gt;</span> <span class="s2">&quot;parameter&quot;</span><span class="p">,</span>
</span><span class='line'>                    <span class="s2">&quot;location&quot;</span> <span class="o">=&gt;</span> <span class="s2">&quot;resource.longUrl&quot;</span>
</span><span class='line'>            <span class="p">}</span>
</span><span class='line'>        <span class="o">]</span><span class="p">,</span>
</span><span class='line'>           <span class="s2">&quot;code&quot;</span> <span class="o">=&gt;</span> <span class="mi">400</span><span class="p">,</span>
</span><span class='line'>        <span class="s2">&quot;message&quot;</span> <span class="o">=&gt;</span> <span class="s2">&quot;Required&quot;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>It looks like an error. And it seems to be saying that I somehow did not specify <code>longUrl</code> parameter. But I did! Also, what&#8217;s up with <code>resource</code> in <code>resource.longUrl</code>. After much googling, some more reading of their gem sources and some debugging on top of that, I was able to figure this out.</p>

<p>You see, <code>urlshortener.url.insert</code> is supposed to be a POST request. And it is correctly sent by the client gem. The only problem is with arguments. <code>parameters</code> key is what goes into query string for GET requests. But POST requests expect data in request body. Theoretically speaking, the gem could be smart enough to serialize <code>parameters</code> as a body if request method is &#8220;POST&#8221;. Sadly, it&#8217;s not the case. The secret is to use <code>body_object</code> key.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">result</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">execute</span><span class="p">({</span>
</span><span class='line'>  <span class="n">api_method</span><span class="p">:</span> <span class="n">urlshortener</span><span class="o">.</span><span class="n">url</span><span class="o">.</span><span class="n">insert</span><span class="p">,</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">body_object</span><span class="p">:</span> <span class="p">{</span>
</span><span class='line'>      <span class="n">longUrl</span><span class="p">:</span> <span class="s1">&#39;http://google.com&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">})</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>body_object</code> will be serialized to request body. This will also set a correct &#8220;Content-Type&#8221; request header (&#8220;application/json&#8221;).</p>

<p>I spent several hours and didn&#8217;t find a hint of a hint to this in the docs. I know, maintaining good APIs is hard and maintaining good docs is even harder, but isn&#8217;t google supposed to be example to us all? :)</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[How to freeze capybara integration/feature spec]]></title>
    <link href="http://tech.tulentsev.com/2014/04/how-to-stop-capybara-integration-slash-feature-spec/"/>
    <updated>2014-04-28T21:05:00+04:00</updated>
    <id>http://tech.tulentsev.com/2014/04/how-to-stop-capybara-integration-slash-feature-spec</id>
    <content type="html"><![CDATA[<p>I normally don&#8217;t write feature specs a lot, because usually the process is a bit of a pain. Especially when you&#8217;re trying to cover an existing legacy page with feature specs. Here&#8217;s one little trick that made my work a log easier, hopefully it will help you too :)</p>

<p>The problem is that sometimes the spec fails and you get a vague idea why (element by selector is not found or something like that), but by the time an error is printed, the browser window is long gone (you see the browser if you use selenium capybara driver, for example). So you want to pause a test and dig around, find out what goes wrong.</p>

<p>Capybara provides you with a <code>page</code> object which you can use to interact with the browser (click buttons, find elements, etc.), but I found it very inconvenient for quick poking around. Well, turns out (I didn&#8217;t know this before) that you can pause a test with something like <code>binding.pry</code> or <code>sleep</code>. Then you can inspect the page under test in the browser which will be left open and not frozen (it&#8217;s the separate process, so when main test process sleeps, browser is unaffected). Developer tools of modern browsers are fantastic, so it&#8217;s a shame not to use them.</p>

<p>Oh yes, you can also use this technique to do repeating test setup. Say, the business reports a bug to you, you want to test it, but you don&#8217;t want to keep a permanent feature spec (they&#8217;re dog slow). What you can do is create a temporary test that will set everything up (create three different types of accounts, two projects and what not) and just put a <code>binding.pry</code> at the end. From then you can tail <code>log/test.log</code> and test the site manually. After you&#8217;re done with the bug, you just delete the test and commit the changes.</p>

<p>Anyway, I hope this is googlable and will save somebody some time.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[How to make custom commands in Atom extensions]]></title>
    <link href="http://tech.tulentsev.com/2014/04/how-to-make-custom-commands-in-atom-extensions/"/>
    <updated>2014-04-11T19:58:00+04:00</updated>
    <id>http://tech.tulentsev.com/2014/04/how-to-make-custom-commands-in-atom-extensions</id>
    <content type="html"><![CDATA[<p>A couple of days ago I finally got my invite to <a href="http://atom.io">Atom editor</a>. A bit late, but I still was excited.</p>

<p>It makes overall positive impression and has some essential things bundled (save on lost focus, trim trailing whitespaces, etc.) But still there are some things lacking. So I decided to check out its praised extensibility.</p>

<p>Again, docs look good and cover some first steps. But I spent a good couple of hours today, trying to invoke my custom command.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='coffeescript'><span class='line'><span class="nv">module.exports =</span>
</span><span class='line'>  <span class="nv">activate: </span><span class="nf">(state) -&gt;</span>
</span><span class='line'>
</span><span class='line'>  <span class="nv">process: </span><span class="o">-&gt;</span>
</span><span class='line'>    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span> <span class="s">&#39;doing custom action&#39;</span>
</span><span class='line'>    <span class="nv">editor = </span><span class="nx">atom</span><span class="p">.</span><span class="nx">workspace</span><span class="p">.</span><span class="nx">getActiveEditor</span><span class="p">()</span>
</span><span class='line'>    <span class="nx">editor</span><span class="p">.</span><span class="nx">insertText</span><span class="p">(</span><span class="s">&#39;hello&#39;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>process</code> in the listing above is a simple action. It gets current editor and inserts string &#8220;hello&#8221; at the cursor. I defined an editor shortcut, like this:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='json'><span class='line'><span class="err">&#39;.editor&#39;:</span>
</span><span class='line'>  <span class="err">&#39;cmd-ctrl-shift-e&#39;:</span> <span class="err">&#39;myext:process&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>where <code>myext</code> is the name of my extension. But upon pressing the hotkey, nothing happened. A couple of hours later I got the solution. My mistake was in assuming that Atom will somehow discover and dynamically call that exported method <code>process</code>. It&#8217;s exported for a reason, right? Well, no. It turns out that you have to bind commands manually.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='coffeescript'><span class='line'><span class="nv">module.exports =</span>
</span><span class='line'>  <span class="nv">activate: </span><span class="nf">(state) -&gt;</span>
</span><span class='line'>    <span class="nx">atom</span><span class="p">.</span><span class="nx">workspaceView</span><span class="p">.</span><span class="nx">command</span> <span class="s">&quot;myext:process&quot;</span><span class="p">,</span> <span class="o">=&gt;</span> <span class="nx">@process</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'>  <span class="nv">process: </span><span class="o">-&gt;</span>
</span><span class='line'>    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span> <span class="s">&#39;doing custom action&#39;</span>
</span><span class='line'>    <span class="nv">editor = </span><span class="nx">atom</span><span class="p">.</span><span class="nx">workspace</span><span class="p">.</span><span class="nx">getActiveEditor</span><span class="p">()</span>
</span><span class='line'>    <span class="nx">editor</span><span class="p">.</span><span class="nx">insertText</span><span class="p">(</span><span class="s">&#39;hello&#39;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>Next time, we&#8217;ll do something useful. :)</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[How To Work with large YAML files and not go crazy]]></title>
    <link href="http://tech.tulentsev.com/2014/04/work-with-large-yaml-files-and-not-go-crazy/"/>
    <updated>2014-04-04T20:15:00+04:00</updated>
    <id>http://tech.tulentsev.com/2014/04/work-with-large-yaml-files-and-not-go-crazy</id>
    <content type="html"><![CDATA[<p>Large Rails apps have large locale .yml files. Some have files so large that it is not feasible to simply open them in editor and work with them. Sure, you can edit them just fine, but you can&#8217;t efficiently search them.</p>

<p>Say, you&#8217;re implementing a new page. It has &#8220;Balance&#8221; (for example) field on it. You think: &#8220;Hm, we have several other pages with this field. Surely, it had been I18n&#8217;d before. Let&#8217;s look in locale files&#8221;. You open 10 kLOC en-US.yml file, start searching and find 15 entries of &#8220;balance:&#8221; string, with varying levels of indentation. What are the full names of these keys? You have no idea.</p>

<p>I googled for quite a while and, to my surprise, haven&#8217;t found a yaml browser with search, which would show me FQN of a key. Here&#8217;s my little script that I wrote to help me in navigating these ginormous files:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="c1">#! /usr/bin/env ruby</span>
</span><span class='line'>
</span><span class='line'><span class="nb">require</span> <span class="s1">&#39;yaml&#39;</span>
</span><span class='line'><span class="nb">require</span> <span class="s1">&#39;colorize&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="n">filename</span> <span class="o">=</span> <span class="no">ARGV</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span>
</span><span class='line'><span class="n">pattern_text</span> <span class="o">=</span> <span class="no">ARGV</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span>
</span><span class='line'>
</span><span class='line'><span class="k">unless</span> <span class="n">filename</span> <span class="o">&amp;&amp;</span> <span class="n">pattern_text</span>
</span><span class='line'>  <span class="nb">puts</span> <span class="s2">&quot;Usage: grep_yaml.rb filename pattern&quot;</span>
</span><span class='line'>  <span class="nb">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="n">pattern</span> <span class="o">=</span> <span class="no">Regexp</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">pattern_text</span><span class="p">,</span> <span class="ss">:nocase</span><span class="p">)</span>
</span><span class='line'><span class="nb">p</span> <span class="n">pattern</span>
</span><span class='line'>
</span><span class='line'><span class="nb">hash</span> <span class="o">=</span> <span class="no">YAML</span><span class="o">.</span><span class="n">load_file</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">recurse</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">pattern</span><span class="p">,</span> <span class="n">current_path</span> <span class="o">=</span> <span class="o">[]</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span><span class='line'>  <span class="k">case</span> <span class="n">obj</span>
</span><span class='line'>  <span class="k">when</span> <span class="nb">String</span>
</span><span class='line'>    <span class="n">path</span> <span class="o">=</span> <span class="n">current_path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">)</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">obj</span> <span class="o">=~</span> <span class="n">pattern</span> <span class="o">||</span> <span class="n">path</span> <span class="o">=~</span> <span class="n">pattern</span>
</span><span class='line'>      <span class="k">yield</span> <span class="o">[</span><span class="n">path</span><span class="p">,</span> <span class="n">obj</span><span class="o">]</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>  <span class="k">when</span> <span class="no">Hash</span>
</span><span class='line'>    <span class="n">obj</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="o">|</span>
</span><span class='line'>      <span class="n">recurse</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">pattern</span><span class="p">,</span> <span class="n">current_path</span> <span class="o">+</span> <span class="o">[</span><span class="n">k</span><span class="o">]</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="n">recurse</span><span class="p">(</span><span class="nb">hash</span><span class="p">,</span> <span class="n">pattern</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">path</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span>
</span><span class='line'>  <span class="n">line</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="n">path</span><span class="si">}</span><span class="s2">:</span><span class="se">\t</span><span class="si">#{</span><span class="n">value</span><span class="si">}</span><span class="s2">&quot;</span>
</span><span class='line'>  <span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span> <span class="p">{</span><span class="o">|</span><span class="n">match</span><span class="o">|</span> <span class="n">match</span><span class="o">.</span><span class="n">green</span> <span class="p">}</span>
</span><span class='line'>  <span class="nb">puts</span> <span class="n">line</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Example usage (grep one of locale files of <a href="https://github.com/discourse/discourse">Discourse</a> project):</p>

<p><img class="center" src="http://tech.tulentsev.com/images/uploads/2014/04/04/yaml_grep.png"></p>

<p>Much more comfortable, isn&#8217;t it?</p>

<p>The source is uploaded as <a href="https://gist.github.com/stulentsev/9978365">gist</a>, for your copying/forking convenience. I should probably make a proper rubygem out of it. But, you know, when the immediate problem is solved, there are always more important things to work on. :/</p>

<p>Hope you find it useful.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Using OSX Notification Center in your programs]]></title>
    <link href="http://tech.tulentsev.com/2014/03/using-osx-notification-center-in-your-programs/"/>
    <updated>2014-03-17T20:50:00+04:00</updated>
    <id>http://tech.tulentsev.com/2014/03/using-osx-notification-center-in-your-programs</id>
    <content type="html"><![CDATA[<p>There is this good feature in OSX, called <a href="http://en.wikipedia.org/wiki/Notification_Center">Notification Center</a> (which Apple may have stolen from Growl, who knows). It has an API, so you can post your events there. If you&#8217;re using Xcode and Apple frameworks, then you can stop reading now. However, if you&#8217;re programming in, say, ruby, you&#8217;re in much less fortunate position. There&#8217;s no official gem or library, so you are left on your own.</p>

<p>But don&#8217;t despair, we have you covered. There&#8217;s a cocoa app called <a href="https://github.com/alloy/terminal-notifier">terminal-notifier</a> that serves as a bridge between Notification Center and your app. It is accessible via command line and is quite configurable. There&#8217;s also a ruby gem that wraps this tool, but the main value is that it is available in command line. Which means that you can use it from whatever language you want. For example, I use it in my stackoverflow question poller which is written in <a href="http://golang.org/">Go</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Dash - now with cheatsheets]]></title>
    <link href="http://tech.tulentsev.com/2014/03/dash-now-with-cheatsheets/"/>
    <updated>2014-03-10T21:50:00+04:00</updated>
    <id>http://tech.tulentsev.com/2014/03/dash-now-with-cheatsheets</id>
    <content type="html"><![CDATA[<p>Just a couple of days ago I blogged about <a href="http://kapeli.com/dash">Dash</a>. And now they have released new major version, with (at least one) new feature: cheatsheets. One of the things I just can&#8217;t hold in my memory is HTTP status codes. I know 200 and 404, that&#8217;s it. All others I have to look up, every single time. And Dash has a cheatsheet for this, so I am covered here.</p>

<p>There is no cheatsheet for Vim, not even on its movements. But good news is: you can create your own: <a href="https://github.com/Kapeli/cheatset#readme">cheatset</a>.</p>

<p>I shall probably create one for TextMate key shortcuts (I should know most of them by now, I guess). We&#8217;ll see.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Useful documentation lookup tools]]></title>
    <link href="http://tech.tulentsev.com/2014/03/useful-documentation-lookup-tools/"/>
    <updated>2014-03-04T23:20:00+04:00</updated>
    <id>http://tech.tulentsev.com/2014/03/useful-documentation-lookup-tools</id>
    <content type="html"><![CDATA[<p>Today I just wanted to share some tools that I use to read documentation.</p>

<p>First one is <a href="http://kapeli.com/dash">Dash</a>. It&#8217;s an offline documentation browser which contains many different topics. It has ruby, rails, CSS, HAML, jQuery, Go, Haskell and everything else you might want. It can also index locally installed rubygems. And did I mention that it is offline? If you&#8217;re anything like me, you need to consult documentation every 30 seconds. And with this tool you can even work on a plane! Can&#8217;t recommend enough.</p>

<p>Another one is for ruby only, <a href="http://www.omniref.com/">OmniRef</a>. These guys have indexed every ruby gem there is and cross-linked the documentation. On the site you can <em>easily</em> switch between version of a gem (or ruby) and see how documented behaviour and/or code changed over time. It also allows leaving notes (user generated content?), but I don&#8217;t see many notes at the moment. Potentially, these comments can be an excellent complement to the official documentation (we see this with, say, MySQL docs).</p>

<p>And, of course, the most important tool for me these days is <a href="http://stackoverflow.com">StackOverflow</a>. More than half of my google searches lead to this chest of collective programming wisdom. The funniest moments are when I struggle with something, then google points me to stackoverflow where I find an answer that <em>I posted myself</em> some time ago. Can you believe this? :)</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Limitations of MongoDB]]></title>
    <link href="http://tech.tulentsev.com/2014/02/limitations-of-mongodb/"/>
    <updated>2014-02-25T13:18:00+04:00</updated>
    <id>http://tech.tulentsev.com/2014/02/limitations-of-mongodb</id>
    <content type="html"><![CDATA[<p>MongoDB is becoming even more popular than it is now. More people want to learn about it. So I was preparing a seminar for this company and I had to compile a list of MongoDB limits. I never knew there were so many! Some of them are reasonable, some are weird. Anyway, it&#8217;s good to know them. Here&#8217;s a list of MongoDB limits as of version 2.4.9:</p>

<ul>
<li>Max document size: 16 MB (we all knew this one, right?)</li>
<li>Max document nesting level: 100 (documents inside documents inside documents&#8230;)</li>
<li>Namespace is limited to ~123 chars (namespace is db_name + collection_name (or index_name))</li>
<li>DB name is limited to 64 chars</li>
<li>Default .ns file can store about 24000 namespaces (again, a namespace is referring to a collection or an index)</li>
<li>If you index some field, that field can&#8217;t contain more than 1024 bytes</li>
<li>Max 64 indexes per collection</li>
<li>Max 31 fields in a compound index</li>
<li>fulltext search and geo indexes are mutually exclusive (you can&#8217;t use both in the same query)</li>
<li>If you set a limit of documents in a capped collection, this limit can&#8217;t be more than 2**32. Otherwise, number of documents is unlimited.</li>
<li>On linux, one mongod instance can&#8217;t store more than 64 TB of data (128 TB without journal)</li>
<li>On windows, mongod can&#8217;t store more than 4 TB of data (8 TB without journal)</li>
<li>Max 12 nodes in a replica set</li>
<li>Max 7 voting nodes in a replica set</li>
<li>You can&#8217;t automatically rollback more than 300 MB of data. If you have more than this, manual invervention is needed.</li>
<li><code>group</code> command doesn&#8217;t work in sharded cluster.</li>
<li>db.eval() doesn&#8217;t work on sharded collections. Works on unsharded, though.</li>
<li><code>$isolated</code>, <code>$snapshot</code>, geoSearch don&#8217;t work in a sharded cluster.</li>
<li>You can&#8217;t refer <code>db</code> object in <code>$where</code> functions.</li>
<li>If you want to shard a collection, it must be smaller than 256 GB, or else it will likely fail to shard.</li>
<li>Individual (not multi) updates/removes in a sharded cluster must include shard key. Multi versions of these commands may not include shard key.</li>
<li>Max 512 bytes for shard key values</li>
<li>You can&#8217;t change shard key for a collection once it&#8217;s sharded.</li>
<li>You can&#8217;t change value of a shard key of a document.</li>
<li>aggregate/$sort produces error if sorting takes more than 10 percent of RAM.</li>
<li>You can&#8217;t use $or in 2d geo queries</li>
<li>You better not use queries with multiple $in parts. If this results in more than 4 million combinations - you get error.</li>
<li>Database names are case-sensitive (even on case-insensitive file systems)</li>
<li>Forbidden characters in database names: linux - /. &#8220;, windows - /. &#8220;*&lt;>:|?</li>
<li>Forbidden characters in collection names: $ sign, &#8220;system.&#8221; prefix</li>
<li>Forbidden characters in field names: .$</li>
<li>Hashed index can&#8217;t be unique</li>
<li>Max connection number is hardcoded to 20k.</li>
</ul>


<p>Hope this was useful to you.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Kata: Convert numbers to Roman numerals]]></title>
    <link href="http://tech.tulentsev.com/2014/02/kata-convert-numbers-to-roman-numerals/"/>
    <updated>2014-02-08T10:24:00+04:00</updated>
    <id>http://tech.tulentsev.com/2014/02/kata-convert-numbers-to-roman-numerals</id>
    <content type="html"><![CDATA[<p>Here is an interesting problem: write a program that converts numbers into <a href="http://en.wikipedia.org/wiki/Roman_numerals">Roman numerals</a>. Roman didn&#8217;t use Arabic numbers. Instead they used symbols of Latin alphabet that represented different values. It&#8217;s a simple system. &#8220;I&#8221; stands for 1, &#8220;V&#8221; stands for 5, &#8220;X&#8221; for 10 and so on. To represent 2 you have to use &#8220;II&#8221; (1 + 1), to represent 7 you use &#8220;VII&#8221; (5 + 1 + 1). Simple, right? Well, no. Here&#8217;s a twist.</p>

<pre>
* 4 is "IV" (not "IIII")
* 9 is "IX" (not "VIIII")
* 40 is "XL" (not "XXXX")
* 49 is "XLIX" (not "XXXXVIIII")
</pre>


<p>Weird, huh? Maybe they didn&#8217;t like 4 identical symbols in a row, who knows. Anyway, how can we solve this?</p>

<p>After trying out several &#8220;smart&#8221; solutions and failing, I&#8217;ve come up with this simple solution: store these special cases along with normal ones and keep subtracting from the number until it&#8217;s zero.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">romanize</span><span class="p">(</span><span class="n">number</span><span class="p">)</span>
</span><span class='line'>  <span class="n">reductions</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'>    <span class="mi">1000</span> <span class="o">=&gt;</span> <span class="s1">&#39;M&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="mi">900</span> <span class="o">=&gt;</span> <span class="s1">&#39;CM&#39;</span><span class="p">,</span>
</span><span class='line'>
</span><span class='line'>    <span class="mi">500</span> <span class="o">=&gt;</span> <span class="s1">&#39;D&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="mi">400</span> <span class="o">=&gt;</span> <span class="s1">&#39;CD&#39;</span><span class="p">,</span>
</span><span class='line'>
</span><span class='line'>    <span class="mi">100</span> <span class="o">=&gt;</span> <span class="s1">&#39;C&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="mi">90</span> <span class="o">=&gt;</span> <span class="s1">&#39;XC&#39;</span><span class="p">,</span>
</span><span class='line'>
</span><span class='line'>    <span class="mi">50</span> <span class="o">=&gt;</span> <span class="s1">&#39;L&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="mi">40</span> <span class="o">=&gt;</span> <span class="s1">&#39;XL&#39;</span><span class="p">,</span>
</span><span class='line'>
</span><span class='line'>    <span class="mi">10</span> <span class="o">=&gt;</span> <span class="s1">&#39;X&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="mi">9</span> <span class="o">=&gt;</span> <span class="s1">&#39;IX&#39;</span><span class="p">,</span>
</span><span class='line'>
</span><span class='line'>    <span class="mi">5</span> <span class="o">=&gt;</span> <span class="s1">&#39;V&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="mi">4</span> <span class="o">=&gt;</span> <span class="s1">&#39;IV&#39;</span><span class="p">,</span>
</span><span class='line'>
</span><span class='line'>    <span class="mi">1</span> <span class="o">=&gt;</span> <span class="s1">&#39;I&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">result</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">while</span> <span class="n">number</span> <span class="o">&gt;</span> <span class="mi">0</span>
</span><span class='line'>    <span class="n">reductions</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">n</span><span class="p">,</span> <span class="n">subst</span><span class="o">|</span>
</span><span class='line'>      <span class="k">if</span> <span class="n">number</span> <span class="o">/</span> <span class="n">n</span> <span class="o">&gt;=</span> <span class="mi">1</span> <span class="c1"># if number contains at least one of n</span>
</span><span class='line'>        <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="n">subst</span>  <span class="c1"># push corresponding symbol to result</span>
</span><span class='line'>        <span class="n">number</span> <span class="o">-=</span> <span class="n">n</span>
</span><span class='line'>        <span class="k">break</span>            <span class="c1"># break from each and start it anew </span>
</span><span class='line'>                         <span class="c1"># so that the largest numbers are checked first again.</span>
</span><span class='line'>      <span class="k">end</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">result</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>A test:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">test_mapping</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'>  <span class="mi">1</span> <span class="o">=&gt;</span> <span class="s1">&#39;I&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">2</span> <span class="o">=&gt;</span> <span class="s1">&#39;II&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">3</span> <span class="o">=&gt;</span> <span class="s1">&#39;III&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">4</span> <span class="o">=&gt;</span> <span class="s1">&#39;IV&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">5</span> <span class="o">=&gt;</span> <span class="s1">&#39;V&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">6</span> <span class="o">=&gt;</span> <span class="s1">&#39;VI&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">7</span> <span class="o">=&gt;</span> <span class="s1">&#39;VII&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">8</span> <span class="o">=&gt;</span> <span class="s1">&#39;VIII&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">9</span> <span class="o">=&gt;</span> <span class="s1">&#39;IX&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">10</span> <span class="o">=&gt;</span> <span class="s1">&#39;X&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">11</span> <span class="o">=&gt;</span> <span class="s1">&#39;XI&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">12</span> <span class="o">=&gt;</span> <span class="s1">&#39;XII&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">13</span> <span class="o">=&gt;</span> <span class="s1">&#39;XIII&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">14</span> <span class="o">=&gt;</span> <span class="s1">&#39;XIV&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">15</span> <span class="o">=&gt;</span> <span class="s1">&#39;XV&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">19</span> <span class="o">=&gt;</span> <span class="s1">&#39;XIX&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">22</span> <span class="o">=&gt;</span> <span class="s1">&#39;XXII&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">29</span> <span class="o">=&gt;</span> <span class="s1">&#39;XXIX&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">30</span> <span class="o">=&gt;</span> <span class="s1">&#39;XXX&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">40</span> <span class="o">=&gt;</span> <span class="s1">&#39;XL&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">49</span> <span class="o">=&gt;</span> <span class="s1">&#39;XLIX&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">99</span> <span class="o">=&gt;</span> <span class="s1">&#39;XCIX&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">950</span> <span class="o">=&gt;</span> <span class="s1">&#39;CML&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="mi">2014</span> <span class="o">=&gt;</span> <span class="s1">&#39;MMXIV&#39;</span><span class="p">,</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="n">test_mapping</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="o">|</span>
</span><span class='line'>  <span class="n">res</span> <span class="o">=</span> <span class="n">romanize</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
</span><span class='line'>  <span class="n">sign</span> <span class="o">=</span> <span class="n">res</span> <span class="o">==</span> <span class="n">v</span> <span class="p">?</span> <span class="s1">&#39;.&#39;</span> <span class="p">:</span> <span class="s1">&#39;F&#39;</span>
</span><span class='line'>
</span><span class='line'>  <span class="nb">puts</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="n">sign</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">k</span><span class="si">}</span><span class="s2"> =&gt; </span><span class="si">#{</span><span class="n">res</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2">&quot;</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>This simple problem took me a little bit over 2 hours to come up with this (simple) solution. Too bad, that often we, programmers, don&#8217;t have time to look for simple solutions and we go for the easiest one.</p>

<p>Hope this helps someone.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[What is UTF-8 (for the dummies)]]></title>
    <link href="http://tech.tulentsev.com/2014/02/what-is-utf-8-for-the-dummies/"/>
    <updated>2014-02-03T09:41:00+04:00</updated>
    <id>http://tech.tulentsev.com/2014/02/what-is-utf-8-for-the-dummies</id>
    <content type="html"><![CDATA[<p>I must admit, I didn&#8217;t really understand what Unicode (more specifically, its flavor &#8220;UTF-8&#8221;) actually is. All I knew was that it&#8217;s a good encoding and it&#8217;s compatible with ASCII. Beyond that - no clue. This video has made it crystal clear.</p>

<iframe width="560" height="315" src="http://tech.tulentsev.com//www.youtube.com/embed/MijmeoH9LT4" frameborder="0" allowfullscreen></iframe>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Pipeline processing in Go]]></title>
    <link href="http://tech.tulentsev.com/2013/11/pipeline-processing-in-go/"/>
    <updated>2013-11-04T12:13:00+04:00</updated>
    <id>http://tech.tulentsev.com/2013/11/pipeline-processing-in-go</id>
    <content type="html"><![CDATA[<p>Pipeline processing is a very powerful design idiom. You have some simple building blocks that you can arrange in different combinations to perform complex tasks. A classic example of that is unix command line. Each tool is very simple. It does only job. But it still amazes me regularly, what you can achieve by simply combining those tools into a pipeline and feeding/piping data through it.</p>

<p>Say, you&#8217;re building an RSS reader that shows new posts live. Implementing it in a regular procedural manner is easy. Something like this (pseudo-code):</p>

<pre>
  loop {
    fetch posts
    for each post {
      if we have not yet seen this post {
        mark post seen
        show it to user
      }
    }
  }
</pre>


<p>Say, now we want to do a focused reading. Which means that we only want to see a subset of posts which satisfy some arbitrary criteria (filter by tags, etc.). No problemo, we just add one conditional in there.</p>

<pre>
  loop {
    fetch posts
    for each post {
      if post is interesting {
        if we have not yet seen this post {
          mark post seen
          show it to user
        }
      }
    }
  }
</pre>


<p>Now it&#8217;s getting a little bit difficult to read and it will get worse. All business rules are in the same pile of code and it may be difficult to tell them apart.</p>

<p>But if you think about it, there is a pipeline that consists of several primitive segments:</p>

<p><img class="center" src="http://tech.tulentsev.com/images/uploads/2013/11/04/simple_pipeline.png"></p>

<p>Each individual block is completely separate from each other (well, maybe except that &#8220;select unseen&#8221; and &#8220;mark all seen&#8221; might use the same storage). If we were to, say, remove caching (and stop serving cached content), we&#8217;d only have to take out those two segments. If we want to change how content is presented to user (print to terminal, send to text-to-speect engine, &#8230;), we only have to replace the final segment. The rest of the pipeline stays untouched. And the aforementioned tag filtering - we just insert it after fetcher:</p>

<p><img class="center" src="http://tech.tulentsev.com/images/uploads/2013/11/04/pipeline_with_filter.png"></p>

<p>In the pipeline, each segment can either swallow a message or pass it on (after applying some transformation (optional)). Two simple rules, infinite flexibility.</p>

<p>Go language allows for natural expression of this design: goroutines as segments, connected by channels.</p>

<p>Here&#8217;s an intentionally primitive example, which filters and transforms a stream of integers.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
</pre></td><td class='code'><pre><code class='go'><span class='line'><span class="k">package</span> <span class="n">main</span>
</span><span class='line'>
</span><span class='line'><span class="k">import</span> <span class="p">(</span>
</span><span class='line'>  <span class="s">&quot;fmt&quot;</span>
</span><span class='line'>  <span class="s">&quot;time&quot;</span>
</span><span class='line'><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// type alias for our pipeline segment</span>
</span><span class='line'><span class="c1">// segment is a function that reads from a stream of integers</span>
</span><span class='line'><span class="c1">//  and writes to a stream of integers</span>
</span><span class='line'><span class="k">type</span> <span class="n">pipelineSegment</span> <span class="k">func</span><span class="p">(</span><span class="n">in</span><span class="p">,</span> <span class="n">out</span> <span class="k">chan</span> <span class="nb">int</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>  <span class="c1">// construct our pipeline. Put generator first, then filters/modifiers</span>
</span><span class='line'>  <span class="n">out</span> <span class="p">:=</span> <span class="n">makePipeline</span><span class="p">(</span><span class="n">generator</span><span class="p">,</span> <span class="n">onlyOdd</span><span class="p">,</span> <span class="n">plusOne</span><span class="p">,</span> <span class="n">squared</span><span class="p">,</span> <span class="n">plusOne</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">for</span> <span class="n">v</span> <span class="p">:=</span> <span class="k">range</span> <span class="n">out</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">fmt</span><span class="p">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">&quot;Resulting value: %d\n&quot;</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// This simply generates sequential integers in infinite loop.</span>
</span><span class='line'><span class="k">func</span> <span class="n">generator</span><span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">out</span> <span class="k">chan</span> <span class="nb">int</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="n">i</span> <span class="p">:=</span> <span class="mi">0</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">for</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">out</span> <span class="p">&lt;-</span> <span class="n">i</span>
</span><span class='line'>    <span class="n">i</span> <span class="p">+=</span> <span class="mi">1</span>
</span><span class='line'>    <span class="n">time</span><span class="p">.</span><span class="n">Sleep</span><span class="p">(</span><span class="mi">100</span> <span class="p">*</span> <span class="n">time</span><span class="p">.</span><span class="n">Millisecond</span><span class="p">)</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Filter. Selects only odd integers. Even integers are swallowed.</span>
</span><span class='line'><span class="k">func</span> <span class="n">onlyOdd</span><span class="p">(</span><span class="n">in</span><span class="p">,</span> <span class="n">out</span> <span class="k">chan</span> <span class="nb">int</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">defer</span> <span class="nb">close</span><span class="p">(</span><span class="n">out</span><span class="p">)</span>
</span><span class='line'>  <span class="k">for</span> <span class="n">val</span> <span class="p">:=</span> <span class="k">range</span> <span class="n">in</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">val</span><span class="p">%</span><span class="mi">2</span> <span class="p">==</span> <span class="mi">1</span> <span class="p">{</span>
</span><span class='line'>      <span class="n">out</span> <span class="p">&lt;-</span> <span class="n">val</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Modifier. Adds 1 and passes on.</span>
</span><span class='line'><span class="k">func</span> <span class="n">plusOne</span><span class="p">(</span><span class="n">in</span><span class="p">,</span> <span class="n">out</span> <span class="k">chan</span> <span class="nb">int</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">defer</span> <span class="nb">close</span><span class="p">(</span><span class="n">out</span><span class="p">)</span>
</span><span class='line'>  <span class="k">for</span> <span class="n">val</span> <span class="p">:=</span> <span class="k">range</span> <span class="n">in</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">out</span> <span class="p">&lt;-</span> <span class="n">val</span> <span class="p">+</span> <span class="mi">1</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Modifier. Passes a square of incoming integer.</span>
</span><span class='line'><span class="k">func</span> <span class="n">squared</span><span class="p">(</span><span class="n">in</span><span class="p">,</span> <span class="n">out</span> <span class="k">chan</span> <span class="nb">int</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">defer</span> <span class="nb">close</span><span class="p">(</span><span class="n">out</span><span class="p">)</span>
</span><span class='line'>  <span class="k">for</span> <span class="n">val</span> <span class="p">:=</span> <span class="k">range</span> <span class="n">in</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">out</span> <span class="p">&lt;-</span> <span class="n">val</span> <span class="p">*</span> <span class="n">val</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Generates pipeline out of individual segments. </span>
</span><span class='line'><span class="c1">// Returns an &quot;exhaust pipe&quot;, from which fully processed integers can be read.</span>
</span><span class='line'><span class="k">func</span> <span class="n">makePipeline</span><span class="p">(</span><span class="n">segments</span> <span class="p">...</span><span class="n">pipelineSegment</span><span class="p">)</span> <span class="p">(</span><span class="n">out</span> <span class="k">chan</span> <span class="nb">int</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="n">current_input</span> <span class="p">:=</span> <span class="nb">make</span><span class="p">(</span><span class="k">chan</span> <span class="nb">int</span><span class="p">)</span>
</span><span class='line'>  <span class="k">var</span> <span class="n">current_output</span> <span class="k">chan</span> <span class="nb">int</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">seg</span> <span class="p">:=</span> <span class="k">range</span> <span class="n">segments</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">current_output</span> <span class="p">=</span> <span class="nb">make</span><span class="p">(</span><span class="k">chan</span> <span class="nb">int</span><span class="p">)</span>
</span><span class='line'>    <span class="k">go</span> <span class="n">seg</span><span class="p">(</span><span class="n">current_input</span><span class="p">,</span> <span class="n">current_output</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">current_input</span> <span class="p">=</span> <span class="n">current_output</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">return</span> <span class="n">current_output</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Produced output:</p>

<pre>
  Resulting value: 5
  Resulting value: 17
  Resulting value: 37
  Resulting value: 65
  Resulting value: 101
  Resulting value: 145
  Resulting value: 197
  ...
</pre>


<p>First generated integer is 0. It&#8217;s even (well, certainly not odd), so it does not make it past filter.</p>

<p>Next one is 1. It passes filter. Then it gets +1, so it&#8217;s now 2. Then it&#8217;s squared and becomes 4. And finally, one more +1 which results in 5. There are no more segments, so this value is read from the output pipe and printed to terminal.</p>

<p>Hope this was useful.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Hide/exclude directories in TextMate 2]]></title>
    <link href="http://tech.tulentsev.com/2013/10/hide-slash-exclude-directories-in-textmate-2/"/>
    <updated>2013-10-30T10:24:00+04:00</updated>
    <id>http://tech.tulentsev.com/2013/10/hide-slash-exclude-directories-in-textmate-2</id>
    <content type="html"><![CDATA[<p>I decided to give TextMate 2 another try today. Hopefully it won&#8217;t freeze upon trying to install a bundle like it did last time. Now I don&#8217;t recall which bundle was that, but so far things are going well. Mostly.</p>

<p>When I opened my Rails app and tried to navigate to file (<code>Cmd+T</code>), I saw this:</p>

<p><img class="center" src="http://tech.tulentsev.com/images/uploads/2013/10/30/tm2_tmp_files.png"></p>

<p>At first I thought that these are files from <code>.git</code> folder and spent embarrassing amount of time trying to make TM2 hide that folder. But these are actually temp asset files generated by Sprockets and they reside in <code>tmp/assets/...</code>.</p>

<p>Long story short, here&#8217;s how you can hide a directory in TextMate 2, both from project browser and navigation dialog.</p>

<p>Create a <code>.tm_properties</code> file. It can be in current project dir or any parent dir (up to <code>$HOME</code>). Put this line into it.</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>excludeDirectories = "{tmp,}"</span></code></pre></td></tr></table></div></figure>


<p>It is worth noting that <code>"{tmp}"</code> doesn&#8217;t work. Apparently, TextMate glob parser breaks down if it can&#8217;t find a comma. So we add one. Alternatively we can use this line:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>excludeDirectories = "{$excludeDirectories,tmp}"</span></code></pre></td></tr></table></div></figure>


<p>It will amend an existing <code>excludeDirectories</code> setting (empty string by default) instead of replacing. And it also has a comma. :)</p>

<p>Hope this post saved you some time.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Move Capistrano's log directory]]></title>
    <link href="http://tech.tulentsev.com/2013/03/move-capistranos-log-directory/"/>
    <updated>2013-03-11T14:34:00+04:00</updated>
    <id>http://tech.tulentsev.com/2013/03/move-capistranos-log-directory</id>
    <content type="html"><![CDATA[<p>So you want to move your log directory? To a separate volume, maybe? But you are afraid that it will break too many things, like all those libraries with <code>#{rails_root}/log</code> paths hardcoded in them? Fear not, there&#8217;s a simple workaround.</p>

<p>As you probably know, capistrano aliases your rails log dir to a shared dir outside of app folder.</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>ln -s /deploy_path/shared/log /deploy_path/current/log</span></code></pre></td></tr></table></div></figure>


<p>This means that, in fact, all your logs turn up at <code>/deploy_path/shared/log/</code>. Now all we need is to symlink that dir as well!</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>sudo mkdir /var/log/yourapp
</span><span class='line'>
</span><span class='line'><span class="c"># make it writable to the app</span>
</span><span class='line'>sudo chown -R youruser:yourgroup /var/log/yourapp/
</span><span class='line'>
</span><span class='line'><span class="c"># deal with existing log dir (I back it up, but you can remove it, or copy the files to </span>
</span><span class='line'><span class="c">#   the new location)</span>
</span><span class='line'>mv /deploy_path/shared/log /deploy_path/shared/log_
</span><span class='line'>ln -s /var/log/yourapp /deploy_path/shared/log
</span></code></pre></td></tr></table></div></figure>


<p>Hope this helps :)</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[cap deploy from an arbitrary branch]]></title>
    <link href="http://tech.tulentsev.com/2013/02/cap-deploy-from-an-arbitrary-branch/"/>
    <updated>2013-02-22T12:34:00+04:00</updated>
    <id>http://tech.tulentsev.com/2013/02/cap-deploy-from-an-arbitrary-branch</id>
    <content type="html"><![CDATA[<p>As you know, capistrano can deploy your app from a specific branch in the repo. You just have to specify it</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">set</span> <span class="ss">:branch</span><span class="p">,</span> <span class="s1">&#39;develop&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>If you develop in a single branch, that should do it for you. All commits go to one branch and all deploys are likely served by the same branch. This is a simple model, good enough for small projects.</p>

<p>However, if you adopt <a href="http://nvie.com/posts/a-successful-git-branching-model/">git flow</a>, it encourages you (not to say &#8220;forces&#8221;) to have separate branch for every feature, release or hotfix that you&#8217;re working on. And, naturally, you want to deploy to dev/staging server right from a feature branch, not having to merge it first to develop/master. This is trivial to accomplish, just change the branch name:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">set</span> <span class="ss">:branch</span><span class="p">,</span> <span class="s1">&#39;feature/my-test-feature&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Now you have a pending file change. What do you do with it? Commit to source control? That increases file churn rate for absolutely no reason. <code>git checkout</code> that file? This is an extra action, of which you will be tired quite soon (not to mention chance to accidentally commit the file).</p>

<p>Solution? Externalize the setting. When deploying, look in the environment for a branch to use and fallback to default name.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">set</span> <span class="ss">:branch</span><span class="p">,</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;BRANCH&#39;</span><span class="o">]</span> <span class="o">||</span> <span class="s2">&quot;develop&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Then you do this</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">BRANCH</span><span class="o">=</span>feature/my-test-feature cap deploy
</span></code></pre></td></tr></table></div></figure>


<p>Or, if you have multistage enabled</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">BRANCH</span><span class="o">=</span>feature/my-test-feature cap staging deploy
</span></code></pre></td></tr></table></div></figure>


<p>Of course, this requires a bit more typing on each deploy, but at the same time it decreases chances of deploying from a wrong branch.</p>

<p>Happy deploying!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Sorting humanized numbers in tablesorter.js]]></title>
    <link href="http://tech.tulentsev.com/2012/12/sorting-humanized-numbers-in-tablesorter-dot-js/"/>
    <updated>2012-12-23T17:48:00+04:00</updated>
    <id>http://tech.tulentsev.com/2012/12/sorting-humanized-numbers-in-tablesorter-dot-js</id>
    <content type="html"><![CDATA[<p><a href="http://tablesorter.com/docs/">Tablesorter</a> is a jQuery plugin for sorting tables. It appeared first in a couple of &#8220;X jquery table sorting plugins&#8221; blog posts, so I went with it when I needed to sort a table a couple of days ago.</p>

<p>It works pretty well out of the box and it even lets you customize some bits. One of those bits is column parser: if you have special data in a column, you need a special way to sort it.</p>

<p>Here&#8217;s <a href="http://tablesorter.com/docs/example-parsers.html">an official example</a></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="c1">// add parser through the tablesorter addParser method </span>
</span><span class='line'><span class="nx">$</span><span class="p">.</span><span class="nx">tablesorter</span><span class="p">.</span><span class="nx">addParser</span><span class="p">({</span>
</span><span class='line'>    <span class="c1">// set a unique id </span>
</span><span class='line'>    <span class="nx">id</span><span class="o">:</span> <span class="s1">&#39;grades&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nx">is</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="c1">// return false so this parser is not auto detected </span>
</span><span class='line'>        <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span><span class='line'>    <span class="p">},</span>
</span><span class='line'>    <span class="nx">format</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="c1">// format your data for normalization </span>
</span><span class='line'>        <span class="k">return</span> <span class="nx">s</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">().</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/good/</span><span class="p">,</span><span class="mi">2</span><span class="p">).</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/medium/</span><span class="p">,</span><span class="mi">1</span><span class="p">).</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/bad/</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
</span><span class='line'>    <span class="p">},</span>
</span><span class='line'>    <span class="c1">// set type, either numeric or text </span>
</span><span class='line'>    <span class="nx">type</span><span class="o">:</span> <span class="s1">&#39;numeric&#39;</span>
</span><span class='line'><span class="p">});</span>
</span><span class='line'>
</span><span class='line'><span class="nx">$</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>    <span class="nx">$</span><span class="p">(</span><span class="s2">&quot;table&quot;</span><span class="p">).</span><span class="nx">tablesorter</span><span class="p">({</span>
</span><span class='line'>        <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>            <span class="mi">6</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>                <span class="nx">sorter</span><span class="o">:</span><span class="s1">&#39;grades&#39;</span>
</span><span class='line'>            <span class="p">}</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>    <span class="p">});</span>
</span><span class='line'><span class="p">});</span>
</span></code></pre></td></tr></table></div></figure>


<p>However, it leaves much to be desired. Like what to do when I want my parser to be auto-detected. Turns out that this <code>is</code> function is a predicate. It accepts a string and decides whether it can be handled by this parser. Then the <code>format</code> function will make conversion to a normalized textual/numeric form.</p>

<p>In my case I have a table with a lot of numbers. And those numbers are in humanized form. That is, instead of <code>121738</code> I output <code>121.74k</code>. This format is not properly sortable as either number or text, so I wrote a parser.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">$</span><span class="p">.</span><span class="nx">tablesorter</span><span class="p">.</span><span class="nx">addParser</span><span class="p">({</span>
</span><span class='line'>  <span class="nx">id</span><span class="o">:</span> <span class="s1">&#39;humannum&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="nx">is</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="sr">/^[\d\.]+[Mk]?$/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">s</span><span class="p">);</span>
</span><span class='line'>  <span class="p">},</span>
</span><span class='line'>  <span class="nx">format</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="kd">var</span> <span class="nx">is_m</span> <span class="o">=</span> <span class="nx">s</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s1">&#39;M&#39;</span><span class="p">)</span> <span class="o">!==</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span><span class='line'>    <span class="kd">var</span> <span class="nx">is_k</span> <span class="o">=</span> <span class="nx">s</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s1">&#39;k&#39;</span><span class="p">)</span> <span class="o">!==</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span><span class='line'>    <span class="kd">var</span> <span class="nx">fl</span> <span class="o">=</span> <span class="nb">parseFloat</span><span class="p">(</span><span class="nx">s</span><span class="p">);</span>
</span><span class='line'>    <span class="kd">var</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">fl</span><span class="p">;</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="nx">is_m</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>      <span class="nx">res</span> <span class="o">=</span> <span class="nx">fl</span> <span class="o">*</span> <span class="mi">1000000</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">is_k</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>      <span class="nx">res</span> <span class="o">=</span> <span class="nx">fl</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
</span><span class='line'>  <span class="p">},</span>
</span><span class='line'>  <span class="nx">type</span><span class="o">:</span> <span class="s1">&#39;numeric&#39;</span>
</span><span class='line'><span class="p">});</span>
</span></code></pre></td></tr></table></div></figure>


<p>It basically checks if a string consists of a number followed by an optional &#8220;M&#8221; or &#8220;k&#8221; and, if that&#8217;s the case, converts it to a &#8220;full&#8221; number.</p>

<p>I wish this was in the official doc.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Queue prioritization in Sidekiq]]></title>
    <link href="http://tech.tulentsev.com/2012/12/queue-prioritization-in-sidekiq/"/>
    <updated>2012-12-17T04:47:00+04:00</updated>
    <id>http://tech.tulentsev.com/2012/12/queue-prioritization-in-sidekiq</id>
    <content type="html"><![CDATA[<p>Unlike <a href="https://github.com/defunkt/resque">Resque</a>, <a href="http://mperham.github.com/sidekiq/">Sidekiq</a> allows you to process several queues at the same time. You can even assign priorities. Here&#8217;s an example from default config:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='yaml'><span class='line'><span class="l-Scalar-Plain">:queues</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="p-Indicator">[</span><span class="nv">often</span><span class="p-Indicator">,</span> <span class="nv">7</span><span class="p-Indicator">]</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="p-Indicator">[</span><span class="nv">default</span><span class="p-Indicator">,</span> <span class="nv">5</span><span class="p-Indicator">]</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="p-Indicator">[</span><span class="nv">seldom</span><span class="p-Indicator">,</span> <span class="nv">3</span><span class="p-Indicator">]</span>
</span></code></pre></td></tr></table></div></figure>


<p>If you want two queues have equal rights, just set the same priorities. However, if you omit priority parameter and define your queues like this:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='yaml'><span class='line'><span class="l-Scalar-Plain">:queues</span><span class="p-Indicator">:</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">email</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">coffee</span>
</span><span class='line'>  <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">billing</span>
</span></code></pre></td></tr></table></div></figure>


<p>Then queues will be checked in a serial manner. That is, Sidekiq will start making coffee only when all emails are sent. If a new email shows up, it will be processed before any other job. In other words, this is how Resque would handle the queues.</p>

<p>Choose whatever fits your task better. It&#8217;s nice to have some flexibility.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Using Sidekiq with redis_failover gem]]></title>
    <link href="http://tech.tulentsev.com/2012/12/using-sidekiq-with-redis-failover-gem/"/>
    <updated>2012-12-15T18:23:00+04:00</updated>
    <id>http://tech.tulentsev.com/2012/12/using-sidekiq-with-redis-failover-gem</id>
    <content type="html"><![CDATA[<p><a href="http://mperham.github.com/sidekiq/">Sidekiq</a> works pretty well with standard redis client. This is an example of configuring it (<a href="https://github.com/mperham/sidekiq/wiki/Advanced-Options">from official wiki</a>):</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="no">Sidekiq</span><span class="o">.</span><span class="n">configure_server</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
</span><span class='line'>  <span class="n">config</span><span class="o">.</span><span class="n">redis</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">:url</span> <span class="o">=&gt;</span> <span class="s1">&#39;redis://redis.example.com:7372/12&#39;</span><span class="p">,</span> <span class="ss">:namespace</span> <span class="o">=&gt;</span> <span class="s1">&#39;mynamespace&#39;</span> <span class="p">}</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="c1"># When in Unicorn, this block needs to go in unicorn&#39;s `after_fork` callback:</span>
</span><span class='line'><span class="no">Sidekiq</span><span class="o">.</span><span class="n">configure_client</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
</span><span class='line'>  <span class="n">config</span><span class="o">.</span><span class="n">redis</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">:url</span> <span class="o">=&gt;</span> <span class="s1">&#39;redis://redis.example.com:7372/12&#39;</span><span class="p">,</span> <span class="ss">:namespace</span> <span class="o">=&gt;</span> <span class="s1">&#39;mynamespace&#39;</span> <span class="p">}</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>But, what if you need a special redis client that can&#8217;t be initialized with redis URL? No worries. Setter for <code>Sidekiq#redis</code> also accepts a <a href="https://github.com/mperham/connection_pool">ConnectionPool</a>. Here we have full control over how we initialize our redis client. Here&#8217;s how you may initialize Sidekiq with a bunch of <a href="https://github.com/ryanlecompte/redis_failover">RedisFailover</a> clients:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="no">Sidekiq</span><span class="o">.</span><span class="n">configure_server</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
</span><span class='line'>  <span class="n">pool</span> <span class="o">=</span> <span class="no">ConnectionPool</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:size</span> <span class="o">=&gt;</span> <span class="mi">10</span><span class="p">,</span> <span class="ss">:timeout</span> <span class="o">=&gt;</span> <span class="mi">3</span><span class="p">)</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">zkservers</span> <span class="o">=</span> <span class="s1">&#39;localhost:2181,localhost:2182,localhost:2183&#39;</span>
</span><span class='line'>    <span class="no">RedisFailover</span><span class="o">::</span><span class="no">Client</span><span class="o">.</span><span class="n">new</span> <span class="n">namespace</span><span class="p">:</span> <span class="s1">&#39;my_service&#39;</span><span class="p">,</span> <span class="n">zkservers</span><span class="p">:</span> <span class="n">zkservers</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">config</span><span class="o">.</span><span class="n">redis</span> <span class="o">=</span> <span class="n">pool</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="c1"># do the same in Sidekiq.configure_client</span>
</span></code></pre></td></tr></table></div></figure>


<p>You can even read Sidekiq&#8217;s <code>concurrency</code> setting and pass it to the connection pool initializer. Now your sidekiq farm doesn&#8217;t care about redis crashes :)</p>

<p>Happy sidekiqing :)</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[My (un)success story of Colemak]]></title>
    <link href="http://tech.tulentsev.com/2012/11/my-un-success-story-of-colemak/"/>
    <updated>2012-11-18T20:29:00+04:00</updated>
    <id>http://tech.tulentsev.com/2012/11/my-un-success-story-of-colemak</id>
    <content type="html"><![CDATA[<p>I&#8217;ve been hearing a lot about <a href="http://colemak.com/">Colemak</a> recently. It&#8217;s a (relatively) new keyboard layout, specially designed for efficient touch typing in English. Any description
of Colemak (including this very post) is accompanied by a story, how QWERTY layout was specifically designed to <em>hinder</em> fast typing, so that typewriter bars don&#8217;t get stuck. Ok, cool story, let&#8217;s try this silver <s>bullet</s> layout.</p>

<p>One of the advertised advantages of this layout is that &#8220;most&#8221; common QWERTY hotkeys are the same. Indeed, such hotkeys as select-all/cut/copy/paste didn&#8217;t move. Yes, this is an advantage for people who copy/paste text all day. But for us, coders, this is really a disadvantage, because some hotkeys are on the same keys, and others have moved. And worst of all, most hotkeys have changed their meaning.</p>

<p>Let me illustrate. Most (all?) browsers in Mac OSX understands the same hotkeys:</p>

<ul>
<li>Cmd-T - open new tab</li>
<li>Cmd-F - find</li>
<li>Cmd-G - find next</li>
<li>Cmd-S - save</li>
</ul>


<p>We&#8217;ve been using these hotkeys for years and now they are burned in our muscle memory. So, with Colemak, when you try to perform an action and you reach for the old key, you get completely different action instead.</p>

<ul>
<li>You tried Cmd-T (qwerty), you hit Cmd-G (colemak), action - &#8220;find next&#8221;</li>
<li>Cmd-F, Cmd-E, &#8220;use selection for find&#8221; (chrome)</li>
<li>Cmd-G, Cmd-D, &#8220;add bookmark&#8221;</li>
<li>Cmd-S, Cmd-R, &#8220;reload&#8221;</li>
</ul>


<p>This is very, VERY frustrating to try search page content and keep adding bookmarks instead. Combine this with a fact that when you switch to a Russian layout, the hotkeys are the same as in QWERTY. So, essentially, you have to learn a whole new set of hotkeys for every program you use and always keep track of the current layout, or otherwise you&#8217;ll get unexpected results.</p>

<p>Now, is it really worth it? Given the fact that programmer&#8217;s productivity is not based on how fast and for how long he can type, I&#8217;d say &#8220;Not really&#8221;. I average 370-450 CPM on QWERTY. This is far more than sufficient for everyday coding needs.</p>

<p>Goodbye, Colemak. Hello again, QWERTY.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Talking Capistrano is your friend]]></title>
    <link href="http://tech.tulentsev.com/2012/11/talking-capistrano-is-your-friend/"/>
    <updated>2012-11-12T22:22:00+04:00</updated>
    <id>http://tech.tulentsev.com/2012/11/talking-capistrano-is-your-friend</id>
    <content type="html"><![CDATA[<p>I want to share with you a small tweak I came up with a couple of days ago. As some of you
know, Mac OS X includes a ton of very handy utilities. One of them is <code>say</code>. The name is
pretty descriptive, you pass it a string and it voices it, using text-to-speech engine,
that is built in Mac OS. Try it now:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>say Hello world!
</span></code></pre></td></tr></table></div></figure>


<p>You can use it anywhere where you can invoke shell commands. Here&#8217;s my capistrano
after-hook, for example:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">after</span> <span class="s1">&#39;deploy&#39;</span> <span class="k">do</span>
</span><span class='line'>  <span class="sb">`say Sire, our unicorns have been deployed`</span>
</span><span class='line'>  <span class="c1"># note the backticks. This string is roughly equivalent to</span>
</span><span class='line'>  <span class="c1"># system &quot;say Sire, our unicorns have been deployed&quot;</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>Now I can launch my <code>cap deploy</code> (which can take minute or two) and go read Hacker News or
whatever. I will be told when deploy is finished. I&#8217;m using this for a few days already
and it still make me smile everytime. I should probably come up with more creative messages.</p>

<p>Happy tweaking!</p>
]]></content>
  </entry>
  
</feed>
