<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>def blog</title>
 <link href="http://artemave.github.com/atom.xml" rel="self"/>
 <link href="http://artemave.github.com"/>
 <updated>2015-11-09T10:16:35+00:00</updated>
 <id>http://artemave.github.com</id>
 <author>
   <name>Artem Avetisyan</name>
   <email></email>
 </author>

 
 <entry>
   <title>Developing in Linux on OSX</title>
   <link href="http://artemave.github.com/tools/2015/10/06/developing-in-linux-on-osx"/>
   <updated>2015-10-06T00:00:00+00:00</updated>
   <id>http://artemave.github.com/tools/2015/10/06/developing-in-linux-on-osx</id>
   <content type="html">
&lt;p&gt;I wanted to move to Linux development environment, but at the same time I wanted to stay on OSX for everything else. So VirtualBox was the obvious option. I also had few extra requirements:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;lots disk space&lt;/li&gt;
  &lt;li&gt;integration with OSX clipboard (so that vim/xclip work transparently)&lt;/li&gt;
  &lt;li&gt;static IP (for hosts file, nginx conf and general convenience of never having to look up new IP address)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you happen to need something similar, then simply follow TLDR; below.&lt;/p&gt;

&lt;h2 id=&quot;tldr&quot;&gt;TLDR;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Install &lt;a href=&quot;https://www.packer.io/docs/installation.html&quot;&gt;Packer&lt;/a&gt;, &lt;a href=&quot;https://www.virtualbox.org/wiki/Downloads&quot;&gt;VirtualBox&lt;/a&gt;, &lt;a href=&quot;https://docs.vagrantup.com/v1/installation/&quot;&gt;Vagrant&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;git clone https://github.com/artemave/vagrant-boxes.git &amp;amp;&amp;amp; cd vagrant-boxes&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;cd packer &amp;amp;&amp;amp; packer build --only=virtualbox-iso -var &#39;DISK_SIZE=102400&#39; ubuntu-15.04-server-amd64.json; cd ..&lt;/code&gt; (disk size is in megabytes)&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;vagrant box add --name &#39;ubuntu-15.04-server-amd64&#39; ./builds/virtualbox/ubuntu-15.04-server-amd64_virtualbox.box&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;vagrant up&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;vagrant ssh&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Provision script will create a user account on the vm with the same name as the user on the host machine you’re running &lt;code&gt;vagrant up&lt;/code&gt; from. Following Vagrant convention, the password is ‘vagrant’.&lt;/p&gt;

&lt;p&gt;The default RAM size is set to 8gb. You can adjust this in &lt;code&gt;Vagrantfile&lt;/code&gt;. Then &lt;code&gt;vagrant reload&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To get host/guest clipboard integration, install &lt;a href=&quot;http://xquartz.macosforge.org/landing/&quot;&gt;XQuartz&lt;/a&gt;, set the pasteboard settings to look like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pasteboard.png&quot; style=&quot;width: 100%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;and make sure XQuartz is running whenever you &lt;code&gt;vagrant ssh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can also build a VMware box (&lt;code&gt;--only=vmware-iso&lt;/code&gt;), but I haven’t tried it.&lt;/p&gt;

&lt;h2 id=&quot;notes-for-the-curious&quot;&gt;Notes (for the curious)&lt;/h2&gt;

&lt;p&gt;When I say “development environment” what that really means is console development environment. I use &lt;a href=&quot;https://tmux.github.io/&quot;&gt;tmux&lt;/a&gt; for keeping sessions/windows per project, Vim as an editor and command line for everything else.&lt;/p&gt;

&lt;p&gt;This kind of setup is perfectly achievable in OSX of course. The main reason to move to Linux was the lack of native docker support for OSX. “But there is &lt;a href=&quot;https://docs.docker.com/installation/mac/&quot;&gt;docker-machine&lt;/a&gt; (former boot2docker)” I can hear you saying. And that is true. But there is also a range of minor issues (such as no support for host data volumes, or slow VirtualBox file sharing for when you want to develop against running container, or some more obscure ones) that come with it. It is just one more abstraction layer that will always get its toll.&lt;/p&gt;

&lt;p&gt;There are other good reasons to use Linux, but, perhaps, it was just that time of the year when one needs to shave a yak, so I thought why not, let us give it a go.&lt;/p&gt;

&lt;p&gt;By the way, nice thing about having all work stuff in a VM is that when you need to restart your laptop, it only takes &lt;code&gt;vagrant suspend&lt;/code&gt; to pause the VM and &lt;code&gt;vagrant up&lt;/code&gt; after restart to pick things up right where you left them. Also setting up new machine is much faster.&lt;/p&gt;

&lt;p&gt;Initially I wanted full GUI experience (with &lt;a href=&quot;https://i3wm.org/&quot;&gt;i3wm&lt;/a&gt; tiling window manager), but the graphics turned out to be far too slow. A bit disappointing, giving the top dog MacBookPro.&lt;/p&gt;

&lt;p&gt;Then I decided to use Vagrant with a server box. However stock boxes are set to have 40GB disk and, as I quickly learned, resizing VB disk images involves more steps than a human can follow.&lt;/p&gt;

&lt;p&gt;So I ended up forking the &lt;a href=&quot;https://github.com/ffuenf/vagrant-boxes&quot;&gt;base boxes repository&lt;/a&gt;, changing the disk size in packer config and building the box myself.&lt;/p&gt;

&lt;p&gt;To keep things simple I also dropped the &lt;code&gt;Vagrantfile&lt;/code&gt; into the root of my fork. There are three things to note there:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;static IP: &lt;code&gt;config.vm.network &quot;private_network&quot;, ip: &quot;192.168.33.10&quot;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;ssh with X11 forwarding (for clipboard integration): &lt;code&gt;config.ssh.forward_x11 = true&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;provision script: installs few useful packages (opinionated) and, most importantly, compiles Vim with X support (for clipboard integration) along with some other batteries (such as Lua support for &lt;a href=&quot;https://github.com/Shougo/unite.vim&quot;&gt;Unite.vim&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Setup Nginx reverse proxy (with ssl termination) on OSX</title>
   <link href="http://artemave.github.com/2015/04/01/ssl-reverse-proxy-osx"/>
   <updated>2015-04-01T00:00:00+00:00</updated>
   <id>http://artemave.github.com/2015/04/01/ssl-reverse-proxy-osx</id>
   <content type="html">
&lt;p&gt;&lt;em&gt;This is&lt;/em&gt; NOT &lt;em&gt;a joke.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To test Google Game Services integration locally, I wanted my local machine to be accessible over https on something other than locahost or 127.0.0.1. So I decided to employ &lt;a href=&quot;http://readme.localtest.me/&quot;&gt;localtest.me&lt;/a&gt; and use nginx locally as ssl terminating reverse proxy to my app running on port 9999.&lt;/p&gt;

&lt;p&gt;Install nginx:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;% brew install nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Generate self-signed certificate:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;% openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /usr/local/etc/nginx/cert.key -out /usr/local/etc/nginx/cert.crt
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Nginx.conf:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Important! I had to change this to myself (artem) or it wouldn&#39;t serve js assets with &quot;permission denied&quot;.
# Group is mandatory despite nginx docs claiming otherwise.
user  artem admin;

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  &#39;$remote_addr - $remote_user [$time_local] &quot;$request&quot; &#39;
                      &#39;$status $body_bytes_sent &quot;$http_referer&quot; &#39;
                      &#39;&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;&#39;;

    sendfile        on;

    keepalive_timeout  65;

    server {
        listen       443 ssl;
        server_name  abc.localtest.me;

        ssl_certificate      cert.crt;
        ssl_certificate_key  cert.key;

        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

        location / {
            proxy_pass http://localhost:9999;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
        # Proxy websocket connections (you probably don&#39;t need this)
        location /games/play/ {
            proxy_pass http://localhost:9999;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection &quot;upgrade&quot;;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Start nginx as sudo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;% sudo nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Test it: &lt;a href=&quot;https://abc.localtest.me&quot;&gt;https://abc.localtest.me&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Value Added Tests</title>
   <link href="http://artemave.github.com/2014/12/08/value-added-tests"/>
   <updated>2014-12-08T00:00:00+00:00</updated>
   <id>http://artemave.github.com/2014/12/08/value-added-tests</id>
   <content type="html">
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: high level acceptance test should focus, reveal and describe the value that a feature adds to the business.&lt;/p&gt;

&lt;p&gt;This statement may be obvious once you “get it”, but may sound remote and abstract if you don’t. So let us leave it hanging around for a while and check out this (slightly adapted) real world example.&lt;/p&gt;

&lt;p&gt;Suppose we’re a car retailer website. We’d like to introduce a “personal assistant” feature so that every time someone requests a quote, a member of our sales team gets assigned to it and an email introducing the assistant gets sent to the user. The auto assignment can be changed later from an admin interface.&lt;/p&gt;

&lt;p&gt;We want to deliver early so we decide to break up the feature and first tackle the creation of “assistant profiles” for existing sales user accounts. After all, you can’t assign anything if it does not exist yet. But, more importantly, some of the new code will end up in production without any public facing changes. So the rest of the new code will be smaller and therefore less disruptive to release. Plus, while we’re busy with the rest of the feature, someone from the sales team can go away and fill up the profiles.&lt;/p&gt;

&lt;p&gt;We practice test driven development and the “outside in” approach, so we start with an integration test that looks like this (if this syntax - ruby/rspec - it does not look familiar, just assume that it does what you think it does):&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;scenario&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;create assistant profile&amp;#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# test data&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;dave&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_sales_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_admin_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# create assistant profile&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;visit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;admin_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;click_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Sales users&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;click_button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Edit&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;click_button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Create assistant profile&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;fill_in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Dave&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;attach_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;click_button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Save&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# check if it has made it into db&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;assert_assistant_profile_for_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dave&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Looks pretty straightforward, so we make that happen and deploy to production. And off to implement the assignment part. I’ll spare the details but while working closely through screens and buttons we realise that assistant profile, in addition to what it already has, should also have have a last name. Turns out, while customers should only see first names, if we happen to have two Daves on the sales team, it will be hard to distinguish between them in case of reassigning (think of a drop down with only names vs drop down with “name surname”).&lt;/p&gt;

&lt;p&gt;Also, the email that gets automatically sent to a customer upon an assignment, should have a round image of Dave (according to designs) and that image could not be rounded with css since there ain’t no fancy css (round corners that is) in emails. Therefore, we have to generate the round version when the profile image is uploaded.&lt;/p&gt;

&lt;p&gt;All that means that assistant profiles created so far will have to be changed to add last name and the images will have to be reuploaded. And the next deploy to production is going to involve down time for db migration (we thought we were done with migrations in the first part).&lt;/p&gt;

&lt;p&gt;Well, luckily, none of this is a big deal since no one from the sales team has actually bothered to create any assistant profiles yet.&lt;/p&gt;

&lt;p&gt;Those issues are not massive, of course. But this simple example hopefully illustrates how, on a bigger scale, things can suddenly get out of sync when features, broken up into bits based on what developers think would be the best way to deliver them, do not quite match up when you put them back together in the end. This results in extra integration work  (by changing something that already is assumed to have been done) or, worse, accepting those inconsistencies for whatever reason, making business to get on with it and building up new features on top. To relate that last one back to our example, we could suggest to use email address in the “reassign assistant” drop down and that way we won’t have to introduce last name at all. It is less user friendly, but does not cost &lt;em&gt;anything&lt;/em&gt;. Suddenly, something that we would not even consider in the beginning, becomes a viable option just on that basis.&lt;/p&gt;

&lt;p&gt;Now. Let us see how we could have avoided those inconsistencies early at a low cost. Let us rewind back to the point where no code has been written yet and we were laying out the first integration test. Except this time let us call it an acceptance test. And what that implies is not just a change of a buzzword but a change of how we think about it. Let us not think about what links and buttons users are going to click, but, instead, let us focus on what motivates them to take the hustle of doing so. Here is our first scenario written this way:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;scenario&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;create assistant profile&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;given_jessy_is_an_admin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;and_walter_is_a_sales_person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;when_jessy_wants_to_make_walter_an_assistant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;then_he_can_do_so_from_an_admin_interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Wait, what? Does Jessy create an assistant profile so that he can… create an assistant profile? That makes no sense. Let us think again. But no matter how hard we try, turns out, on its own, there is no better value in just creating assistant profiles. Which reveals the fact that there is &lt;em&gt;no&lt;/em&gt; value at all. And that is why sales haven’t created any profiles yet. They are pragmatic and also busy. They’d only invest time in doing something if the return on that investment was worth it. (of course, in reality, there can be plenty of other reasons, but I am making a point here!)&lt;/p&gt;

&lt;p&gt;So let us write something that adds value, shall we?&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;scenario&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;customer contacts an assistant by replying to introduction email&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;given_ned_just_requested_a_quote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;and_he_realised_that_he_forgot_to_ask_about_finance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;when_he_receives_an_email_introducing_an_assistant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;then_he_can_reply_to_this_email_with_his_question&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There is also going to be a “customer contacts an assistant from a quote page” scenario. And a scenario for every other way for a customer to exercise “contact assistant” &lt;em&gt;feature&lt;/em&gt;. But let us just focus on this one. &lt;/p&gt;

&lt;p&gt;Notice how it assumes that “assistant profile” is already an existing thing. By the time we’re done implementing this scenario (and its siblings), there will still be no user interface for creating those. There will, however, be an obvious need to create them. Since, otherwise, none of the above will produce any value. This is a good old “outside in” only this time applied on a higher level where no code, but the features themselves are being designed.&lt;/p&gt;

&lt;p&gt;So, perhaps, now it would be the right time to do the CRUD bit? It certainly is a better time, but let us keep exploring what “assistant” is without diving into admin interfaces just yet. Why? Because that admin ui is the direct reflection of our &lt;em&gt;current&lt;/em&gt; knowledge of what “assistant” is. When the knowledge changes, the code will have to be changed too. Best wait until our knowledge is complete.&lt;/p&gt;

&lt;p&gt;And there happens to be one more feature in the pile that is not about managing profiles - “reassign assistant”:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;scenario&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;admin reassigns assistant&amp;#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;given_tweedy_has_requested_a_quote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;and_rocky_has_been_assigned_to_assist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;when_he_gets_hit_by_a_bus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;then_an_admin_can_reassign_ginger_to_be_tweedies_assistant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While implementing the reassign drop down, we realise that an assistant also requires a last name. Good thing we didn’t rush with the admin ui. We’d have to go back and add it everywhere.&lt;/p&gt;

&lt;p&gt;Ok, now can we do the CRUD? Yes. But it is no longer just about the CRUD. As we’ve already established, there isn’t much value in it on its own. But what is that value then? There is one last piece of functionality left unspecified: initial assistant assignment. Remember that “you can’t assign anything if it does not exist yet” argument? Well now it is the right time to pull it out. So let us connect the two and complete the puzzle:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;scenario&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;admin creates an assistant&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;given_marty_is_a_sales_person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;and_rust_is_an_admin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;when_he_creates_an_assistant_profile_for_marty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;and_then_customer_reggie_requests_a_quote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;then_marty_becomes_reggies_assistant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And… cut!&lt;/p&gt;

&lt;p&gt;There are tools that support the this style of testing. Most notably, Cucumber (using Gherkin syntax). A lot of people don’t like Cucumber and they have good reasons for it. Still, in my opinion, it is well worth it.&lt;/p&gt;

&lt;p&gt;But this is not why I am mentioning it. One part of Gherkin is a feature narrative. It is a blob of text at the very top that is not mapped to any executable code. For that reason, it is often skipped because it is just words. But if you’re sold on that value thing I am talking about, that would be the starting point of exploring it. The starting point here is to understand why the business needs the feature, before getting into the details of individual scenarios. In the hunt for value, this will set you on the right track. And it is still “outside in” by the way, only at the even higher level. Back to our example we could write something like:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In order to reduce the drop out of potential customers,&lt;br /&gt;
we’d like to make their experience more personal and engaging by assigning an “assistant” to each quote request.&lt;br /&gt;
Assistants will follow up on quotes and will be there to answer any questions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, yeah, The Value. It is an incredibly important thing to question. The earlier the better. Doing so costs little compared to what it can save. And going back to our tests, it is kind of hidden when the test is written as a sequence of actions (imperative style), but a lot more obvious when it is written in terms of what users are trying to achieve and why (declarative style).&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ajax in Rails how it is meant to be</title>
   <link href="http://artemave.github.com/2014/07/10/ajax-in-rails-how-it-is-meant-to-be"/>
   <updated>2014-07-10T00:00:00+00:00</updated>
   <id>http://artemave.github.com/2014/07/10/ajax-in-rails-how-it-is-meant-to-be</id>
   <content type="html">
&lt;p&gt;&lt;em&gt;If you can use &lt;a href=&quot;https://github.com/rails/turbolinks&quot;&gt;turbolinks&lt;/a&gt; (e.g. brand new project), use it and read no further. It’ll cost you nothing and will get you a long way before you need anything more sophisticated.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Like so many others, the website I am currently working on is using client side js and ajax to make user experience snappier by not reloading page when they click links and buttons. But, ultimately, every user action results in a server call. No state is client side only.&lt;/p&gt;

&lt;p&gt;To do this, we intercept form submissions and link clicks in js, turn them into &lt;code&gt;$.ajax&lt;/code&gt; and handle the json or html returned from the server in success handler. Pretty standard. And pretty repetitive too. Why so? Because every time code follows the same pattern of intercepting the user event, rebinding to &lt;code&gt;$.ajax&lt;/code&gt; and handing the response. Over and over again.&lt;/p&gt;

&lt;p&gt;Rails is good in shortcutting common tasks away. And this case is no exception. Below I’ll demonstrate what rails has to offer using a simplified example of posting a comment into the comments thread (&lt;a href=&quot;https://github.com/artemave/railjax-demo&quot;&gt;source code&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Let us start with a non-ajax version (&lt;a href=&quot;https://github.com/artemave/railjax-demo/commit/7df1ee54c558fc6d051cba611832c64fd224d7de&quot;&gt;commit&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;controllers/comments_controller.rb&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;index&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@comments&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment_params&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:index&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;views/comments/index.html.erb&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-erb&quot; data-lang=&quot;erb&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;form_for&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_area&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@comments&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;views/comments/_comment.html.erb&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-erb&quot; data-lang=&quot;erb&quot;&gt;&lt;span class=&quot;x&quot;&gt;&amp;lt;div class=&amp;quot;comment&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;  &amp;lt;span&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;  &amp;lt;span&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now let us ajaxify adding new comment.&lt;/p&gt;

&lt;h2 id=&quot;json-way&quot;&gt;Json way&lt;/h2&gt;

&lt;p&gt;Intercept form submission and turn it into an ajax call that returns json (&lt;a href=&quot;https://github.com/artemave/railjax-demo/commit/84b7fd1bee5f7c20e443eb0a59b6b8dd01ca9b43&quot;&gt;commit&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;comments/comments_controller.rb&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment_params&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;respond_to&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:index&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;assets/javascripts/comments.js.coffee&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffeescript&quot; data-lang=&quot;coffeescript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;submit&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;#new_comment&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;(e) -&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$form = &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;attr&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;action&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.json&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;serializeArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;(comment) -&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$text = &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;&amp;lt;span&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;text&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$createdAt = &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;&amp;lt;span&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;created_at&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$newComment = &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;&amp;lt;div class=&amp;quot;comment&amp;quot;&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;append&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$createdAt&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;.comment:last&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;after&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$newComment&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This might not be the best way of client side rendering, but it immediately highlights the problem of duplicate rendering of the same thing - comment - on the client and on the server. So, instead of returning json, let us reuse server side template and return html instead.&lt;/p&gt;

&lt;h2 id=&quot;html-way&quot;&gt;HTML way&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/artemave/railjax-demo/commit/dcc848f2b1294cc978a78b08dd901e3d617a2c65&quot;&gt;commit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;controllers/comments_controller.rb&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment_params&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xhr?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:index&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;assets/javascripts/comments.js.coffee&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffeescript&quot; data-lang=&quot;coffeescript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;submit&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;#new_comment&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;(e) -&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$form = &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;action&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;serializeArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;(html) -&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;.comment:last&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;after&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That is where we normally stop. Our client side javascript is largely a combination of the two approaches above: json and html. But there is a third one where rails returns javascript that automatically gets evaled (by jquery-ujs) on successful response. Sounds evil. But let us see it in action.&lt;/p&gt;

&lt;h2 id=&quot;javascript-way&quot;&gt;Javascript way&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/artemave/railjax-demo/commit/2d06091a06850ba997d6bbcd4ad58fbd4bb5bbb2&quot;&gt;commit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;controllers/comments_controller.rb&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@comment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Comment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comment_params&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;respond_to&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;js&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;views/comments/create.js.coffee&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-coffeescript&quot; data-lang=&quot;coffeescript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;.comment:last&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;after&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;&amp;lt;%= j render @comment %&amp;gt;&amp;#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We also need to add &lt;code&gt;remote: true&lt;/code&gt; to &lt;code&gt;form_for&lt;/code&gt; in comments index.&lt;/p&gt;

&lt;h2 id=&quot;so&quot;&gt;So?&lt;/h2&gt;

&lt;p&gt;So why is this better than returning html?&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Less code. More specifically, less glue code. Glue code is repetitive and boring. Good riddance.&lt;/li&gt;
  &lt;li&gt;No reference to the dom selector (of the comments form). Therefor, less coupling to maintain. Plus you don’t have to make up names. Because &lt;em&gt;naming things is hard&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;Better code location. In json/html example the js code can be anywhere. Which means, harder to find. Whereas js template can only be in one place (its controller view path - &lt;code&gt;views/comments&lt;/code&gt;) and named after the action - &lt;code&gt;create.js.coffee&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Less client/server side code distinction. Means: less layers in development stack. Apart from returning javascript instead of html, js template is no different from html one. The same erb and rails helpers are available there.&lt;/li&gt;
  &lt;li&gt;No need to pass server side data to client js. That is where we normally start abusing html5 data attributes or simply store server side data (e.g. path helper values) in javascript variables on page load so it can be later accessed from javascript. Another bowl of glue.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;but-what-about-loading&quot;&gt;But what about “loading…”?&lt;/h2&gt;

&lt;p&gt;Sometimes you need to run javascript before the ajax request is sent. Typical example would be to disable user control (link or button) while request is going on and maybe show a loading spinner or something. Rails provides a simple shortcut for disabling controls: remote buttons and links accept &lt;code&gt;disable_with&lt;/code&gt; option that will disable a control while request is in progress optionally changing its text.&lt;/p&gt;

&lt;p&gt;Also, jquery-ujs broadcasts a &lt;a href=&quot;https://github.com/rails/jquery-ujs/wiki/ajax#custom-events-fired-during-data-remote-requests&quot;&gt;number of events&lt;/a&gt; which can be used to trigger global loading indicator or something along those lines. &lt;/p&gt;

&lt;p&gt;Obviously they can also be bound to specific links, forms and buttons but that is where it stops being pretty :)&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;&lt;/em&gt; (17 July 2014)&lt;/p&gt;

&lt;p&gt;Josh Chisholm suggested in comments a variation of js approach in which &lt;code&gt;AccountsController#create&lt;/code&gt; redirects to &lt;code&gt;index&lt;/code&gt; instead of rendering javascript (&lt;a href=&quot;https://github.com/artemave/railjax-demo/commit/7bf73734eecf554070a56f5feadb44545b5d00bb&quot;&gt;commit&lt;/a&gt;). The nice thing about it, is that there is no need for different &lt;code&gt;respond_to&lt;/code&gt; based on type of request (html or js) in &lt;code&gt;create&lt;/code&gt;. Request processing follows the same path regardless.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Git a Grep on Vim</title>
   <link href="http://artemave.github.com/2014/06/02/vim-git-grep"/>
   <updated>2014-06-02T00:00:00+00:00</updated>
   <id>http://artemave.github.com/2014/06/02/vim-git-grep</id>
   <content type="html">
&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt; &lt;em&gt;Combination of &lt;code&gt;.gitignore&lt;/code&gt; and &lt;code&gt;.agignore&lt;/code&gt; will do just fine (though it falls short on subdirectory specific ignores). So ignore the rest of this post.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’ve been a happy user of &lt;a href=&quot;https://github.com/rking/ag.vim&quot;&gt;ag.vim&lt;/a&gt; for years until recently I started to work on a phonegap project. Two things are special about it:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;./node_modules&lt;/li&gt;
  &lt;li&gt;compiled source files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both of these are included when grepping the project and, man, this is annoying. And slow.&lt;/p&gt;

&lt;p&gt;So today I’ve had enough and decided to do something. Surprisingly, quick googling didn’t reveal anything I could just copypaste somewhere, so I had to put on my bash pyjamas and sort this out like a pro.&lt;/p&gt;

&lt;p&gt;I’ve heard of &lt;code&gt;git-grep&lt;/code&gt; and the plan was to bolt it in instead of a stock &lt;code&gt;ag&lt;/code&gt;, but only if current vim folder is a git project (or a subfolder of one). This way none of the aformentioned types of files will be included in the search.&lt;/p&gt;

&lt;p&gt;So this is it. Two steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First&lt;/strong&gt;. Save this code to &lt;code&gt;agprg.sh&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# this is used as vim grepprg in conjunctions with ag.vim&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; -d .git &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; git rev-parse --git-dir &amp;gt; /dev/null 2&amp;gt;&lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt;1&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
  git grep -n &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;read &lt;/span&gt;git_grep&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;file_and_line&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;$git_grep&amp;quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; cut -d: -f1,2&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;$git_grep&amp;quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; sed &lt;span class=&quot;s1&quot;&gt;&amp;#39;s/[^:]*:[^:]*:\(.*\)/\1/&amp;#39;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;$match&amp;quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; awk &lt;span class=&quot;s2&quot;&gt;&amp;quot;{print index(\$0, \&amp;quot;$1\&amp;quot;)}&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;$file_and_line:$column:$match&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
  ag --column &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Make sure this is in the &lt;code&gt;$PATH&lt;/code&gt; and don’t forget to &lt;code&gt;chmod +x&lt;/code&gt; it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second&lt;/strong&gt;. Add this to &lt;code&gt;.vimrc&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;let g:agprg = &#39;agprg.sh&#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And voila, search. As in, The Search. Fast and furious.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;If you don’t use &lt;code&gt;ag.vim&lt;/code&gt; then you can still use this with &lt;code&gt;vimgrep&lt;/code&gt;. Just add this (in addition to the above) to &lt;code&gt;.vimrc&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;let g:grepformat=&quot;%f:%l:%c:%m&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/ggreer/the_silver_searcher&quot;&gt;The silver searcherer&lt;/a&gt; is still expected to be installed.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Faster Chrome extension development cycle</title>
   <link href="http://artemave.github.com/2013/12/28/faster-chrome-extension-development-cycle"/>
   <updated>2013-12-28T00:00:00+00:00</updated>
   <id>http://artemave.github.com/2013/12/28/faster-chrome-extension-development-cycle</id>
   <content type="html">
&lt;p&gt;Here is how developing chrome extension works out of the box:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;make a change&lt;/li&gt;
  &lt;li&gt;open “extensions” page&lt;/li&gt;
  &lt;li&gt;find your extension there&lt;/li&gt;
  &lt;li&gt;hit reload&lt;/li&gt;
  &lt;li&gt;test the change&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Steps 2,3,4 really should not be there. Doing this over an over again just makes me want to stop writing software and get into baby clothes instead.&lt;/p&gt;

&lt;p&gt;Turns out I am not the only one to feel that pain. There is an &lt;a href=&quot;https://chrome.google.com/webstore/detail/extensions-reloader/fimgfedafeadlieiabdeeaodndnlbhid&quot;&gt;Extenstion Reloader&lt;/a&gt; extension that, if combined with &lt;a href=&quot;https://github.com/guard/guard&quot;&gt;Guard&lt;/a&gt;, automates those steps.&lt;/p&gt;

&lt;p&gt;There is a bit of setup. But really is just a bit, assuming you’ve got a ruby interpreter and a terminal. It works on OSX and, with minor adjustments, on Linux. Not sure about Windows.&lt;/p&gt;

&lt;p&gt;Install &lt;code&gt;guard-shell&lt;/code&gt; gem:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ gem install guard-shell
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Add &lt;code&gt;Guardfile&lt;/code&gt; to the root of your project with the following contents:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:shell&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;watch&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/(js|css|html|json)$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;sb&quot;&gt;`open -g http://reload.extensions`&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In a terminal, cd into project folder and run &lt;code&gt;guard&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That is it. From now on, while guard is running, changing any file inside your project will result into extensions reload in a background.&lt;/p&gt;

&lt;p&gt;&amp;lt;/br&amp;gt;&lt;/p&gt;

&lt;p&gt;One caveat though, looks like Extension Reloader does not reload changes in manifest. Other than that, you got it!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>CIborg anywhere (not just on ec2)</title>
   <link href="http://artemave.github.com/2013/07/29/ciborg-anywhere"/>
   <updated>2013-07-29T00:00:00+00:00</updated>
   <id>http://artemave.github.com/2013/07/29/ciborg-anywhere</id>
   <content type="html">
&lt;p&gt;&lt;a href=&quot;https://github.com/pivotal/ciborg&quot;&gt;ciborg&lt;/a&gt; lets you easily create CI instances on Amazon ec2. It is truly amazing in the sense that it just works. Having recently been stubbed multiple times to death with &lt;a href=&quot;http://docs.opscode.com/chef/knife.html&quot;&gt;knife&lt;/a&gt; by &lt;a href=&quot;http://www.opscode.com/chef/&quot;&gt;chef&lt;/a&gt;, I am very grateful that tools like ciborg do exist. &lt;/p&gt;

&lt;p&gt;The only downside is that it is coupled to ec2 and ec2 only. Hopefully folks at pivotal labs will sort this out at some point, but as of this writing, that is the reality of it. So whether you want to explore other (cheaper) options such as &lt;a href=&quot;https://www.digitalocean.com/&quot;&gt;digitalocean&lt;/a&gt; you are out of luck.&lt;/p&gt;

&lt;p&gt;I’ve been looking into ciborg source code thinking I could cannibalise bits that are only specifically about provisioning and configuration, rather than creating/destroying instances. I got deep down that rabbit hole before I finally discovered that there is no need to do that at all. The ciborg commands responsible for this (&lt;code&gt;bootstrap&lt;/code&gt; and &lt;code&gt;chef&lt;/code&gt;) already are operating on the basis that there is a server somewhere and that its address is explicitly set in the config. In my case, all I had to do was to create a droplet on digitalocean and put its ip in &lt;code&gt;ciborg.yml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There a few things however that ciborg assumes about an instance:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;it is ubuntu 12.04&lt;/li&gt;
  &lt;li&gt;it has &lt;code&gt;ubuntu&lt;/code&gt; user account with sudo rights (NOPASSWD)&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;ubuntu&lt;/code&gt; user can ssh without password (hint: &lt;code&gt;ssh-copy-id ubuntu@your-ci-host&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After making sure my newly created droplet satisfied those conditions, I ran &lt;code&gt;ciborg bootstrap&lt;/code&gt; followed by &lt;code&gt;ciborg chef&lt;/code&gt; and watched the magic going. For a long while. Once that had finished, &lt;code&gt;ciborg open&lt;/code&gt; popped up a browser with jenkins already running my first build!&lt;/p&gt;

&lt;p&gt;In case of a small setup of only one ci instance I reckon losing the ability to automatically create/destroy instances is not a big deal. What is left, on the other hand, is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;jenkins behind nginx secure proxy&lt;/li&gt;
  &lt;li&gt;jenkins that can pull from your github private repos&lt;/li&gt;
  &lt;li&gt;bootstrapped ci environment suitable out the box for simple stuff like rails&lt;/li&gt;
  &lt;li&gt;the multitude of recipes from travis-ci cookbooks pluggable by simply adding them to &lt;code&gt;ciborg.yml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the &lt;code&gt;ciborg.yml&lt;/code&gt; (that has gotten smaller as a result of all the this):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;---
ssh_port: 22
master: 12.13.14.15
recipes:
- pivotal_ci::jenkins
- pivotal_ci::limited_travis_ci_environment
- pivotal_ci
- mongodb::default # that is how easy to add recipe from travis-ci cookbooks!
cookbook_paths:
- ./chef/cookbooks/
- ./chef/travis-cookbooks/ci_environment
- ./chef/project-cookbooks
node_attributes:
  travis_build_environment:
    user: jenkins
    group: nogroup
    home: /var/lib/jenkins
  nginx:
    basic_auth_user: login
    basic_auth_password: password
  jenkins:
    builds:
    - name: master
      repository: git@github.com:shopa/shopa.git
      branch: master
      command: script/ci_build.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;TL;DR;&lt;/strong&gt; Use ciborg! Now anywhere!&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Faster Android emulator with Android-x86 on VirtualBox</title>
   <link href="http://artemave.github.com/2013/05/01/faster-android-emulator-with-android-x86-on-virtualbox"/>
   <updated>2013-05-01T00:00:00+00:00</updated>
   <id>http://artemave.github.com/2013/05/01/faster-android-emulator-with-android-x86-on-virtualbox</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt; &lt;em&gt;Setting up VirtualBox only makes sense on OSX. Lots of people suggested in comments that using KVM on Linux works just fine.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A considerable amount of yak shaving, proportional to how big or new the next task is, normally accompanies my development activities. I am very new to Android development, so a lot of yak shaving has to happen before I start producing anything useful.&lt;/p&gt;

&lt;p&gt;This post itself is yak shaving and as such makes a good example of meta yak shaving (it is, and describes yak shaving). The described yak shaving is about speeding up android emulator, as you might have already guessed.&lt;/p&gt;

&lt;p&gt;So. The first thing on that quest is a low hanging fruit of &lt;a href=&quot;http://developer.android.com/tools/devices/emulator.html#accel-vm&quot;&gt;Virtual Machine Acceleration&lt;/a&gt;. Sadly, it is low rewarding too. It barely promotes emulator from being a joke to “unbearably slow”. Not good enough.&lt;/p&gt;

&lt;p&gt;Next stop is more interesting - &lt;a href=&quot;http://www.android-x86.org/&quot;&gt;Android-x86&lt;/a&gt;. It ports Android on x86 platform so it can be installed on a PC or, more specific to our needs, on a &lt;a href=&quot;https://www.virtualbox.org/&quot;&gt;VirtualBox&lt;/a&gt; VM. Let us go through the setup, as there are few hoops to be bear in mind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create vm&lt;/strong&gt; with sensible defaults (Other Linux, 512 Mb RAM, HD 2 GB). Boot with &lt;a href=&quot;http://www.android-x86.org/download&quot;&gt;the ISO&lt;/a&gt; that you need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup port forwarding&lt;/strong&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;% VBoxManage modifyvm &quot;VM_NAME_HERE&quot; --natpf1 &quot;console,tcp,,5554,,5554&quot;    
% VBoxManage modifyvm &quot;VM_NAME_HERE&quot; --natpf1 &quot;adb,tcp,,5555,,5555&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;so that &lt;code&gt;adb&lt;/code&gt; (Android Debug Bridge) can connect to vm:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;% adb connect localhost
connected to localhost:5555
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At this point we’ve got a fully functional Android VM that we can deploy to from Eclipse. To do that simply use a run configuration with target set to “Always prompt to pick device”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix resolution&lt;/strong&gt; because it is not mobile by default. Here is how to do it (mostly copied from &lt;a href=&quot;http://stackoverflow.com/questions/6202342/switch-android-x86-screen-resolution/8273560#8273560&quot;&gt;here&lt;/a&gt;):&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Add custom screen resolution:&lt;/p&gt;

    &lt;pre&gt;&lt;code&gt; % VBoxManage setextradata &quot;VM_NAME_HERE&quot; &quot;CustomVideoMode1&quot; &quot;480x800x16&quot;
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;Figure out what is the ‘hex’-value for your &lt;code&gt;VideoMode&lt;/code&gt;:
    &lt;ul&gt;
      &lt;li&gt;Start the VM&lt;/li&gt;
      &lt;li&gt;In GRUB menu enter &lt;code&gt;a&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;In the next screen append &lt;code&gt;vga=ask&lt;/code&gt; and press Enter&lt;/li&gt;
      &lt;li&gt;Find your resolution and write down/remember the ‘hex’-value for &lt;code&gt;Mode&lt;/code&gt; column&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Translate the value to decimal notation (for example &lt;code&gt;360&lt;/code&gt; hex is &lt;code&gt;864&lt;/code&gt; in decimal).&lt;/li&gt;
  &lt;li&gt;Go to &lt;code&gt;menu.lst&lt;/code&gt; and modify it:&lt;br /&gt;
    &lt;ul&gt;
      &lt;li&gt;From the GRUB menu select &lt;code&gt;Debug Mode&lt;/code&gt;  &lt;/li&gt;
      &lt;li&gt;&lt;code&gt;vi /mnt/grub/menu.lst&lt;/code&gt; to add &lt;code&gt;vga=864&lt;/code&gt; (if your ‘hex’-value is 360) to the end of kernel boot parameters.
Depending on what device you want to emulate (and probably current state of Android-x86 project) you might also want to change DPI (e.g. add/change &lt;code&gt;DPI=240&lt;/code&gt;).&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code&gt;reboot -f&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;br /&gt;
Finally, to enable &lt;strong&gt;proper mouse support&lt;/strong&gt;, in VM settings (System tab) uncheck “Enable absolute pointing device”.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
That is it. Fast emulator, comparable to that of iOS. If only Google had something like that in their sdk. But hey, that would mean what, no yak shaving? Unacceptable.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://dl.dropboxusercontent.com/u/362737/android-emulator.jpg&quot; alt=&quot;emulator&quot; /&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Dependency lookup with binding_of_caller</title>
   <link href="http://artemave.github.com/2013/03/30/dependency-lookup-with-binding_of_caller"/>
   <updated>2013-03-30T00:00:00+00:00</updated>
   <id>http://artemave.github.com/2013/03/30/dependency-lookup-with-binding_of_caller</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt; &lt;em&gt;Turns out &lt;code&gt;binding_of_caller&lt;/code&gt; is not recommended for production apps&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Rails project, RetailersController, products action, circa 2013 AD:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;products&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@products&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_retailer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;created_at desc&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;paginate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;per_page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That query is not too bad. I can totally live with it. But I’ve been roaming around this controller for the last few days and every time I am passing by this piece of code it just does not make the journey better. The effect is somewhat enhanced by three other actions that look identical to this one (except they query for other things). So today I finally stopped and decided to sort it out like a pro! Well. That, and I had to to put some tests around those actions and stubbing out a query such as the one above is just so much the opposite of fun… But that would be a boring start for a post.&lt;/p&gt;

&lt;p&gt;Despite Rails community actively exploring The Right Way™ to deal with the logic that does not fit into Rails MVC, the most common thing amongst those who just need to Get Shit Done™ is to move the query logic into model. Ok. Second most common. The first one admittedly is to leave it as is and get some shit done instead. However, I do not belong to that category of people, since I am writing this post instead of getting shit done. And neither are you, since you are reading this post instead of getting shit done. So let us disregard those lucky bastards and see what is down that rabbit hole.&lt;/p&gt;

&lt;p&gt;Ok, moving the query into the model is not cool. So what is? I don’t know! And when I don’t know, I tend to start with making stuff up. What would an ideal client code look like? It would be nice to have something that is easy to stub out in controller test. Also, it would make it more readable to see where the products are coming from. And certainly none of the pagination/sorting machinery should make it through. Something like this:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;products&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@products&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PrepareForTableView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_retailer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This way retailer is still aware of its products but on the other hand controller does not need to know how to sort and paginate. Also, it looks like a decorator. Or presenter? Whatever. Design patterns - check.&lt;/p&gt;

&lt;p&gt;The slightly annoying problem however is that it is not going to work. At least not without also passing &lt;code&gt;params[:page]&lt;/code&gt;. Ok. How does it look now?&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;vi&quot;&gt;@products&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PrepareForTableView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_retailer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Acceptable. But not as good as before. What can be done?&lt;/p&gt;

&lt;p&gt;Conventions work great for Rails. So let us introduce one. The one that postulates that &lt;code&gt;PrepareForTableView&lt;/code&gt; is only ever going to be used in a controller. So what? Well that means that its methods will always be called from the context of a controller. If only we could get hold of that context. Oh, wait. Isn’t it what ruby &lt;code&gt;Binding&lt;/code&gt; is for?&lt;/p&gt;

&lt;p&gt;Quick recap: &lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Objects of class Binding encapsulate the execution context at some
particular place in the code and retain this context for future use.
The variables, methods, value of self, and possibly an iterator block
that can be accessed in this context are all retained.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ok, but how is this useful? The profit comes from the fact that ruby can &lt;code&gt;eval&lt;/code&gt; stuff in specified context (binding). Like this:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controller_context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;params[:page]&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So all that is needed for our case is access to binding of caller. &lt;a href=&quot;https://github.com/banister/binding_of_caller&quot;&gt;binding_of_caller&lt;/a&gt; gem does just that (and does it well):&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;PrepareForTableView&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;module_function&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;relation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;of_caller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;params[:page]&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;created_at desc&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paginate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;per_page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Good.&lt;/p&gt;

&lt;p&gt;Now let us finally get some shit done and allow retailer to download products in xls. xls version does not require sorting. Also our client is specific about having more than one page worth of data in there. Having controller context at hand gives our module a lot of flexibility so the above can be achieved without even changing client code:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# renamed as it is no longer just about table view&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;WithinRequestContext&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;module_function&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;adjust_data&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;of_caller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;[request, params]&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Mime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;HTML&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;created_at desc&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paginate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;per_page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The client code remains the same:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;products&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@products&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WithinRequestContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adjust_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_retailer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Last, but not least. Unit testing this module is simple. The test just needs to have &lt;code&gt;params&lt;/code&gt; and &lt;code&gt;request&lt;/code&gt; defined:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WithinRequestContext&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;request&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:relation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;relation&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;before&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
         &lt;span class=&quot;c1&quot;&gt;# relation follows builder pattern, so should the stub&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:paginate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;#adjust_data&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Non HTML request&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;before&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;some mime type&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

          &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;returns unmodified relation&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should_not_receive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should_not_receive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:paginate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;no&quot;&gt;WithinRequestContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adjust_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;HTML request&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;before&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Mime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;HTML&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

          &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;paginates&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should_receive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:paginate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;per_page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;no&quot;&gt;WithinRequestContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adjust_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

          &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;orders by created_at desc&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should_receive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;created_at desc&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;no&quot;&gt;WithinRequestContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adjust_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relation&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That is all I’ve got on dependency lookup with &lt;a href=&quot;https://github.com/banister/binding_of_caller&quot;&gt;binding_of_caller&lt;/a&gt; - an awesome little gem used by &lt;a href=&quot;https://github.com/pry/pry-stack_explorer&quot;&gt;pry-stack_explorer&lt;/a&gt; and &lt;a href=&quot;https://github.com/charliesome/better_errors&quot;&gt;better_errors&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is worth mentioning that &lt;em&gt;it is all fun and games until someone gets to maintain it&lt;/em&gt;. I’ll wait till then before start recommending the above approach… But you don’t have to!&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Refactoring in command line and vim</title>
   <link href="http://artemave.github.com/2013/02/14/refactoring-in-cmd-line-and-vim"/>
   <updated>2013-02-14T00:00:00+00:00</updated>
   <id>http://artemave.github.com/2013/02/14/refactoring-in-cmd-line-and-vim</id>
   <content type="html">
&lt;p&gt;Refactoring in dynamic languages may seem like a hassle. And it is! There is no IDE to perform the renaming magic, it is all a bit manual, hence error prone and scary. The good news is, the command line almighty is on your side. The not so good news is, its capabilities are so vast that it is often difficult to remember how to do X, Y and Z required to solve a particular problem. &lt;/p&gt;

&lt;p&gt;I am working on a rails project at the moment. Every now and then there is a ‘domain object’ (model, controller, views, tests, etc.) that needs to be renamed. It is a good example of refactoring so I figured it deserves a little coverage.&lt;/p&gt;

&lt;p&gt;So, to business. There is model called &lt;code&gt;OfflineDeal&lt;/code&gt; that needs to be turned into &lt;code&gt;InstoreDeal&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;renaming-files&quot;&gt;renaming files&lt;/h2&gt;

&lt;p&gt;Let us find all the files with &lt;code&gt;offline_deal&lt;/code&gt; in their name:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;% find . -name &quot;*offline_deal*&quot; 
./app/assets/stylesheets/retailer/offline_deals.css.less
./app/controllers/offline_deals_controller.rb
./app/models/offline_deal.rb
./app/views/offline_deals
./db/migrate/20130117121729_create_retailer_offline_deals.rb
./db/migrate/20130118164309_change_vouchers_deal_id_to_offline_deal_id.rb
./db/migrate/20130121150012_add_referrer_reward_to_offline_deals.rb
./db/migrate/20130130172326_add_fine_print_to_offline_deals.rb
./db/migrate/20130131122749_rename_offline_deals_description_to_voucher_text.rb
./spec/controllers/offline_deals_controller_spec.rb
./spec/factories/offline_deals.rb
./spec/models/offline_deal_spec.rb
./spec/routing/offline_deals_routing_spec.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And pipe the result of find command into vim:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;% find . -name &quot;*offline_deal*&quot; | vim -
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice that not all of these files require renaming. Migration files reflect change history and should therefor stay untouched. Let us get rid of the lines with those file names:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:g/migrate/d
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and change the remaining ones into rename command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:%s/\(.*\)offline_deal\(.*\)/git mv &amp;amp; \1instore_deal\2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each line is now turned into &lt;code&gt;git mv&lt;/code&gt; command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;...
git mv ./app/models/offline_deal.rb ./app/models/instore_deal.rb
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And here is the magic part. Run them all:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:w !sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Voila, files are renamed.&lt;/p&gt;

&lt;h2 id=&quot;file-changes&quot;&gt;file changes&lt;/h2&gt;

&lt;p&gt;Inside project files there are 3 things to change: method references, symbol references and class name references.&lt;/p&gt;

&lt;p&gt;The first two (including dynamic methods such as path helpers or AR finders) are covered with replacing all occurrences of &lt;code&gt;offline_deal&lt;/code&gt; with &lt;code&gt;instore_deal&lt;/code&gt;. Let us do it first.&lt;/p&gt;

&lt;p&gt;Find all matching files and open them in vim:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;% vim $(ag offline_deal -l app lib spec features config)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I am using &lt;a href=&quot;https://github.com/ggreer/the_silver_searcher&quot;&gt;ag&lt;/a&gt; - a faster alternative to &lt;a href=&quot;http://betterthangrep.com/&quot;&gt;ack&lt;/a&gt; (a popular alternative to grep). &lt;code&gt;-l&lt;/code&gt; option tells it to only print names of files with matches, not the matches themselves (as it does by default). That list of file names is passed to vim so they are all opened in buffers.&lt;/p&gt;

&lt;p&gt;Now for each opened buffer let us perform the replace and save the changes:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:bufdo %s/offline_deal/instore_deal/g | update
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To rename class names, we repeat the above steps with &lt;code&gt;OfflineDeal&lt;/code&gt; -&amp;gt; &lt;code&gt;InstoreDeal&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;db-changes-rails-specific&quot;&gt;db changes (rails specific)&lt;/h2&gt;

&lt;p&gt;This part has got nothing to do with command line refactoring and it is only here for the sake of completeness.&lt;/p&gt;

&lt;p&gt;We rename db table &lt;code&gt;offline_deals&lt;/code&gt; to &lt;code&gt;instore_deals&lt;/code&gt; in a migration. There are other tables referencing it, so corresponding foreign keys and index columns should also be changed.&lt;/p&gt;

&lt;h2 id=&quot;run-tests&quot;&gt;run tests&lt;/h2&gt;

&lt;p&gt;No tests? Time to panic!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Better Shopify app integration testing</title>
   <link href="http://artemave.github.com/2012/12/22/better-shopify-integration-tests"/>
   <updated>2012-12-22T00:00:00+00:00</updated>
   <id>http://artemave.github.com/2012/12/22/better-shopify-integration-tests</id>
   <content type="html">
&lt;p&gt;Part of the system I am currently working on is to act as Shopify application. That is to be able to interact with its API. &lt;/p&gt;

&lt;p&gt;I have an integration test (cucumber/capybara) that uses real Shopify retailer site, however, that approach has its obvious drawbacks: such tests are slower and they fail every now and then because of network issues. It is a good start while working on a feature, but it is just not good enough to have it in a an everyday test suite. &lt;/p&gt;

&lt;p&gt;On the other hand, a test that uses real site is closer to reality. So, ideally, I want to keep both options, but only run the latter one occasionally to check higher lever integration.&lt;/p&gt;

&lt;p&gt;My test scenario consists of three steps: application installation, buying something on retailer site and querying api for order details. &lt;/p&gt;

&lt;p&gt;Below I am going to cover how to make all three not depend on actual shopify site, but still be able to dynamically switch back to that path.&lt;/p&gt;

&lt;h2 id=&quot;install-application&quot;&gt;install application&lt;/h2&gt;

&lt;p&gt;Shopify is using &lt;code&gt;OAuth2&lt;/code&gt; to authenticate and grant permissions to the application (that is, install). If your app is a rails one, then you must be using &lt;a href=&quot;https://github.com/Shopify/shopify_api&quot;&gt;shopify_api&lt;/a&gt; gem which in turn is using &lt;a href=&quot;https://github.com/Shopify/omniauth-shopify-oauth2&quot;&gt;omniauth-shopify-oauth2&lt;/a&gt; under the hood. But you probably know that anyway. &lt;/p&gt;

&lt;p&gt;Lesser known that &lt;a href=&quot;https://github.com/intridea/omniauth&quot;&gt;omniauth&lt;/a&gt; has a pretty elegant &lt;a href=&quot;https://github.com/intridea/omniauth/wiki/Integration-Testing&quot;&gt;solution&lt;/a&gt; for integration testing. All you have to do is to stick the following somewhere in &lt;code&gt;env.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;OmniAuth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test_mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;OmniAuth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_mock&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:shopify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;shopify&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;credentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;test_token_blah&amp;#39;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;From their docs: &lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Once you have enabled test mode, all requests to OmniAuth will be short circuited to use the mock authentication hash as described below. A request to &lt;code&gt;/auth/provider&lt;/code&gt; will redirect immediately to &lt;code&gt;/auth/provider/callback&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So now in the step definition we need to distinguish between authenticating locally or against real shopify. Assuming there is link to install app on a current page, here is helper that does the job:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;install_app&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;click_link&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;install app&amp;#39;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$http_stubbed&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# then &amp;#39;install app&amp;#39; link will bring us to real shopify site admin area&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fill_in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Email Address&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;real_admin_user@example.com&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fill_in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Password&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;real_password&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;click_button&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Sign In&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;click_button&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Install&amp;#39;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;$http_stubbed&lt;/code&gt; is set via passing an environment variable when running cucumber:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# env.rb&lt;/span&gt;
&lt;span class=&quot;vg&quot;&gt;$http_stubbed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;UNSTUB_HTTP&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;   &lt;/p&gt;

&lt;h2 id=&quot;buying-a-product&quot;&gt;buying a product&lt;/h2&gt;

&lt;p&gt;In my application, when user buys something on shopify store, some custom javascript (added to shopify success page) calls back home to trigger order processing. The system does not care if it is a real shopify success page. So all we need is a page that, when visited, runs that script. Having done that, let us introduce a helper that will conditionally buy either real product or a fake local one:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;buy_banana&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;$http_stubbed&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# test route that leads to our fake success page&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;visit&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/test_retailer/banana_success&amp;#39;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;visit&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;http://real-store.myshopify.com/products/banana&amp;#39;&lt;/span&gt;    
    &lt;span class=&quot;n&quot;&gt;add_current_product_to_cart&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;purchase_contents_of_cart&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt; &lt;br /&gt;
## querying Shopify API for order details&lt;/p&gt;

&lt;p&gt;Once the order is purchased, our system goes away and queries the Shopify API to get order details and do some mumbo jumbo with it. Since the goal is to replace real Shopify, we need to stub out those interactions, but in such way that our system never notices the difference.&lt;/p&gt;

&lt;p&gt;Common approach to stub HTTP interactions is to use &lt;a href=&quot;https://github.com/myronmarston/vcr&quot;&gt;VCR&lt;/a&gt; gem. It records low level HTTP interactions, stores them in files and allows to replay them later in tests.&lt;/p&gt;

&lt;p&gt;On the surface that seems strait forward, but doing it right™ ended up being the most confusing and difficult part of the whole setup. Let us cover things I had troubles with bit by bit.&lt;/p&gt;

&lt;h3 id=&quot;ignore-unwanted-requests&quot;&gt;ignore unwanted requests&lt;/h3&gt;

&lt;p&gt;Once in playback mode, VCR will blow up on any request it does not know about. This is totally sensible, but may (and will) cause confusion at first. Good news is that VCR can be configured to ignore certain requests. Here is an example from my setup:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;VCR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ignore_localhost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ignore_request&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# this is just an example obviously&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/(?&amp;lt;!vasily-on-shopify.)localtest\.me|www.google-analytics.com/&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt; &lt;br /&gt;
### binary headers&lt;/p&gt;

&lt;p&gt;By default, all headers in VCR fixtures (this is only in case of Shopify though) will be in binary representation. That is not always handy, especially if you need to dynamically change some values in there. This problem is described in more details &lt;a href=&quot;https://groups.google.com/forum/?fromgroups=#!topic/vcr-ruby/2sKrJa86ktU&quot;&gt;here&lt;/a&gt;. And here is the workaround:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;VCR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default_cassette_options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;serialize_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:syck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# so that headers are human readable&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt; &lt;br /&gt;
### binary response body&lt;/p&gt;

&lt;p&gt;The above trick does not solve another binary related problem: response body. That is because it is genuine binary data and it took me quite a while to figure out exactly what the heck this binary represents. Turns out the response content is gzipped. &lt;/p&gt;

&lt;p&gt;Why is this a problem? It becomes one the second you need to dynamically modify response data. In my case, order created timestamp has to be changed so that order passes some validation inside my app. It is easy to find created_at in human readable response json and change its value to &lt;code&gt;&amp;lt;%= Time.now %&amp;gt;&lt;/code&gt;. The same is obviously impossible with binary data.&lt;/p&gt;

&lt;p&gt;So here how I deal with this:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;VCR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;before_record&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interaction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;zlib&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;stringio&amp;#39;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interaction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;user-agent&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/ShopifyAPI/&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;content_encoding_header&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interaction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;content-encoding&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;                 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interaction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;interaction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content_encoding_header&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;gzip&amp;#39;&lt;/span&gt;
                                    &lt;span class=&quot;n&quot;&gt;interaction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delete&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;content-encoding&amp;#39;&lt;/span&gt;
                                    &lt;span class=&quot;no&quot;&gt;Zlib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;GzipReader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;StringIO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;
                                  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                                    &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;
                                  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above code unzips content before cassette is recorded.&lt;/p&gt;

&lt;h3 id=&quot;rerecord-cassettes&quot;&gt;rerecord cassettes&lt;/h3&gt;

&lt;p&gt;I want to be able to rerecord cassettes every now and then to keep up with the reality. One problem here is that it erases all manual modifications made to VCR fixtures (e.g, created_at ERBfication).&lt;/p&gt;

&lt;p&gt;The solution here is to employ &lt;code&gt;c.before_record&lt;/code&gt; hook more heavily. It might end up in more code, you’re going to need to parse json, change it and serialize back, but the result is worth it. Here an example:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;VCR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;before_record&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interaction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# change created_at to Time.now so it passes timebox check&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interaction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;uri&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/orders.*\.json/&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interaction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;order&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# means if not 401&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;order&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;created_at&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;lt;%= Time.now %&amp;gt;&amp;#39;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;interaction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Also, when rerecording, do not use VCR record mode &lt;code&gt;:all&lt;/code&gt; as it only overwrites the same requests and adds new ones, but does not wipe out the old unmatched ones. Simply remove fixture file instead.&lt;/p&gt;

&lt;p&gt;And that is about it! Simple, isn’t it? But jokes apart, I believe fiddling with tests at such level is an investment in understanding your system better which in turn allows to find bugs earlier and fix them while in comfort zone (that is, while the context is in your head and the code is not in production) &lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>SlowDown.vim</title>
   <link href="http://artemave.github.com/2012/11/12/slowdown-vim"/>
   <updated>2012-11-12T00:00:00+00:00</updated>
   <id>http://artemave.github.com/2012/11/12/slowdown-vim</id>
   <content type="html">
&lt;p&gt;I am a vim user.&lt;/p&gt;

&lt;p&gt;As such, I take continuously enhancing my vim-fu seriously, to keep impressing myself and, of course, others too. It is just fun. And not much beyond. Personally I don’t understand the argument for learning vim ‘because it makes you more productive’. I mean, things do get much faster in vim by all means, but if you are after productivity, mastering a text editor just does not seem to be the top priority. I don’t think vim can (or should) be sold this way these days. Imagine Yoda saying ‘Use the Force Luke - it will make you more productive’. They would have never made it past episode IV!&lt;/p&gt;

&lt;p&gt;Learning other languages, reading other people’s code and so on - that is what going to make you more productive.&lt;/p&gt;

&lt;p&gt;However. It wasn’t always like that. There were times when mastering vim moves was the matter of life and death. A lot of what I largely consider as a way to show off (perhaps even recursive macros!) was once a prerequisite to getting things done. It was in the era of Slowness. Slow terminals. Or teletypes. Or God knows what else, but the point is that a war could have started and finished before user input would show up as output. No wonder people were getting far more advanced in far shorter time than I am today.&lt;/p&gt;

&lt;p&gt;This first occurred to me once when I got stuck editing a perl script over ssh tunnel through two (or three?) servers. Don’t ask me how I got into this situation. Sometimes shit like that just happens. So, things have gotten really slow and by the end of day two I found myself using moves I never even thought I knew. That got me thinking. What if there were some sort of a way to slow my everyday vim down? That would naturally force me to trade the amount of keystrokes to their more efficient vim alternatives.&lt;/p&gt;

&lt;p&gt;And that is how &lt;a href=&quot;https://github.com/artemave/slowdown.vim&quot;&gt;SlowDown.vim&lt;/a&gt; was born. Perhaps the only vim plugin on the planet that (deliberately) makes user experience worse. And possibly the smallest one too.&lt;/p&gt;

&lt;p&gt;Check it out. It is fun!&lt;/p&gt;

&lt;p&gt; 
 &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Special thanks to &lt;a href=&quot;https://github.com/adiel&quot;&gt;Adrian Longley&lt;/a&gt; for proof reading&lt;/em&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Stub like a surgeon, spy like James Bond with REST-assured</title>
   <link href="http://artemave.github.com/2012/05/27/stub-like-a-surgeon-spy-like-james-bond-with-rest-assured"/>
   <updated>2012-05-27T00:00:00+00:00</updated>
   <id>http://artemave.github.com/2012/05/27/stub-like-a-surgeon-spy-like-james-bond-with-rest-assured</id>
   <content type="html">
&lt;p&gt;This is the story of &lt;a href=&quot;https://github.com/BBC/REST-assured&quot;&gt;REST-assured&lt;/a&gt;, a testing tool for isolating automated integration tests from external HTTP-based dependencies. Unlike alternatives that I know of (most notably &lt;a href=&quot;https://github.com/myronmarston/vcr&quot;&gt;VCR&lt;/a&gt;), it does not stub out the underlying HTTP client library, but rather is an actual service that is configurable to respond in arbitrary ways. As an immediate result it is language agnostic, supports spying and… Let me tell you the whole story first.&lt;/p&gt;

&lt;p&gt;I work in BBC Future Media. The problem of testing systems talking to each other via HTTP is extremely relevant here. There are a countless number of services that provide more or less RESTful APIs and in turn consume from others. None of the products (that I know of) are standalone.&lt;/p&gt;

&lt;p&gt;When it comes to integration testing (in a particular project) we are forced to think hard how to represent those dependencies to our application. It would be nice of course to simply spin up a virtual machine with the services needed next to our code, but that sadly is rarely an option, not the least because some of those services are in development themselves.&lt;/p&gt;

&lt;p&gt;Lots of tools adressing this problem arose in such a highly ‘fertilized’ environment. Some are hidden in corporate SVN repos, and some have seen the light of day (&lt;a href=&quot;https://github.com/featurist/rack-stubs&quot;&gt;rack-stubs&lt;/a&gt;, &lt;a href=&quot;https://github.com/AndrewVos/anmo&quot;&gt;anmo&lt;/a&gt;). REST-assured is one of a kind that eventually grew into something more generic and hopefully can be helpful out there in the wild.&lt;/p&gt;

&lt;p&gt;When I first faced this problem, I thought for a bit and started using hand-crafted fixtures which were served directly from tests. As the number of tests grew, the focus shifted from creating new fixtures towards reusing existing ones. Further down the line, making them configurable started to sound like a good idea. Then one day it became apparent that it is quite hard to figure out their state during non-trivial tests. And so it went on and on, presenting new challenges.&lt;/p&gt;

&lt;p&gt;And it was for that reason (not because programmers love to create frameworks!), it didn’t take too long till I found myself pulling all that stuff out into a framework. Well, OK, not a framework strictly speaking, but something along those lines.&lt;/p&gt;

&lt;p&gt;My current project here is a ‘control panel’ type of thing that sits between multiple external systems, presents their data in the browser, issues control requests to some of them based on user actions, delivers responses back to the user and updates the state of things in others.&lt;/p&gt;

&lt;p&gt;We keep development within the BDD cycle. Every feature starts with automated acceptance test. For this we are using &lt;a href=&quot;http://cukes.info/&quot;&gt;Cucumber&lt;/a&gt; with ruby. Once we get the meaningful failure out it, we go down to Javascript unit tests (using &lt;a href=&quot;http://pivotal.github.com/jasmine/&quot;&gt;Jasmine&lt;/a&gt;) and when those pass, end up on Java layer where JUnit and &lt;a href=&quot;http://code.google.com/p/mockito/&quot;&gt;Mockito&lt;/a&gt; help us to test drive the back-end.&lt;/p&gt;

&lt;p&gt;On a side note. Nowadays there is a nice alternative for Java projects - &lt;a href=&quot;https://github.com/cucumber/cucumber-jvm&quot;&gt;cucumber-jvm&lt;/a&gt; - so that everything can be kept within the same project, language, libraries, etc. It looks very impressive, but on the other hand I kind of appreciate the fact that there is a ten foot wall between production code and integration tests. We are forced to treat the system as a black box.&lt;/p&gt;

&lt;p&gt;With a few exceptions, every scenario in our suite has a &lt;code&gt;Given&lt;/code&gt; step that implies data lookup in external systems. And many of our &lt;code&gt;Then&lt;/code&gt; steps verify that certain requests have happened in a particular manner. So being able to stub/spy on HTTP interactions becomes a prerequisite to being able to test anything at all.&lt;/p&gt;

&lt;p&gt;Below I am going to cover few typical scenarios and how REST-assured comes at rescue. There will be ruby code (just because there is ruby client library), but it isn’t limited to ruby and can be controlled from within any environment capable of making http requests (e.g, curl will do). Or even through web interface.&lt;/p&gt;

&lt;p&gt;Let us summon an imaginary client who wants an application that shows most popular tweets for a search criteria. He anticipates that users aren’t generally strong on command line and also won’t be bothered installing a native client. So he wants a webapp. All right. We unfold our cucumber communicator and agree to start with the following simplest feature:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;there&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;are&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;no&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;popular&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tweets&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;about&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;X&amp;quot;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;search&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;popular&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tweets&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;about&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;X&amp;quot;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;see&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;there&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;are&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;no&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;popular&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tweets&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;about&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;X&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I am not going to go into details of setting up a &lt;a href=&quot;http://www.sinatrarb.com/&quot;&gt;Sinatra&lt;/a&gt; and Cucumber project (check out the demo project &lt;a href=&quot;https://github.com/artemave/REST-assured-example&quot;&gt;here&lt;/a&gt;). Except for one thing which is relevant to what we are talking about. To be able to stub Twitter’s search API, let us inject its host as a dependency (so to speak):&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# tweet_checker.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;TWITTER_HOST&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;TWITTER_HOST&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;http://search.twitter.com&amp;#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and configure it to point to the REST-assured instance in tests before loading application code:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# env.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;TWITTER_HOST&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;http://localhost:4578&amp;#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;../../tweet_checker&amp;#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now let us find out what real Twitter returns when nothing is found:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;% curl &lt;span class=&quot;s1&quot;&gt;&amp;#39;http://search.twitter.com/search.json?q=asdfsdfrefsdfsdgs&amp;amp;result_type=popular&amp;#39;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;completed_in&amp;quot;&lt;/span&gt;:0.0070,&lt;span class=&quot;s2&quot;&gt;&amp;quot;max_id&amp;quot;&lt;/span&gt;:0,&lt;span class=&quot;s2&quot;&gt;&amp;quot;max_id_str&amp;quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&amp;quot;0&amp;quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&amp;quot;page&amp;quot;&lt;/span&gt;:1,&lt;span class=&quot;s2&quot;&gt;&amp;quot;query&amp;quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&amp;quot;asdfsdfrefsdfsdgs&amp;quot;&lt;/span&gt;,
&lt;span class=&quot;s2&quot;&gt;&amp;quot;results&amp;quot;&lt;/span&gt;:&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&amp;quot;results_per_page&amp;quot;&lt;/span&gt;:15,&lt;span class=&quot;s2&quot;&gt;&amp;quot;since_id&amp;quot;&lt;/span&gt;:0,&lt;span class=&quot;s2&quot;&gt;&amp;quot;since_id_str&amp;quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And use the result to craft response in the Given step:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^there are no popular tweets about &amp;quot;([^&amp;quot;])*&amp;quot;$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;RestAssured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;fullpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;/search.json?q=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;amp;result_type=popular&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%({&amp;quot;completed_in&amp;quot;:0.0070,&amp;quot;max_id&amp;quot;:0,&amp;quot;max_id_str&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;page&amp;quot;:1,&amp;quot;query&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;sx&quot;&gt;      &amp;quot;results&amp;quot;:[],&amp;quot;results_per_page&amp;quot;:15,&amp;quot;since_id&amp;quot;:0,&amp;quot;since_id_str&amp;quot;:&amp;quot;0&amp;quot;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above means that a GET request to &lt;em&gt;http://localhost:4578/search.json?q=X&amp;amp;result_type=popular&lt;/em&gt; will return the JSON for an empty result set. And when our app (driven from &lt;code&gt;When&lt;/code&gt; step) hits this url, it will appear to it as if there are no tweets. So that &lt;code&gt;Then&lt;/code&gt; step can observe the result page and check for the expected message about no popular tweets.&lt;/p&gt;

&lt;p&gt;Next, we want to tackle the case where twitter returns something. Here goes the second feature:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;following&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tweets&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;are&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;most&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;popular&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mentioning&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Y&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from_user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;                                 &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bob&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tweet&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;gets&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;recuked!&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alice&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Yo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;your&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cuke&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;makes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;me&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;puke&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;search&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;popular&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tweets&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mentioning&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Y&amp;quot;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;see&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;those&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tweets&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To be fair, the above scenario should not expose any test data. But since I want to demonstrate usage of more complex fixtures and I couldn’t think of a better example, we’ll stick to this one.&lt;/p&gt;

&lt;p&gt;So, again, we take a sample of a real response - only this time, since it is quite a big chunk of JSON, let us put it in a separate file. We also want to substitute bits of it with test data, so let us make an ERB template out of it.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-erb&quot; data-lang=&quot;erb&quot;&gt;&lt;span class=&quot;x&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;  &amp;quot;results&amp;quot;: [&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tweets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;    {&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;      &amp;quot;profile_image_url&amp;quot;: &amp;quot;http:\/\twimg.com\/profile_images\/u.jpg&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;      &amp;quot;from_user_id_str&amp;quot;: &amp;quot;149752077&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;      &amp;quot;created_at&amp;quot;:&amp;quot;Sat, 05 May 2012 21:37:12 +0000&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;      &amp;quot;id_str&amp;quot;: &amp;quot;116001342874595328&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;      &amp;quot;from_user&amp;quot;: &amp;quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:from_user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;      &amp;quot;to_user_id&amp;quot;: null,&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;      &amp;quot;text&amp;quot;: &amp;quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&amp;quot;,&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;      &amp;quot;metadata&amp;quot;: {&lt;/span&gt;
&lt;span class=&quot;x&quot;&gt;      ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt; &lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^the following tweets are most popular mentioning &amp;quot;([^&amp;quot;&amp;quot;]*)&amp;quot;:$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@expected_tweets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hashes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clone&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expand_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;../tweets.json.erb&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;__FILE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;tweets_json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Erubis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Eruby&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;tweets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@expected_tweets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;RestAssured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;fullpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;/search.json?q=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;amp;result_type=popular&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tweets_json&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The verification step is going collect tweets shown on a page and compare them with those saved in &lt;code&gt;@expected_tweets&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^I should see those tweets$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;actual_tweets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;#tweets tbody tr&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;from_user&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;.from_user&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;text&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;.text&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;actual_tweets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@expected_tweets&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It is worth mentioning that our Given step is already a bit too complicated for a step definition. All the template mangling and creation of a double should be hidden in a domain object. Something like, &lt;code&gt;PopularTweetsSearchResult.new(tweets: @expected_tweets)&lt;/code&gt;. And with time, as the number of constructor options grows, that domain object creation can in turn be delegated to &lt;a href=&quot;https://github.com/thoughtbot/factory_girl&quot;&gt;Factory Girl&lt;/a&gt; (or the like). This might sound like overkill, but the truth is that Cucumber is not particularly great at organising steps, so passing state around in conjunction with step reusability pushes complexity through the roof before you know it. So, ruthlessly moving whatever you can away from that context is not a bad idea.&lt;/p&gt;

&lt;p&gt;Ok. The project goes on and the day comes when our client shows their true face as the following feature lands on our table:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;there&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;are&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;popular&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tweets&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;about&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Z&amp;quot;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;search&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;popular&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tweets&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;about&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Z&amp;quot;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;be&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;The&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Vegan&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Police&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Intelligence&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Service&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/Vegan_Police_Movie.jpg&quot; style=&quot;width:100%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Well, money has no smell. Besides, we already switched to use Factory Girl, so there is no turning back anyway.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^there are popular tweets about &amp;quot;([^&amp;quot;]*)&amp;quot;$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# factory takes care of creating double with sensible default tweets&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@search&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:popular_tweets_search_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;vi&quot;&gt;@create_record_double&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RestAssured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt; 
  &lt;span class=&quot;ss&quot;&gt;fullpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;/api/tweet_search_records&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;verb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;201&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;@create_record_double&lt;/code&gt; is going to match any POST to &lt;em&gt;http://localhost:4578/api/tweet_search_records&lt;/em&gt;. It is not going to return any content, just an HTTP 201 repsonse. Ideally it should also be hidden inside its own domain object, but for demonstration purposes let us leave it as is. Much like the Twitter search API, The Vegan Police Intelligence Service host address also needs to be changed to point to the REST-assured instance.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;Then&lt;/code&gt; step is going to analyse what requests have hit &lt;code&gt;@create_record_double&lt;/code&gt; (if any) and verify that they match our expectations.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^the record should be sent to The Vegan Police Intelligence Service$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@create_record_double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wait_for_requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# will raise exception if that does not happen&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@create_record_double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# verify that payload contains search query and user&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@search&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@search&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_user&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above looks a bit messy. &lt;a href=&quot;https://github.com/dchelimsky/rspec/wiki/Custom-Matchers&quot;&gt;Rspec custom matchers&lt;/a&gt; help a great deal with tidying up complex verifications such as this one. I am not going to go into the details of defining one, but in the end  it should look like the following:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^the record should be sent to The Vegan Police Intelligence Service$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@create_record_double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;have_been_requested&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@search&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@search&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Years have passed since we commissioned our little project. The silver has been spent big time. And millions of people have been brainwashed to absolutely love it. But one day we got an email from a beloved customer to investigate an issue. The problem seems to be that no records are being posted to The Vegan Police Intelligence Service any more.&lt;/p&gt;

&lt;p&gt;Say no more. We immediately check the CI build status and observe that everything is happily passing. Meaning that, as far as our tests are concerned, records are being sent. After hours of collective head scratching, we finally go back and read the rest of their &lt;a href=&quot;http://soreeyes.org/archive/2012/03/05/beware-the-hindenbug/&quot;&gt;smug report&lt;/a&gt; where they suggest, amongst other obviously irrelevant things, that it could be due to recent API changes. Blimey! Turns out the new version changed the records API. The easiest fix in our case would be to stick &lt;code&gt;/v1&lt;/code&gt; in the POST URL in order to use the old version. OK. First we change our test double and make sure of the correct failure, then change the implementation. And looks like we are done, right? Not quite so. The bug is fixed but the underlying problem is not. Fixtures will still eventually go out of date. We need a way to validate our assumptions (that is what fixtures are) against something more real.&lt;/p&gt;

&lt;p&gt;Unfortunately, it is difficult to address this problem in general (we tried), as the definition of valid is very much context dependant. So even though REST-assured creates this problem, it doesn’t provide any built-in solutions (so typical!) and your tests are probably the best place to host such checks. Nonetheless, forewarned is forearmed and hopefully this is not going to ruin your day. One piece of advice here: consider using tools like VCR to cache HTTP interactions or else things will get REALLY slow.&lt;/p&gt;

&lt;p&gt;Speaking of slow, REST-assured adds quite a bit of an overhead to test startup, as it spins up a separate service stuffed with ActiveRecord, ActiveResource and some other things you don’t want to know about. Two options here: consider using &lt;a href=&quot;https://github.com/sporkrb/spork&quot;&gt;spork&lt;/a&gt; or start it separately from tests. Both options work great in development (e.g. when you want lots of re-runs) and also give you the benefit of being able to inspect doubles via a web interface after the tests have finished.&lt;/p&gt;

&lt;p&gt;Wrap up time. Let us recap what REST-assured is and what it is useful for. REST-assured is a locally deployable service that you can configure at runtime to respond to arbitrary HTTP requests. As such, it can be used to serve test fixtures to an application under test. It also records all requests made to it, so that more control-type requests can also be verified.&lt;/p&gt;

&lt;p&gt;So that is it for the introduction of REST-assured. You can give it a try &lt;a href=&quot;http://rest-assured.herokuapp.com/&quot;&gt;right here&lt;/a&gt; right now. Have a nice day. And don’t let them catch you.&lt;/p&gt;

&lt;p&gt; 
 &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Special thanks go to &lt;a href=&quot;https://github.com/samstarling&quot;&gt;Sam Starling&lt;/a&gt; for proof reading&lt;/em&gt;.&lt;/p&gt;
</content>
 </entry>
 
 
</feed>