{"version": "https://jsonfeed.org/version/1",
  "title": "elver.me - blog (JSON Feed)",
  "description": "Recent content in Blog on elver.me",
  "home_page_url": "https://elver.me/blog/",
  "feed_url": "https://elver.me/blog/feed.json",
  "items": [
    {
      "id": "https://elver.me/blog/running-custom-ruby-with-cypress/",
      "url": "https://elver.me/blog/running-custom-ruby-with-cypress/","title": "Running custom Ruby with Cypress",
      "content_html": "\u003cp\u003eI\u0026rsquo;ve been using \u003ca href=\"https://www.cypress.io\"\u003eCypress\u003c/a\u003e on a recent project to do end-to-end testing.\nFrom the brief introduction I\u0026rsquo;ve had so far, I\u0026rsquo;m impressed. The debugging\nexperience seems like it\u0026rsquo;s going to be considerably better than with tests\nwritten using \u003ca href=\"https://github.com/teamcapybara/capybara\"\u003eCapybara\u003c/a\u003e. Capybara is great, but Cypress\u0026rsquo;s visual\ndebugging is hard to compete against.\u003c/p\u003e\n\u003ch2 id=\"running-custom-ruby\"\u003eRunning custom Ruby\u003c/h2\u003e\n\u003cp\u003eThis project uses \u003ca href=\"https://github.com/shakacode/cypress-on-rails\"\u003eCypressOnRails\u003c/a\u003e to build a bridge between\ntests running within Cypress and Ruby-land, where the Rails app lives. In one\nparticular test I wanted to turn a feature switch on and off (using\n\u003ca href=\"https://github.com/jnunemaker/flipper\"\u003eFlipper\u003c/a\u003e). This required me calling some Ruby code from the Cypress\ntest.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#008080\"\u003eFlipper\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eenable(\u003cspan style=\"color:#990073\"\u003e:new_feature\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis is possible through \u003ccode\u003ecy.appEval\u003c/code\u003e added by CypressOnRails.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eit(\u003cspan style=\"color:#d14\"\u003e\u0026#39;Tests something with a feature switch turned on\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#000;font-weight:bold\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  cy.appEval(\u003cspan style=\"color:#d14\"\u003e\u0026#34;Flipper.enable(:new_feature)\u0026#34;\u003c/span\u003e).then(() =\u0026gt; {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    cy.visit(\u003cspan style=\"color:#d14\"\u003e\u0026#39;/a/page\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#998;font-style:italic\"\u003e// select some elements\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#998;font-style:italic\"\u003e// assert some things\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e})\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"making-it-reusable\"\u003eMaking it reusable\u003c/h2\u003e\n\u003cp\u003eCypress supports adding functions that can be re-used inside the\n\u003ccode\u003espec/cypress/support/commands.js\u003c/code\u003e file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCypress.Commands.add(\u003cspan style=\"color:#d14\"\u003e\u0026#39;enableFeature\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#000;font-weight:bold\"\u003efunction\u003c/span\u003e (flag) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  cy.appEval(\u003cspan style=\"color:#d14\"\u003e\u0026#39;Flipper.enable(:\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e+\u003c/span\u003e flag \u003cspan style=\"color:#000;font-weight:bold\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#39;)\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCypress.Commands.add(\u003cspan style=\"color:#d14\"\u003e\u0026#39;disableFeature\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#000;font-weight:bold\"\u003efunction\u003c/span\u003e (flag) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  cy.appEval(\u003cspan style=\"color:#d14\"\u003e\u0026#39;Flipper.disable(:\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e+\u003c/span\u003e flag \u003cspan style=\"color:#000;font-weight:bold\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#39;)\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we can call these functions throughout all our tests.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eit(\u003cspan style=\"color:#d14\"\u003e\u0026#39;Tests something with a feature switch turned on\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#000;font-weight:bold\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  cy.enableFeature(\u003cspan style=\"color:#d14\"\u003e\u0026#39;new_feature\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#998;font-style:italic\"\u003e// Turn it on\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  cy.visit(\u003cspan style=\"color:#d14\"\u003e\u0026#39;/a/page\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#998;font-style:italic\"\u003e// select some elements\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#998;font-style:italic\"\u003e// assert some things\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  cy.disableFeature(\u003cspan style=\"color:#d14\"\u003e\u0026#39;new_feature\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#998;font-style:italic\"\u003e// Turn it off again\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e})\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
      "summary": "\u003cp\u003eI\u0026rsquo;ve been using \u003ca href=\"https://www.cypress.io\"\u003eCypress\u003c/a\u003e on a recent project to do end-to-end testing.\nFrom the brief introduction I\u0026rsquo;ve had so far, I\u0026rsquo;m impressed. The debugging\nexperience seems like it\u0026rsquo;s going to be considerably better than with tests\nwritten using \u003ca href=\"https://github.com/teamcapybara/capybara\"\u003eCapybara\u003c/a\u003e. Capybara is great, but Cypress\u0026rsquo;s visual\ndebugging is hard to compete against.\u003c/p\u003e\n\u003ch2 id=\"running-custom-ruby\"\u003eRunning custom Ruby\u003c/h2\u003e\n\u003cp\u003eThis project uses \u003ca href=\"https://github.com/shakacode/cypress-on-rails\"\u003eCypressOnRails\u003c/a\u003e to build a bridge between\ntests running within Cypress and Ruby-land, where the Rails app lives. In one\nparticular test I wanted to turn a feature switch on and off (using\n\u003ca href=\"https://github.com/jnunemaker/flipper\"\u003eFlipper\u003c/a\u003e). This required me calling some Ruby code from the Cypress\ntest.\u003c/p\u003e",
      "date_published": "2021-04-30T00:00:00+00:00",
      "date_modified": "2021-04-30T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/til-about-markdown-reference-style-links/",
      "url": "https://elver.me/blog/til-about-markdown-reference-style-links/","title": "TIL about Markdown reference style links",
      "content_html": "\u003cp\u003eAfter years of writing Markdown I only recently discovered that you can use any\nidentifier in a reference style link. I had made the incorrect assumption that\nthe identifier \u003cem\u003ehad\u003c/em\u003e to be a number, but no!\u003c/p\u003e\n\u003ch2 id=\"before\"\u003eBefore\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-markdown\" data-lang=\"markdown\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[I am an example link][1]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[1]: https://example.com\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"after\"\u003eAfter\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-markdown\" data-lang=\"markdown\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[I am an example link][example]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[example]: https://example.com\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI\u0026rsquo;ve spent a long time re-ordering/numbering my reference links in the past but\nthat should no longer be necessary.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eAfter years of writing Markdown I only recently discovered that you can use any\nidentifier in a reference style link. I had made the incorrect assumption that\nthe identifier \u003cem\u003ehad\u003c/em\u003e to be a number, but no!\u003c/p\u003e\n\u003ch2 id=\"before\"\u003eBefore\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-markdown\" data-lang=\"markdown\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[I am an example link][1]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[1]: https://example.com\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"after\"\u003eAfter\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-markdown\" data-lang=\"markdown\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[I am an example link][example]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[example]: https://example.com\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI\u0026rsquo;ve spent a long time re-ordering/numbering my reference links in the past but\nthat should no longer be necessary.\u003c/p\u003e",
      "date_published": "2021-03-13T00:00:00+00:00",
      "date_modified": "2021-03-13T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/converting-ebook-formats-with-ebook-convert/",
      "url": "https://elver.me/blog/converting-ebook-formats-with-ebook-convert/","title": "Converting ebook formats with ebook-convert",
      "content_html": "\u003cp\u003eI need to convert between ebook formats on a regular basis so that I can read\nthem on my Kindle, and I prefer to do this on the command line as it\u0026rsquo;s quicker\nand easier.\u003c/p\u003e\n\u003cp\u003eI found a utility called \u003ccode\u003eebook-convert\u003c/code\u003e that comes included as part of\n\u003ca href=\"https://calibre-ebook.com\"\u003eCalibre\u003c/a\u003e, the ebook management behemouth, and it suits the task\nsuperbly.  It\u0026rsquo;s a shame to have to install such a big application to get access\nto a small utility, but I do sometimes use the metadata editing features of\nCalibre, so it\u0026rsquo;s a price I\u0026rsquo;m willing to pay.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s very easy to use and hasn\u0026rsquo;t failed me up to now.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eebook-convert *.epub .mobi \u003cspan style=\"color:#998;font-style:italic\"\u003e# from epub to mobi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eebook-convert *.mobi .epub \u003cspan style=\"color:#998;font-style:italic\"\u003e# from mobi to epub\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOther formats are also supported.\u003c/p\u003e\n\u003cp\u003eInstallable on macOS using \u003ccode\u003ebrew install calibre\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eYou might want to link the \u003ccode\u003eebook-convert\u003c/code\u003e binary to \u003ccode\u003e/usr/local/bin\u003c/code\u003e for easier\nusage after installation: \u003ccode\u003eln -s /Applications/calibre.app/Contents/MacOS/ebook-convert /usr/local/bin/ebook-convert\u003c/code\u003e\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI need to convert between ebook formats on a regular basis so that I can read\nthem on my Kindle, and I prefer to do this on the command line as it\u0026rsquo;s quicker\nand easier.\u003c/p\u003e\n\u003cp\u003eI found a utility called \u003ccode\u003eebook-convert\u003c/code\u003e that comes included as part of\n\u003ca href=\"https://calibre-ebook.com\"\u003eCalibre\u003c/a\u003e, the ebook management behemouth, and it suits the task\nsuperbly.  It\u0026rsquo;s a shame to have to install such a big application to get access\nto a small utility, but I do sometimes use the metadata editing features of\nCalibre, so it\u0026rsquo;s a price I\u0026rsquo;m willing to pay.\u003c/p\u003e",
      "date_published": "2021-02-28T00:00:00+00:00",
      "date_modified": "2021-02-28T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/combine-pdfs-on-the-command-line-with-pdfunite/",
      "url": "https://elver.me/blog/combine-pdfs-on-the-command-line-with-pdfunite/","title": "Combine PDFs on the command line with pdfunite",
      "content_html": "\u003cp\u003eI often need a quick way to combine PDF pages into a single file. I\u0026rsquo;ve used\nmacOS\u0026rsquo;s Preview in the past but found it clunky so I went looking for a command\nline alternative.\u003c/p\u003e\n\u003cp\u003eThere seem to be many ways to do this, but I\u0026rsquo;ve found the \u003ccode\u003epdfunite\u003c/code\u003e utility\nto be the least friction method for me.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epdfunite file1.pdf file2.pdf destination.pdf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFiles are combined in the order you supply them in the arguments.\u003c/p\u003e\n\u003cp\u003eThankfully \u003ccode\u003epdfunite\u003c/code\u003e is easily installable on macOS, as part of the\n\u003ca href=\"https://poppler.freedesktop.org/\"\u003ePoppler\u003c/a\u003e project, using \u003ccode\u003ebrew install poppler\u003c/code\u003e.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI often need a quick way to combine PDF pages into a single file. I\u0026rsquo;ve used\nmacOS\u0026rsquo;s Preview in the past but found it clunky so I went looking for a command\nline alternative.\u003c/p\u003e\n\u003cp\u003eThere seem to be many ways to do this, but I\u0026rsquo;ve found the \u003ccode\u003epdfunite\u003c/code\u003e utility\nto be the least friction method for me.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epdfunite file1.pdf file2.pdf destination.pdf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFiles are combined in the order you supply them in the arguments.\u003c/p\u003e",
      "date_published": "2021-01-30T00:00:00+00:00",
      "date_modified": "2021-01-30T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/project-specific-gemrc-files-using-the-gemrc-environment-variable/",
      "url": "https://elver.me/blog/project-specific-gemrc-files-using-the-gemrc-environment-variable/","title": "Project specific .gemrc files using the GEMRC environment variable",
      "content_html": "\u003cp\u003eRecently I needed to add a new \u003ca href=\"https://guides.rubygems.org/command-reference/#gem-sources\"\u003esource entry\u003c/a\u003e to my \u003ccode\u003e.gemrc\u003c/code\u003e for a\nprivate \u003ca href=\"https://rubygems.org/\"\u003eRubyGems\u003c/a\u003e server. I \u003ca href=\"https://github.com/jordelver/dotfiles/blob/master/gemrc\"\u003ecommit my \u003ccode\u003e.gemrc\u003c/code\u003e to\nGit\u003c/a\u003e, so this was a problem.  Not only because I don\u0026rsquo;t want\nproject specific changes cluttering up my \u003ccode\u003e.gemrc\u003c/code\u003e (I don\u0026rsquo;t), but also because\nthe URL of the source contains secrets that shouldn\u0026rsquo;t be shared.\u003c/p\u003e\n\u003cp\u003eMy default \u003ccode\u003egemrc\u003c/code\u003e \u003ccode\u003e:sources:\u003c/code\u003e entry looked like this.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#555\"\u003e---\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e\u003c/span\u003e\u003cspan style=\"color:#000080\"\u003e:sources\u003c/span\u003e:\u003cspan style=\"color:#bbb\"\u003e \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e\u003c/span\u003e- https://rubygems.org\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThere doesn\u0026rsquo;t seem to be a supported way to have per-project \u003ccode\u003e.gemrc\u003c/code\u003e files but\nyou \u003cem\u003ecan\u003c/em\u003e \u003ca href=\"http://www.soulcutter.com/articles/hiding-gemrc-credentials-in-dotfiles.html\"\u003eoverride the file using the \u003ccode\u003eGEMRC\u003c/code\u003e\u003c/a\u003e\nenvironment variable. So if you can set a per-project \u003ccode\u003eGEMRC\u003c/code\u003e environment\nvariable, you \u003cem\u003ecan\u003c/em\u003e have a per-project \u003ccode\u003e.gemrc\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eI use \u003ca href=\"https://direnv.net/\"\u003e\u003ccode\u003edirenv\u003c/code\u003e\u003c/a\u003e to set environment variables for my projects by\ncreating a \u003ccode\u003e.envrc\u003c/code\u003e file in each of my project directories. So I can also use\nthat here. I just add a line like this.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#0086b3\"\u003eexport\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eGEMRC\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003epath/to/project/specific/gemrc\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd in that file I add the extra key to \u003ccode\u003e:sources:\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#555\"\u003e---\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e\u003c/span\u003e\u003cspan style=\"color:#000080\"\u003e:sources\u003c/span\u003e:\u003cspan style=\"color:#bbb\"\u003e \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e\u003c/span\u003e- https://rubygems.org\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e\u003c/span\u003e- https://user:pass@custom.gem.server\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI need to add the original \u003ccode\u003ehttps://rubygems.org\u003c/code\u003e source too because although\nthe \u003ccode\u003e~/.gemrc\u003c/code\u003e will still be read, the keys in the project specific file will\nreplace any keys with the same name completely - they are not merged. You can\nsee what values \u003ccode\u003egem\u003c/code\u003e is using by running \u003ccode\u003egem environment\u003c/code\u003e. Great for\ndebugging.\u003c/p\u003e\n\u003cp\u003eI think using \u003ccode\u003eGEMRC\u003c/code\u003e for this purpose is a reasonable workaround for\nper-project \u003ccode\u003e.gemrc\u003c/code\u003e files.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eRecently I needed to add a new \u003ca href=\"https://guides.rubygems.org/command-reference/#gem-sources\"\u003esource entry\u003c/a\u003e to my \u003ccode\u003e.gemrc\u003c/code\u003e for a\nprivate \u003ca href=\"https://rubygems.org/\"\u003eRubyGems\u003c/a\u003e server. I \u003ca href=\"https://github.com/jordelver/dotfiles/blob/master/gemrc\"\u003ecommit my \u003ccode\u003e.gemrc\u003c/code\u003e to\nGit\u003c/a\u003e, so this was a problem.  Not only because I don\u0026rsquo;t want\nproject specific changes cluttering up my \u003ccode\u003e.gemrc\u003c/code\u003e (I don\u0026rsquo;t), but also because\nthe URL of the source contains secrets that shouldn\u0026rsquo;t be shared.\u003c/p\u003e\n\u003cp\u003eMy default \u003ccode\u003egemrc\u003c/code\u003e \u003ccode\u003e:sources:\u003c/code\u003e entry looked like this.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#555\"\u003e---\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e\u003c/span\u003e\u003cspan style=\"color:#000080\"\u003e:sources\u003c/span\u003e:\u003cspan style=\"color:#bbb\"\u003e \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e\u003c/span\u003e- https://rubygems.org\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThere doesn\u0026rsquo;t seem to be a supported way to have per-project \u003ccode\u003e.gemrc\u003c/code\u003e files but\nyou \u003cem\u003ecan\u003c/em\u003e \u003ca href=\"http://www.soulcutter.com/articles/hiding-gemrc-credentials-in-dotfiles.html\"\u003eoverride the file using the \u003ccode\u003eGEMRC\u003c/code\u003e\u003c/a\u003e\nenvironment variable. So if you can set a per-project \u003ccode\u003eGEMRC\u003c/code\u003e environment\nvariable, you \u003cem\u003ecan\u003c/em\u003e have a per-project \u003ccode\u003e.gemrc\u003c/code\u003e.\u003c/p\u003e",
      "date_published": "2020-12-06T00:00:00+00:00",
      "date_modified": "2020-12-06T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/vim-plugins-dont-have-to-be-hard/",
      "url": "https://elver.me/blog/vim-plugins-dont-have-to-be-hard/","title": "Vim plugins don't have to be hard",
      "content_html": "\u003cp\u003eWhen pairing on something I will often insert \u003ca href=\"https://docs.github.com/en/free-pro-team@latest/github/committing-changes-to-your-project/creating-a-commit-with-multiple-authors#creating-co-authored-commits-on-the-command-line\"\u003eGitHub coauthorship\ninformation\u003c/a\u003e into a commit by adding \u003ccode\u003eCo-authored-by: name \u0026lt;name@example.com\u0026gt;\u003c/code\u003e to the bottom of a commit message. Both parties then get\ncredit for the work.  However, it\u0026rsquo;s a bit tedious to do this manually.\u003c/p\u003e\n\u003cp\u003eI went looking for a solution that could be triggered from within Vim and \u003ca href=\"https://github.com/maxjacobson/vim-fzf-coauthorship\"\u003efound\nthis nice little plugin\u003c/a\u003e. It gives you a command\n\u003ccode\u003e:Coauthorship\u003c/code\u003e which when run will show a list of Git repo contributors from\nwhom you can choose using the \u003ccode\u003efzf\u003c/code\u003e fuzzy finder. It\u0026rsquo;s really neat, and does\nexactly what I was looking for.\u003c/p\u003e\n\u003cp\u003eHere is the Vimscript in its entirety.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-vimscript\" data-lang=\"vimscript\"\u003efunction! AttributeCoauthorship(nameAndEmail)\n  let attribution = \u0026#34;Co-authored-by: \u0026#34; . a:nameAndEmail\n  silent put =attribution\nendfunction\n\nfunction! Coauthorship()\n  call fzf#run({\n    \\ \u0026#39;source\u0026#39;: \u0026#39;git log --pretty=\u0026#34;%an \u0026lt;%ae\u0026gt;\u0026#34; | sort | uniq\u0026#39;,\n    \\ \u0026#39;sink\u0026#39;: function(\u0026#39;AttributeCoauthorship\u0026#39;),\n    \\ \u0026#39;options\u0026#39;: \u0026#34;--preview \u0026#39;git log -1 --author {} --pretty=\\\u0026#34;authored %h %ar:%n%n%B\\\u0026#34;\u0026#39;\u0026#34;\n    \\ })\nendfunction\n\ncommand! Coauthorship call Coauthorship()\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWhat struck me is just how little code it takes to get something like this\nworking. Of course, it\u0026rsquo;s relying on external programs (\u003ccode\u003egit\u003c/code\u003e, \u003ccode\u003esort\u003c/code\u003e, \u003ccode\u003euniq\u003c/code\u003e)\nand libraries (the \u003ccode\u003efzf\u003c/code\u003e vim plugin code), but the \u003cem\u003eplumbing\u003c/em\u003e to compose this\nis small, concise, and easy to build upon. It feels like the unix philosophy in\naction.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve never gone as far as programming my editor for fear of thinking \u0026ldquo;this\nisn\u0026rsquo;t for me, its too hard\u0026rdquo; but this plugin has got me thinking that it \u003cem\u003emight\njust\u003c/em\u003e be for me after all.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eWhen pairing on something I will often insert \u003ca href=\"https://docs.github.com/en/free-pro-team@latest/github/committing-changes-to-your-project/creating-a-commit-with-multiple-authors#creating-co-authored-commits-on-the-command-line\"\u003eGitHub coauthorship\ninformation\u003c/a\u003e into a commit by adding \u003ccode\u003eCo-authored-by: name \u0026lt;name@example.com\u0026gt;\u003c/code\u003e to the bottom of a commit message. Both parties then get\ncredit for the work.  However, it\u0026rsquo;s a bit tedious to do this manually.\u003c/p\u003e\n\u003cp\u003eI went looking for a solution that could be triggered from within Vim and \u003ca href=\"https://github.com/maxjacobson/vim-fzf-coauthorship\"\u003efound\nthis nice little plugin\u003c/a\u003e. It gives you a command\n\u003ccode\u003e:Coauthorship\u003c/code\u003e which when run will show a list of Git repo contributors from\nwhom you can choose using the \u003ccode\u003efzf\u003c/code\u003e fuzzy finder. It\u0026rsquo;s really neat, and does\nexactly what I was looking for.\u003c/p\u003e",
      "date_published": "2020-11-20T00:00:00+00:00",
      "date_modified": "2020-11-20T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/rspec-like-doc-format-output-in-elixir-tests/",
      "url": "https://elver.me/blog/rspec-like-doc-format-output-in-elixir-tests/","title": "RSpec-like doc format output in Elixir tests",
      "content_html": "\u003cp\u003eWhen running RSpec tests \u003ca href=\"https://relishapp.com/rspec/rspec-core/v/3-9/docs/command-line/format-option\"\u003eyou can pass a \u003ccode\u003e--format\u003c/code\u003e flag\u003c/a\u003e to \u003ccode\u003erspec\u003c/code\u003e\nto format test output in a more verbose style. The default output looks like this.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e....F.....*.....\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIf you pass \u003ccode\u003e--format documentation\u003c/code\u003e it will look more like this.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esomething\n  does something that passes\n  does something that fails (FAILED - 1)\n  does something that is pending (PENDING: Not Yet Implemented)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI find this output really helpful. It helps to visualise the \u003cem\u003eshape\u003c/em\u003e of the\ntests, and guides naming because you can see the visual hierarchy of how things\nfit together.\u003c/p\u003e\n\u003ch2 id=\"elixir-and-exunit\"\u003eElixir and ExUnit\u003c/h2\u003e\n\u003cp\u003eYou can achieve a very similar result to RSpec by passing \u003ccode\u003e--trace\u003c/code\u003e to \u003ccode\u003emix test\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003emix test --trace\n\nTestApp.TimeDateHelper\n  * test relative_format/1 single hour, multiple minutes (0.00ms)\n  * test relative_format/1 greater than an hour (0.00ms)\n  * test relative_format/1 less than a minute (0.00ms)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eYou can make this change across the whole test suite by changing the call to\n\u003ccode\u003eExUnit.start/1\u003c/code\u003e to include a trace option. This is usually in your\n\u003ccode\u003etest/test_helper.exs\u003c/code\u003e file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-elixir\" data-lang=\"elixir\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#458;font-weight:bold\"\u003eExUnit\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003estart(\u003cspan style=\"color:#990073\"\u003etrace\u003c/span\u003e: \u003cspan style=\"color:#008080\"\u003etrue\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBe aware though, this might not be what you want. From \u003ca href=\"https://hexdocs.pm/ex_unit/ExUnit.html#configure/1-options\"\u003ethe ExUnit\ndocs\u003c/a\u003e:\u003c/p\u003e\n\u003cblockquote style=\"text-align: left; margin: 0;\"\u003e\n  \u003cp style=\"margin: 0;\"\u003e\n    ...sets ExUnit into trace mode, \u003cstrong\u003ethis sets :max_cases to 1\u003c/strong\u003e\n    and prints each test case and test while running...\n  \u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eSetting \u003ccode\u003etrace: true\u003c/code\u003e will also set \u003ccode\u003emax_cases: 1\u003c/code\u003e which will reduce the amount\nof tests that are run in parallel.\u003c/p\u003e\n\u003cp\u003eSo this could slow down your test suite. Be careful!\u003c/p\u003e\n",
      "summary": "\u003cp\u003eWhen running RSpec tests \u003ca href=\"https://relishapp.com/rspec/rspec-core/v/3-9/docs/command-line/format-option\"\u003eyou can pass a \u003ccode\u003e--format\u003c/code\u003e flag\u003c/a\u003e to \u003ccode\u003erspec\u003c/code\u003e\nto format test output in a more verbose style. The default output looks like this.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e....F.....*.....\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIf you pass \u003ccode\u003e--format documentation\u003c/code\u003e it will look more like this.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esomething\n  does something that passes\n  does something that fails (FAILED - 1)\n  does something that is pending (PENDING: Not Yet Implemented)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI find this output really helpful. It helps to visualise the \u003cem\u003eshape\u003c/em\u003e of the\ntests, and guides naming because you can see the visual hierarchy of how things\nfit together.\u003c/p\u003e",
      "date_published": "2020-10-24T00:00:00+00:00",
      "date_modified": "2020-10-24T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/gitattributes/",
      "url": "https://elver.me/blog/gitattributes/","title": "Using gitattributes to improve git output",
      "content_html": "\u003cp\u003eFrom the git man page:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eA gitattributes file is a simple text file that gives attributes to pathnames.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://www.git-scm.com/docs/gitattributes\"\u003e\u003ccode\u003egitattributes\u003c/code\u003e\u003c/a\u003e allows you to tell git that files should be\ntreated in certain ways. You can use git attributes to apply various attributes\nbut we\u0026rsquo;re focussing on \u003ccode\u003ediff\u003c/code\u003e. For example, this tells git that files ending in\n\u003ccode\u003e*.ex\u003c/code\u003e should be treated as Elixir code during diff operations.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e*.ex diff=elixir\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI couldn\u0026rsquo;t get the standard \u003ccode\u003e~/.gitattributes\u003c/code\u003e file location to work for me so I\ndecided to take this opportunity to standardise all my git config under\n\u003ccode\u003e~/.config/git\u003c/code\u003e instead.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTIP\u003c/strong\u003e You can use \u003ca href=\"https://www.git-scm.com/docs/git-check-attr\"\u003e\u003ccode\u003egit check-attr --all -- path/to/file\u003c/code\u003e\u003c/a\u003e to\ncheck which attributes are applying to a file. This is very useful during setup.\u003c/p\u003e\n\u003cp\u003eSo what effect does this have?\u003c/p\u003e\n\u003ch2 id=\"hunk-output\"\u003eHunk output\u003c/h2\u003e\n\u003cp\u003eHere\u0026rsquo;s a simple example. On line 5 we can see a change, but that isn\u0026rsquo;t the\nimportant part. The interesting difference is what\u0026rsquo;s shown on line 1 after the\nfilename. In this case it\u0026rsquo;s \u003ccode\u003edefmodule TestWeb.ListController\u003c/code\u003e which in the\nElixir \u003cstrong\u003emodule\u003c/strong\u003e in which the change has been made.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-patch\" data-lang=\"patch\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#aaa\"\u003e@ path/to/file.ex:25 @ defmodule TestWeb.ListController do\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#aaa\"\u003e\u003c/span\u003e     {:ok, _list} = Lists.delete_list(list)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     conn\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#dfd\"\u003e+    |\u0026gt; put_flash(:info, \u0026#34;List deleted successfully.\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#dfd\"\u003e\u003c/span\u003e\u003cspan style=\"color:#000;background-color:#fdd\"\u003e-    |\u0026gt; put_flash(:info, \u0026#34;List deleted successfully\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#fdd\"\u003e\u003c/span\u003e     |\u0026gt; redirect(to: Routes.list_path(conn, :index))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   end\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e end\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf we add \u003ccode\u003e*.ex diff=elixir\u003c/code\u003e to the attributes file we see \u003ccode\u003edef delete(conn, %{\u0026quot;id\u0026quot; =\u0026gt; id})\u003c/code\u003e, which is the \u003cstrong\u003efunction\u003c/strong\u003e containing the change. Far more\nspecific.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-patch\" data-lang=\"patch\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#aaa\"\u003e@ path/to/file.ex:25 @ def delete(conn, %{\u0026#34;id\u0026#34; =\u0026gt; id}) do\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#aaa\"\u003e\u003c/span\u003e     {:ok, _list} = Lists.delete_list(list)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     conn\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#dfd\"\u003e+    |\u0026gt; put_flash(:info, \u0026#34;List deleted successfully.\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#dfd\"\u003e\u003c/span\u003e\u003cspan style=\"color:#000;background-color:#fdd\"\u003e-    |\u0026gt; put_flash(:info, \u0026#34;List deleted successfully\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#fdd\"\u003e\u003c/span\u003e     |\u0026gt; redirect(to: Routes.list_path(conn, :index))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   end\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e end\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"--function-context\"\u003e\u0026ndash;function-context\u003c/h2\u003e\n\u003cp\u003eAdding this setting also changes the way that\n\u003ca href=\"https://www.git-scm.com/docs/git-diff#Documentation/git-diff.txt---function-context\"\u003e\u003ccode\u003e--function-context\u003c/code\u003e\u003c/a\u003e works. What is \u003ccode\u003e--function-context\u003c/code\u003e? It\ncan be passed to \u003ccode\u003egit diff\u003c/code\u003e (and other subcommands) and will show the full\nchange in the full context of where the change has been made.  By default this\nmeans it will show the \u003cem\u003efull\u003c/em\u003e module, which is probably not what you want most\nof the time.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-patch\" data-lang=\"patch\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#aaa\"\u003e@ path/to/file.ex:25 @ def delete(conn, %{\u0026#34;id\u0026#34; =\u0026gt; id}) do\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#aaa\"\u003e\u003c/span\u003e defmodule TestWeb.ListController do\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   use TestWeb, :controller\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   # ...other functions removed for brevity\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   def delete(conn, %{\u0026#34;id\u0026#34; =\u0026gt; id}) do\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     list = Lists.get_list!(id)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     {:ok, _list} = Lists.delete_list(list)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     conn\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#dfd\"\u003e+    |\u0026gt; put_flash(:info, \u0026#34;List deleted successfully.\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#dfd\"\u003e\u003c/span\u003e\u003cspan style=\"color:#000;background-color:#fdd\"\u003e-    |\u0026gt; put_flash(:info, \u0026#34;List deleted successfully\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#fdd\"\u003e\u003c/span\u003e     |\u0026gt; redirect(to: Routes.list_path(conn, :index))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   end\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e end\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHowever, with the change to \u003ccode\u003egitattributes\u003c/code\u003e, the \u0026ldquo;function context\u0026rdquo; becomes the\n\u003cem\u003eactual\u003c/em\u003e function.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-patch\" data-lang=\"patch\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#aaa\"\u003e@ path/to/file.ex:25 @ def delete(conn, %{\u0026#34;id\u0026#34; =\u0026gt; id}) do\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#aaa\"\u003e\u003c/span\u003e   def delete(conn, %{\u0026#34;id\u0026#34; =\u0026gt; id}) do\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     list = Lists.get_list!(id)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     {:ok, _list} = Lists.delete_list(list)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     conn\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#dfd\"\u003e+    |\u0026gt; put_flash(:info, \u0026#34;List deleted successfully.\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#dfd\"\u003e\u003c/span\u003e\u003cspan style=\"color:#000;background-color:#fdd\"\u003e-    |\u0026gt; put_flash(:info, \u0026#34;List deleted successfully\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#fdd\"\u003e\u003c/span\u003e     |\u0026gt; redirect(to: Routes.list_path(conn, :index))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   end\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e end\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt\u0026rsquo;s strange that a lot of the available \u003ccode\u003egitattibutes\u003c/code\u003e are not defaults in git.\nBut with a few simple tweaks you can get more useful git output.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eFrom the git man page:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eA gitattributes file is a simple text file that gives attributes to pathnames.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ca href=\"https://www.git-scm.com/docs/gitattributes\"\u003e\u003ccode\u003egitattributes\u003c/code\u003e\u003c/a\u003e allows you to tell git that files should be\ntreated in certain ways. You can use git attributes to apply various attributes\nbut we\u0026rsquo;re focussing on \u003ccode\u003ediff\u003c/code\u003e. For example, this tells git that files ending in\n\u003ccode\u003e*.ex\u003c/code\u003e should be treated as Elixir code during diff operations.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e*.ex diff=elixir\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI couldn\u0026rsquo;t get the standard \u003ccode\u003e~/.gitattributes\u003c/code\u003e file location to work for me so I\ndecided to take this opportunity to standardise all my git config under\n\u003ccode\u003e~/.config/git\u003c/code\u003e instead.\u003c/p\u003e",
      "date_published": "2020-09-30T00:00:00+00:00",
      "date_modified": "2020-09-30T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/how-to-get-a-list-of-filenames-from-a-plex-playlist/",
      "url": "https://elver.me/blog/how-to-get-a-list-of-filenames-from-a-plex-playlist/","title": "How to get a list of filenames from a Plex playlist",
      "content_html": "\u003cp\u003eI recently wanted to delete some files from my Plex server. Plex has a feature\nto allow deleting of files through the web interface, but it makes me feel\nslightly uneasy having that feature turned on due to security concerns, so I\nchose to keep it off, and for this one-off task I decided to delete the files\nmanually.\u003c/p\u003e\n\u003cp\u003eI needed a list of filenames so I started by adding all the files I wanted to\ndelete to a playlist called \u0026ldquo;To Delete\u0026rdquo;. Plex uses a SQLite database to store\nmetadata. This is what I used to get the information from the playlist.\u003c/p\u003e\n\u003cp\u003eConnect to the SQLite database like this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esqlite3 \u003cspan style=\"color:#d14\"\u003e\u0026#39;/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases/com.plexapp.plugins.library.db\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can then list all playlists. The \u003ccode\u003e15\u003c/code\u003e in \u003ccode\u003emetadata_type = 15\u003c/code\u003e is for\nplaylists.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esqlite\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eselect\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003etitle\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003efrom\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003emetadata_items\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003ewhere\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003emetadata_type\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#099\"\u003e15\u003c/span\u003e;\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can see that the output includes the \u0026ldquo;To Delete\u0026rdquo; playlist.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eChristmas\nPixar\nComedy classics\nStar Wars\nTo Delete\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIdeally, we\u0026rsquo;d like a list of filenames in a text file. This makes it easy to\nprocess later. The \u003ccode\u003esqlite\u003c/code\u003e client has a \u003ccode\u003e.output\u003c/code\u003e command that allows you to\nredirect output from queries to an external file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esqlite\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e.\u003cspan style=\"color:#000;font-weight:bold\"\u003eoutput\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003efiles\u003cspan style=\"color:#000;font-weight:bold\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eto\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003edelete\u003c/span\u003e.txt\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhen we run subsequent queries, the output will end up in \u003ccode\u003efiles-to-delete.txt\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eNow, we can run a query to get a list of filenames. I won\u0026rsquo;t pretend to\nunderstand the whole structure of this query, but it works for me. Thanks to\n\u003ca href=\"https://www.reddit.com/r/PleX/comments/3cbku8/export_list_of_file_paths_in_a_plex_playlist_for/\"\u003ethis Reddit thread\u003c/a\u003e. Note the \u003ccode\u003ewhere metadata_items.title = 'To Delete'\u003c/code\u003e clause where we specify the playlist name.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esqlite\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eselect\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003efile\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003efrom\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003emedia_parts\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eleft\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eouter\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003ejoin\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003emedia_items\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eon\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e  \u003c/span\u003emedia_items.id\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003emedia_parts.media_item_id\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eleft\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eouter\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003ejoin\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e  \u003c/span\u003eplay_queue_generators\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eon\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003eplay_queue_generators.metadata_item_id\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e  \u003c/span\u003emedia_items.metadata_item_id\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eleft\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eouter\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003ejoin\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003emetadata_items\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eon\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e  \u003c/span\u003emetadata_items.id\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003eplay_queue_generators.playlist_id\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e  \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003ewhere\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003emetadata_items.title\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#39;To Delete\u0026#39;\u003c/span\u003e;\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003efiles-to-delete.txt\u003c/code\u003e file now contains a list of filenames for the movies\non the \u0026ldquo;To Delete\u0026rdquo; playlist. We can now use that list to delete the files. In my\ncase, I used a loop in fish shell.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003efor\u003c/span\u003e f in \u003cspan style=\"color:#000;font-weight:bold\"\u003e(\u003c/span\u003ecat files-to-delete.txt\u003cspan style=\"color:#000;font-weight:bold\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  rm -i -v \u003cspan style=\"color:#008080\"\u003e$f\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eend\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThere are certainly many ways to do this, but this is how I did it.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI recently wanted to delete some files from my Plex server. Plex has a feature\nto allow deleting of files through the web interface, but it makes me feel\nslightly uneasy having that feature turned on due to security concerns, so I\nchose to keep it off, and for this one-off task I decided to delete the files\nmanually.\u003c/p\u003e\n\u003cp\u003eI needed a list of filenames so I started by adding all the files I wanted to\ndelete to a playlist called \u0026ldquo;To Delete\u0026rdquo;. Plex uses a SQLite database to store\nmetadata. This is what I used to get the information from the playlist.\u003c/p\u003e",
      "date_published": "2020-08-31T00:00:00+00:00",
      "date_modified": "2020-08-31T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/renaming-files-vim-style/",
      "url": "https://elver.me/blog/renaming-files-vim-style/","title": "Renaming files Vim-style",
      "content_html": "\u003cp\u003eI’ve \u003ca href=\"/blog/renaming-files-like-a-pro/\"\u003epreviously written about renaming files\u003c/a\u003e with the \u003ccode\u003erename\u003c/code\u003e\nutility.  \u003ccode\u003erename\u003c/code\u003e uses sed-like syntax with regular expressions to rename\nfiles, which are very powerful, but can be a bit tricky to get right.\u003c/p\u003e\n\u003cp\u003eI recently came across \u003ca href=\"https://github.com/thameera/vimv\"\u003evimv\u003c/a\u003e, which is a utility (written in bash) to\nlet you use the full power of Vim to rename files. As a vim user being able to\nuse the same patterns I would use when writing code is very appealing.\u003c/p\u003e\n\u003cscript id=\"asciicast-RfVtJfuoplCMZd50vESXbOAFR\"\nsrc=\"https://asciinema.org/a/RfVtJfuoplCMZd50vESXbOAFR.js\" async\u003e\u003c/script\u003e\n\u003cp\u003eIt\u0026rsquo;s a great idea, and it turns out that this is also not a new idea. Many other\ntools have very similar abilities as pointed out by many great \u003ca href=\"https://twitter.com/MasteringVim/status/1270720407272738818\"\u003ereplies to the\noriginal tweet\u003c/a\u003e. Some of tools that offer this ability\ninclude (in order of discovery) the aforementioned vimv; three different file\nmanagers: \u003ca href=\"https://github.com/ranger/ranger\"\u003eranger\u003c/a\u003e, \u003ca href=\"https://github.com/jarun/nnn\"\u003ennn\u003c/a\u003e, and \u003ca href=\"https://github.com/vifm/vifm\"\u003evifm\u003c/a\u003e; \u003ca href=\"https://github.com/trapd00r/vidir\"\u003evidir\u003c/a\u003e; and\n\u003ca href=\"http://www.nongnu.org/renameutils/\"\u003eqmv\u003c/a\u003e, from renameutils (around since 2001!).\u003c/p\u003e\n\u003cscript id=\"asciicast-Scwm0G1Qc70AgaOKhiHdezKSr\"\nsrc=\"https://asciinema.org/a/Scwm0G1Qc70AgaOKhiHdezKSr.js\" async\u003e\u003c/script\u003e\n\u003cp\u003eWe are spoilt for choice! I already use \u003ccode\u003ennn\u003c/code\u003e so I will be making use of it\u0026rsquo;s\n\u0026ldquo;batch rename\u0026rdquo; feature on my file server. For other cases I think I\u0026rsquo;ll be\nreaching for \u003ccode\u003eqmv\u003c/code\u003e as it\u0026rsquo;s very mature, and simple to use. Try out one that\nworks for you.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI’ve \u003ca href=\"/blog/renaming-files-like-a-pro/\"\u003epreviously written about renaming files\u003c/a\u003e with the \u003ccode\u003erename\u003c/code\u003e\nutility.  \u003ccode\u003erename\u003c/code\u003e uses sed-like syntax with regular expressions to rename\nfiles, which are very powerful, but can be a bit tricky to get right.\u003c/p\u003e\n\u003cp\u003eI recently came across \u003ca href=\"https://github.com/thameera/vimv\"\u003evimv\u003c/a\u003e, which is a utility (written in bash) to\nlet you use the full power of Vim to rename files. As a vim user being able to\nuse the same patterns I would use when writing code is very appealing.\u003c/p\u003e",
      "date_published": "2020-07-12T00:00:00+00:00",
      "date_modified": "2020-07-12T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/get-video-urls-from-a-youtube-playlist/",
      "url": "https://elver.me/blog/get-video-urls-from-a-youtube-playlist/","title": "Get video URLs from a YouTube playlist",
      "content_html": "\u003cp\u003eI wanted to make my own list of YouTube video URLs today, and as far as I can\ntell, YouTube doesn\u0026rsquo;t let you do that. The excellent tool\n\u003ca href=\"https://github.com/ytdl-org/youtube-dl\"\u003e\u003ccode\u003eyoutube-dl\u003c/code\u003e\u003c/a\u003e came to the rescue, along with a post on\n\u003ca href=\"https://askubuntu.com/questions/1090510/how-to-download-a-playlist-without-the-videos\"\u003easkubuntu\u003c/a\u003e that combines it with \u003ccode\u003ejq\u003c/code\u003e and \u003ccode\u003esed\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eI wanted to output the video URLs \u003cem\u003eand\u003c/em\u003e the titles, and it turns out this can be\nachieved with \u003ccode\u003ejq\u003c/code\u003e on it\u0026rsquo;s own. I changed the \u003ccode\u003ejq\u003c/code\u003e portion and the final\ncommandline looks like this.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eyoutube-dl --dump-json --flat-playlist \u003cspan style=\"color:#d14\"\u003e\u0026#34;https://www.youtube.com/playlist?list=PL46-cKSxMYYCMpzXo6p0Cof8hJInYgohU\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  | jq -r \u003cspan style=\"color:#d14\"\u003e\u0026#39;\u0026#34;\\(.title)\\nhttps://youtu.be/\\(.id)\\n\u0026#34;\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis outputs something like:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eVim Un-Alphabet 01: Teaser\nhttps://youtu.be/7LDlUMMbv6k\n\nVim Un-Alphabet 02: Help\nhttps://youtu.be/ZTCzWRqR_us\n\nVim Un-Alphabet 03: Tilde\nhttps://youtu.be/5jMiYtXz2QA\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOf course, we can pipe the result of this to a file by appending \u003ccode\u003e \u0026gt; result.txt\u003c/code\u003e\nor, in my case, straight to the clipboard with \u003ccode\u003e | pbcopy\u003c/code\u003e.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI wanted to make my own list of YouTube video URLs today, and as far as I can\ntell, YouTube doesn\u0026rsquo;t let you do that. The excellent tool\n\u003ca href=\"https://github.com/ytdl-org/youtube-dl\"\u003e\u003ccode\u003eyoutube-dl\u003c/code\u003e\u003c/a\u003e came to the rescue, along with a post on\n\u003ca href=\"https://askubuntu.com/questions/1090510/how-to-download-a-playlist-without-the-videos\"\u003easkubuntu\u003c/a\u003e that combines it with \u003ccode\u003ejq\u003c/code\u003e and \u003ccode\u003esed\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eI wanted to output the video URLs \u003cem\u003eand\u003c/em\u003e the titles, and it turns out this can be\nachieved with \u003ccode\u003ejq\u003c/code\u003e on it\u0026rsquo;s own. I changed the \u003ccode\u003ejq\u003c/code\u003e portion and the final\ncommandline looks like this.\u003c/p\u003e",
      "date_published": "2020-06-11T00:00:00+00:00",
      "date_modified": "2020-06-11T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/fixing-commits-with-git-commit-fixup-and-git-rebase-autosquash/",
      "url": "https://elver.me/blog/fixing-commits-with-git-commit-fixup-and-git-rebase-autosquash/","title": "Fixing commits with git commit --fixup and git rebase --autosquash",
      "content_html": "\u003cp\u003eThe way I like to make changes in response to a PR review is to make any fixes\nto the original commits using a rebase. I don\u0026rsquo;t want commits that \u0026ldquo;fix typos\u0026rdquo; in\nmy history, especially at the feature branch level. There are varying opinions\nand approaches to this, but I think rebasing is great tool when used on your \u003cem\u003eown\u003c/em\u003e\nbranches and in moderation.\u003c/p\u003e\n\u003cp\u003eStandard procedure for this is - 1) Make the code change; 2) Commit the change;\n3) Start an interactive rebase; 4) Identify the commit that needs fixing; 5)\nMove the new commit underneath it; 6) Change it to \u0026ldquo;squash\u0026rdquo;. It\u0026rsquo;s quite tedious.\u003c/p\u003e\n\u003ch2 id=\"fixup-commits\"\u003eFixup commits\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---fixupltcommitgt\"\u003eFixup commits\u003c/a\u003e are created using \u003ccode\u003egit commit --fixup \u0026lt;SHA\u0026gt;\u003c/code\u003e.\nPractically, \u003ccode\u003e--fixup\u003c/code\u003e associates a new commit with an existing commit so that\nwhen you do an interactive rebase, you don\u0026rsquo;t have to re-order any commits in\norder to squash them. And you don\u0026rsquo;t have to change any commit messages. From the\ndocs:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eConstruct a commit message for use with rebase \u0026ndash;autosquash.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"--autosquash\"\u003e\u0026ndash;autosquash?\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---autosquash\"\u003e\u003ccode\u003e--autosquash\u003c/code\u003e\u003c/a\u003e is a flag to use with \u003ccode\u003erebase\u003c/code\u003e and takes everything\na step further. Once you\u0026rsquo;ve committed your changes with \u003ccode\u003egit commit --fixup \u0026lt;SHA\u0026gt;\u003c/code\u003e you can start an interactive rebase as normal, but pass the\n\u003ccode\u003e--autosquash\u003c/code\u003e flag too.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egit rebase -i --autosquash master\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow you won\u0026rsquo;t have to do anything, the rebase will automatically take care of\nsquashing those commits created with \u003ccode\u003e--fixup\u003c/code\u003e in the correct order!\u003c/p\u003e\n\u003cp\u003eYou can have this behaviour by default, which seems safe and sensible, by\nsetting \u003ccode\u003eautosquash = true\u003c/code\u003e in your \u003ccode\u003e~/.gitconfig\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e[rebase]\n  autosquash = true\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"automating-further\"\u003eAutomating further\u003c/h2\u003e\n\u003cp\u003eI use \u003ccode\u003e--fixup\u003c/code\u003e so much that I have a helper alias in my \u003ccode\u003e~/.gitconfig\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e[alias]\n  fixup = \u0026#34;!git log -n 50 --pretty=format:\u0026#39;%h %s\u0026#39; --no-merges | fzf | cut -c -7 | xargs -o git commit --fixup\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis lets me type \u003ccode\u003egit fixup\u003c/code\u003e and presents a list of my 50 most recent commits\nand allows me to search the list using fzf. Once a commit is selected, the SHA\nis passed to \u003ccode\u003egit commit --fixup\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eIt works like this:\u003c/p\u003e\n\u003cscript id=\"asciicast-nyRPY9XaPPtC39ihVDgoN3anH\"\nsrc=\"https://asciinema.org/a/nyRPY9XaPPtC39ihVDgoN3anH.js\" async\u003e\u003c/script\u003e\n\u003cp\u003eI hope this makes your commit fixing easier!\u003c/p\u003e\n",
      "summary": "\u003cp\u003eThe way I like to make changes in response to a PR review is to make any fixes\nto the original commits using a rebase. I don\u0026rsquo;t want commits that \u0026ldquo;fix typos\u0026rdquo; in\nmy history, especially at the feature branch level. There are varying opinions\nand approaches to this, but I think rebasing is great tool when used on your \u003cem\u003eown\u003c/em\u003e\nbranches and in moderation.\u003c/p\u003e\n\u003cp\u003eStandard procedure for this is - 1) Make the code change; 2) Commit the change;\n3) Start an interactive rebase; 4) Identify the commit that needs fixing; 5)\nMove the new commit underneath it; 6) Change it to \u0026ldquo;squash\u0026rdquo;. It\u0026rsquo;s quite tedious.\u003c/p\u003e",
      "date_published": "2020-06-04T00:00:00+00:00",
      "date_modified": "2020-06-04T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/history-deleting-helper-for-fish-shell/",
      "url": "https://elver.me/blog/history-deleting-helper-for-fish-shell/","title": "History deleting helper for Fish shell",
      "content_html": "\u003cp\u003eI \u003ca href=\"/blog/deleting-fish-shell-history/\"\u003ewrote back in February\u003c/a\u003e about a neat \u003ccode\u003ehistory\u003c/code\u003e command built-in to the Fish\nshell that allows you to, amongst other things, delete entries from your\nhistory.\u003c/p\u003e\n\u003cp\u003eTo recap, you use it like this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ \u003cspan style=\"color:#0086b3\"\u003ehistory\u003c/span\u003e delete --contains \u003cspan style=\"color:#d14\"\u003e\u0026#34;mix echo\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e1\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#0086b3\"\u003ehistory\u003c/span\u003e search --contains \u003cspan style=\"color:#d14\"\u003e\u0026#34;mix echo\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e2\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e mix echo.migrate\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e3\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e mix echo.create\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e4\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e mix echo.reset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e5\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e mix echo.setup\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEnter nothing to cancel the delete, or\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEnter one or more of the entry IDs separated by a space, or\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEnter \u003cspan style=\"color:#d14\"\u003e\u0026#34;all\u0026#34;\u003c/span\u003e to delete all the matching entries.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003eI\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e Delete which entries? \u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis is really cool, but the only thing missing for me was the ability to\nfuzzily find history entries, so I wrote a quick Fish function to combine\n\u003ccode\u003ehistory\u003c/code\u003e with \u003ca href=\"https://github.com/junegunn/fzf\"\u003e\u003ccode\u003efzf\u003c/code\u003e\u003c/a\u003e, my favourite shell program. I also made a small\nchange to the history command by swapping \u003ccode\u003e--contains\u003c/code\u003e for \u003ccode\u003e--prefix\u003c/code\u003e which\nmakes more sense when combined with \u003ccode\u003efzf\u003c/code\u003e as the search is more constrained.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003efunction\u003c/span\u003e dh -d \u003cspan style=\"color:#d14\"\u003e\u0026#34;Fuzzily delete entries from your history\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#0086b3\"\u003ehistory\u003c/span\u003e | fzf | \u003cspan style=\"color:#0086b3\"\u003eread\u003c/span\u003e -l item; and \u003cspan style=\"color:#0086b3\"\u003ehistory\u003c/span\u003e delete --prefix \u003cspan style=\"color:#d14\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#008080\"\u003e$item\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eend\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow you can type \u003ccode\u003edh\u003c/code\u003e and fuzzily search for history before being dropped into\nthe interactive prompt as usual.\u003c/p\u003e\n\u003cscript id=\"asciicast-hoIxMqnjYe9qjg4uA46EC0rRG\"\nsrc=\"https://asciinema.org/a/hoIxMqnjYe9qjg4uA46EC0rRG.js\" async\u003e\u003c/script\u003e\n",
      "summary": "\u003cp\u003eI \u003ca href=\"/blog/deleting-fish-shell-history/\"\u003ewrote back in February\u003c/a\u003e about a neat \u003ccode\u003ehistory\u003c/code\u003e command built-in to the Fish\nshell that allows you to, amongst other things, delete entries from your\nhistory.\u003c/p\u003e\n\u003cp\u003eTo recap, you use it like this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ \u003cspan style=\"color:#0086b3\"\u003ehistory\u003c/span\u003e delete --contains \u003cspan style=\"color:#d14\"\u003e\u0026#34;mix echo\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e1\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#0086b3\"\u003ehistory\u003c/span\u003e search --contains \u003cspan style=\"color:#d14\"\u003e\u0026#34;mix echo\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e2\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e mix echo.migrate\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e3\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e mix echo.create\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e4\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e mix echo.reset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e5\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e mix echo.setup\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEnter nothing to cancel the delete, or\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEnter one or more of the entry IDs separated by a space, or\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEnter \u003cspan style=\"color:#d14\"\u003e\u0026#34;all\u0026#34;\u003c/span\u003e to delete all the matching entries.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003eI\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e Delete which entries? \u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis is really cool, but the only thing missing for me was the ability to\nfuzzily find history entries, so I wrote a quick Fish function to combine\n\u003ccode\u003ehistory\u003c/code\u003e with \u003ca href=\"https://github.com/junegunn/fzf\"\u003e\u003ccode\u003efzf\u003c/code\u003e\u003c/a\u003e, my favourite shell program. I also made a small\nchange to the history command by swapping \u003ccode\u003e--contains\u003c/code\u003e for \u003ccode\u003e--prefix\u003c/code\u003e which\nmakes more sense when combined with \u003ccode\u003efzf\u003c/code\u003e as the search is more constrained.\u003c/p\u003e",
      "date_published": "2020-05-29T00:00:00+00:00",
      "date_modified": "2020-05-29T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/protecting-phoenix-endpoints-with-http-basic-auth/",
      "url": "https://elver.me/blog/protecting-phoenix-endpoints-with-http-basic-auth/","title": "Protecting Phoenix endpoints with HTTP Basic Auth (on Heroku)",
      "content_html": "\u003cp\u003e\u003ca href=\"https://github.com/phoenixframework/phoenix_live_dashboard\"\u003ePhoenix LiveDashboard\u003c/a\u003e was recently officially announced\nby \u003ca href=\"https://twitter.com/josevalim/status/1250846714665357315\"\u003eJosé Valim\u003c/a\u003e, and I was keen try it out so I\ninstalled it on a toy app I\u0026rsquo;ve been developing, which is deployed to Heroku.\u003c/p\u003e\n\u003cp\u003eI didn\u0026rsquo;t want my LiveDashboard to be available to the whole world, so the\nquickest and easiest method of protecting it was to use HTTP Basic Authentication,\n\u003ca href=\"https://hexdocs.pm/phoenix_live_dashboard/Phoenix.LiveDashboard.html#module-extra-add-dashboard-access-on-all-environments-including-production\"\u003esomething that is recommended in the LiveDashboard docs\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eTo that end, I installed and configured the \u003ca href=\"https://hex.pm/packages/basic_auth\"\u003e\u003ccode\u003ebasic_auth\u003c/code\u003e plug\u003c/a\u003e,\nand read the username and password to configure it from environment variables.\u003c/p\u003e\n\u003cp\u003eThese are the steps I took.\u003c/p\u003e\n\u003ch2 id=\"install-and-configure\"\u003eInstall and configure\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003eAdd the \u003ccode\u003ebasic_auth\u003c/code\u003e plug\u003c/p\u003e\n\u003cp\u003eAdd \u003ccode\u003ebasic_auth\u003c/code\u003e to your \u003ccode\u003emix.exs\u003c/code\u003e file\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-elixir\" data-lang=\"elixir\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\u003cspan style=\"color:#990073\"\u003e:basic_auth\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;~\u0026gt; 2.2.4\u0026#34;\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eInstall dependencies\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emix deps.get\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eConfigure \u003ccode\u003ebasic_auth\u003c/code\u003e from environment variables\u003c/p\u003e\n\u003cp\u003eEdit your \u003ccode\u003eprod.secret.exs\u003c/code\u003e file and add this.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-elixir\" data-lang=\"elixir\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehttp_basic_auth_username \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#458;font-weight:bold\"\u003eSystem\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eget_env(\u003cspan style=\"color:#d14\"\u003e\u0026#34;HTTP_BASIC_AUTH_USERNAME\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#000;font-weight:bold\"\u003e||\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#000;font-weight:bold\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e    environment variable HTTP_BASIC_AUTH_USERNAME is missing.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehttp_basic_auth_password \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#458;font-weight:bold\"\u003eSystem\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eget_env(\u003cspan style=\"color:#d14\"\u003e\u0026#34;HTTP_BASIC_AUTH_PASSWORD\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#000;font-weight:bold\"\u003e||\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#000;font-weight:bold\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e    environment variable HTTP_BASIC_AUTH_PASSWORD is missing.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e# Set basic auth from environment variables\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econfig \u003cspan style=\"color:#990073\"\u003e:example_web\u003c/span\u003e, \u003cspan style=\"color:#990073\"\u003ebasic_auth\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#990073\"\u003eusername\u003c/span\u003e: http_basic_auth_username,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#990073\"\u003epassword\u003c/span\u003e: http_basic_auth_password,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eUpdate the router to protect the LiveDashboard routes\u003c/p\u003e\n\u003cp\u003eCreate a new pipeline that conditionally adds the \u003ccode\u003ebasic_auth\u003c/code\u003e plug if the\n\u003ccode\u003eHTTP_BASIC_AUTH_USERNAME\u003c/code\u003e or \u003ccode\u003eHTTP_BASIC_AUTH_PASSWORD\u003c/code\u003e are present.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-elixir\" data-lang=\"elixir\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epipeline \u003cspan style=\"color:#990073\"\u003e:protected\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#458;font-weight:bold\"\u003eSystem\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eget_env(\u003cspan style=\"color:#d14\"\u003e\u0026#34;HTTP_BASIC_AUTH_USERNAME\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#000;font-weight:bold\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#458;font-weight:bold\"\u003eSystem\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eget_env(\u003cspan style=\"color:#d14\"\u003e\u0026#34;HTTP_BASIC_AUTH_PASSWORD\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    plug \u003cspan style=\"color:#458;font-weight:bold\"\u003eBasicAuth\u003c/span\u003e, \u003cspan style=\"color:#990073\"\u003euse_config\u003c/span\u003e: {\u003cspan style=\"color:#990073\"\u003e:example_web\u003c/span\u003e, \u003cspan style=\"color:#990073\"\u003e:basic_auth\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen add \u003ccode\u003e:protected\u003c/code\u003e to the pipeline.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-elixir\" data-lang=\"elixir\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003escope \u003cspan style=\"color:#d14\"\u003e\u0026#34;/\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#458;font-weight:bold\"\u003eExampleWeb\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  pipe_through [\u003cspan style=\"color:#990073\"\u003e:browser\u003c/span\u003e, \u003cspan style=\"color:#990073\"\u003e:protected\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  live_dashboard \u003cspan style=\"color:#d14\"\u003e\u0026#34;/dashboard\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eSet your environment variables on Heroku\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eheroku config:set \u003cspan style=\"color:#008080\"\u003eHTTP_BASIC_AUTH_USERNAME\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003eadmin \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e                  \u003cspan style=\"color:#008080\"\u003eHTTP_BASIC_AUTH_PASSWORD\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003eP@ssword\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eNow when you visit \u003ccode\u003e/dashboard\u003c/code\u003e you should be prompted to enter your username\nand password.\u003c/p\u003e\n",
      "summary": "\u003cp\u003e\u003ca href=\"https://github.com/phoenixframework/phoenix_live_dashboard\"\u003ePhoenix LiveDashboard\u003c/a\u003e was recently officially announced\nby \u003ca href=\"https://twitter.com/josevalim/status/1250846714665357315\"\u003eJosé Valim\u003c/a\u003e, and I was keen try it out so I\ninstalled it on a toy app I\u0026rsquo;ve been developing, which is deployed to Heroku.\u003c/p\u003e\n\u003cp\u003eI didn\u0026rsquo;t want my LiveDashboard to be available to the whole world, so the\nquickest and easiest method of protecting it was to use HTTP Basic Authentication,\n\u003ca href=\"https://hexdocs.pm/phoenix_live_dashboard/Phoenix.LiveDashboard.html#module-extra-add-dashboard-access-on-all-environments-including-production\"\u003esomething that is recommended in the LiveDashboard docs\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eTo that end, I installed and configured the \u003ca href=\"https://hex.pm/packages/basic_auth\"\u003e\u003ccode\u003ebasic_auth\u003c/code\u003e plug\u003c/a\u003e,\nand read the username and password to configure it from environment variables.\u003c/p\u003e",
      "date_published": "2020-05-01T00:00:00+00:00",
      "date_modified": "2020-05-01T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/til-about-the-external-resource-module-attribute-in-elixir/",
      "url": "https://elver.me/blog/til-about-the-external-resource-module-attribute-in-elixir/","title": "TIL about the @external_resource module attribute in Elixir",
      "content_html": "\u003cp\u003eToday I learnt about the \u003ccode\u003e@external_resource\u003c/code\u003e module attribute in \u003ca href=\"https://elixir-lang.org/\"\u003eElixir\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eFrom the \u003ca href=\"https://hexdocs.pm/elixir/Module.html#module-external_resource\"\u003eModule documentation page\u003c/a\u003e:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eSometimes a module embeds information from an external file. This attribute\nallows the module to annotate which external resources have been used.\u003c/p\u003e\n\u003cp\u003eTools like Mix may use this information to ensure the module is recompiled in case any of the external resources change.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ccode\u003e@external_resource\u003c/code\u003e means that you can specify a resource \u003cem\u003eoutside\u003c/em\u003e of your\nmodule that will trigger recompilation of your module - basically, it sets up a\ndependency between your module and another file.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://blog.oestrich.org/2018/03/elixir-external-resources/\"\u003eEric Oestrich blogged about a real-life use-case\u003c/a\u003e for this feature -\ngenerating functions based on an external \u0026ldquo;translations\u0026rdquo; file. At compile-time\nhe reads the file and generates functions based on it. His post goes into a lot\nmore depth and has code examples. I recommend giving it a read.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eToday I learnt about the \u003ccode\u003e@external_resource\u003c/code\u003e module attribute in \u003ca href=\"https://elixir-lang.org/\"\u003eElixir\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eFrom the \u003ca href=\"https://hexdocs.pm/elixir/Module.html#module-external_resource\"\u003eModule documentation page\u003c/a\u003e:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eSometimes a module embeds information from an external file. This attribute\nallows the module to annotate which external resources have been used.\u003c/p\u003e\n\u003cp\u003eTools like Mix may use this information to ensure the module is recompiled in case any of the external resources change.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ccode\u003e@external_resource\u003c/code\u003e means that you can specify a resource \u003cem\u003eoutside\u003c/em\u003e of your\nmodule that will trigger recompilation of your module - basically, it sets up a\ndependency between your module and another file.\u003c/p\u003e",
      "date_published": "2020-04-23T00:00:00+00:00",
      "date_modified": "2020-04-23T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/handling-a-custom-response-body-with-faraday-middleware/",
      "url": "https://elver.me/blog/handling-a-custom-response-body-with-faraday-middleware/","title": "Handling a custom response body with Faraday middleware",
      "content_html": "\u003cp\u003eI\u0026rsquo;ve been working with a REST API recently that returns JSON except for one\nparticular endpoint. This endpoint returns a plain text response with a\nnon-standard content type header.\u003c/p\u003e\n\u003cp\u003eIt looks like this:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eA:1AHGJDSMMPLMPPGNLJBQVLBRSKVDLQRPP:2213,2214,2215\nA:201AGBHDRLQHNHPHKKMPKLGPMDRDTDMVL:3134,3135,3136,3137\nU:1AHGJDSMMPLMPPGNLJBQVLBRSKVDLQRPP:2212\nU:201AGBHDRLQHNHPHKKMPKLGPMDRDTDMVL:3133\nX:2AVDSSBSTSQDRDKBHCNTRTHPNTBGQDTMD:2677,2685,2969,2996,3002\nX:1AHGJDSMMPLMPPGNLJBQVLBRSKVDLQRPP:3029,3056\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe output is seat availability for concert venues with colons separating the\nfields like so \u003ccode\u003e\u0026lt;seat-type\u0026gt;:\u0026lt;band-id\u0026gt;:\u0026lt;seats\u0026gt;\u003c/code\u003e. The problem is that we need to\nhandle this response explicitly as the default JSON parsing doesn\u0026rsquo;t know what to\ndo with it.\u003c/p\u003e\n\u003cp\u003eThe desired output in this case would be like this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#990073\"\u003e:seat_type\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;A\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#990073\"\u003e:band_id\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;1AHGJDSMMPLMPPGNLJBQVLBRSKVDLQRPP\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#990073\"\u003e:seats\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;2213\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;2214\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;2215\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#990073\"\u003e:seat_type\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;A\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#990073\"\u003e:band_id\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;201AGBHDRLQHNHPHKKMPKLGPMDRDTDMVL\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#990073\"\u003e:seats\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;3134\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;3135\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;3136\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;3137\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003e...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"faraday-middleware\"\u003eFaraday Middleware\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;m using \u003ca href=\"https://lostisland.github.io/faraday/\"\u003eFaraday\u003c/a\u003e as the HTTP client for this project and it has the\nconcept of middleware. Middleware can apply to requests or responses and are\nhooked into the lifecycle of a request.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eFaraday is an HTTP client library that provides a common interface over many\nadapters (such as Net::HTTP) and \u003cstrong\u003eembraces the concept of Rack\nmiddleware when processing the request/response cycle.\u003c/strong\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eThis means that we can write a custom middleware to parse the response for our\ncustom content type, which for the purpose of illustration, we\u0026rsquo;ll call\n\u003ccode\u003evenue/seats\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"custom-response-middleware\"\u003eCustom response middleware\u003c/h2\u003e\n\u003cp\u003eFirst we make sure we have the correct gems.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egem \u003cspan style=\"color:#d14\"\u003e\u0026#39;faraday\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#39;~\u0026gt; 1.0\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egem \u003cspan style=\"color:#d14\"\u003e\u0026#39;faraday_middleware\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#990073\"\u003egithub\u003c/span\u003e: \u003cspan style=\"color:#d14\"\u003e\u0026#39;lostisland/faraday_middleware\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#990073\"\u003eref\u003c/span\u003e: \u003cspan style=\"color:#d14\"\u003e\u0026#39;e1324ca\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe latest version of \u003ccode\u003efaraday_middleware\u003c/code\u003e isn\u0026rsquo;t compatible with Faraday 1.0 as\nfar as I can tell at the moment, so I pinned the gem to commit \u003ccode\u003ee1324c\u003c/code\u003e, which\nis. There is a \u003ca href=\"https://github.com/lostisland/faraday_middleware/releases/tag/v1.0.0.rc1\"\u003epre-release version available\u003c/a\u003e at the time\nof writing.\u003c/p\u003e\n\u003cp\u003eNow we can write a middleware that we will later hook into the request/response\ncycle. I\u0026rsquo;m using the \u003ccode\u003eResponseMiddleware\u003c/code\u003e from \u003ca href=\"https://github.com/lostisland/faraday_middleware/blob/master/lib/faraday_middleware/response_middleware.rb\"\u003e\u003ccode\u003efaraday_middleware\u003c/code\u003e\u003c/a\u003e\nas it has a neat \u003ccode\u003edefine_parser\u003c/code\u003e helper which allows us to very easily supply a\nparser for \u003ccode\u003evenue/seats\u003c/code\u003e content type.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#0086b3\"\u003erequire\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;faraday\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#0086b3\"\u003erequire\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;faraday_middleware\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#0086b3\"\u003erequire\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;faraday_middleware/response_middleware\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e# Custom response middleware\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#458;font-weight:bold\"\u003eSeatAvailabilityResponse\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e::\u003c/span\u003e\u003cspan style=\"color:#008080\"\u003eFaradayMiddleware\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e::\u003c/span\u003e\u003cspan style=\"color:#008080\"\u003eResponseMiddleware\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  define_parser \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003ebody, _\u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#008080\"\u003eSeatAvailability\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eparse(body)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e# Register the middleware so we can use it\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#008080\"\u003eFaraday\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e::\u003c/span\u003e\u003cspan style=\"color:#008080\"\u003eResponse\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eregister_middleware(\u003cspan style=\"color:#990073\"\u003eseat_availability\u003c/span\u003e: \u003cspan style=\"color:#008080\"\u003eSeatAvailabilityResponse\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAt the bottom you can see that I register this class as a response middleware\ncalled \u0026ldquo;seat_availability\u0026rdquo; using \u003ccode\u003eFaraday::Response.register_middleware\u003c/code\u003e. This\nallows us to refer to this middleware in the future.\u003c/p\u003e\n\u003ch2 id=\"response-parsing\"\u003eResponse parsing\u003c/h2\u003e\n\u003cp\u003eI also delegate parsing responsibility to another class, \u003ccode\u003eSeatAvailability\u003c/code\u003e.\nYou could just as easily add it inline, but I like to separate the\nresponsibilities, and it also makes it easier to test if it\u0026rsquo;s a separate class.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#458;font-weight:bold\"\u003eSeatAvailability\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#458;font-weight:bold\"\u003eself\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003e\u003cspan style=\"color:#900;font-weight:bold\"\u003eparse\u003c/span\u003e(body)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#000;font-weight:bold\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003eif\u003c/span\u003e body\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003enil?\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    body\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003esplit\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003emap \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003esection\u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      seat_type, band_id, seats \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e section\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003esplit(\u003cspan style=\"color:#d14\"\u003e\u0026#34;:\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#990073\"\u003eseat_type\u003c/span\u003e: seat_type,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#990073\"\u003eband_id\u003c/span\u003e: band_id,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#990073\"\u003eseats\u003c/span\u003e: seats\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003esplit(\u003cspan style=\"color:#d14\"\u003e\u0026#34;,\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"registering-the-middleware\"\u003eRegistering the middleware\u003c/h2\u003e\n\u003cp\u003eAnd this is how we use the new middleware. We create \u003ccode\u003eFaraday\u003c/code\u003e connection object\nand in the block call \u003ccode\u003econn.response\u003c/code\u003e in order to add it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econn \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eFaraday\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003enew(\u003cspan style=\"color:#d14\"\u003e\u0026#34;http://example.com/api\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003econn\u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#998;font-style:italic\"\u003e#...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#998;font-style:italic\"\u003e# Use the `SeatAvailabilityResponse` middleware for \u0026#34;venue/seats\u0026#34; content types\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  conn\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eresponse \u003cspan style=\"color:#990073\"\u003e:seat_availability\u003c/span\u003e, \u003cspan style=\"color:#990073\"\u003econtent_type\u003c/span\u003e: \u003cspan style=\"color:#d14\"\u003e\u0026#34;venue/seats\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#998;font-style:italic\"\u003e#...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow when we make a call to an endpoint that returns a body with a \u003ccode\u003evenue/seats\u003c/code\u003e\ncontent type it will automatically be parsed into an array of hashes for us.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#099\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e (pry) \u003cspan style=\"color:#990073\"\u003emain\u003c/span\u003e: \u003cspan style=\"color:#099\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026gt;\u003c/span\u003e response \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e conn\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eget(\u003cspan style=\"color:#d14\"\u003e\u0026#34;availability\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#990073\"\u003e:seat_type\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;A\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#990073\"\u003e:band_id\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;1AHGJDSMMPLMPPGNLJBQVLBRSKVDLQRPP\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#990073\"\u003e:seats\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;2213\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;2214\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;2215\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#990073\"\u003e:seat_type\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;A\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#990073\"\u003e:band_id\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;201AGBHDRLQHNHPHKKMPKLGPMDRDTDMVL\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#990073\"\u003e:seats\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;3134\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;3135\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;3136\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;3137\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI hope this demonstrates how easy it is to handle custom responses in a very\nstraight-forward and modular way.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI\u0026rsquo;ve been working with a REST API recently that returns JSON except for one\nparticular endpoint. This endpoint returns a plain text response with a\nnon-standard content type header.\u003c/p\u003e\n\u003cp\u003eIt looks like this:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eA:1AHGJDSMMPLMPPGNLJBQVLBRSKVDLQRPP:2213,2214,2215\nA:201AGBHDRLQHNHPHKKMPKLGPMDRDTDMVL:3134,3135,3136,3137\nU:1AHGJDSMMPLMPPGNLJBQVLBRSKVDLQRPP:2212\nU:201AGBHDRLQHNHPHKKMPKLGPMDRDTDMVL:3133\nX:2AVDSSBSTSQDRDKBHCNTRTHPNTBGQDTMD:2677,2685,2969,2996,3002\nX:1AHGJDSMMPLMPPGNLJBQVLBRSKVDLQRPP:3029,3056\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe output is seat availability for concert venues with colons separating the\nfields like so \u003ccode\u003e\u0026lt;seat-type\u0026gt;:\u0026lt;band-id\u0026gt;:\u0026lt;seats\u0026gt;\u003c/code\u003e. The problem is that we need to\nhandle this response explicitly as the default JSON parsing doesn\u0026rsquo;t know what to\ndo with it.\u003c/p\u003e",
      "date_published": "2020-03-27T00:00:00+00:00",
      "date_modified": "2020-03-27T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/deleting-fish-shell-history/",
      "url": "https://elver.me/blog/deleting-fish-shell-history/","title": "Deleting fish shell history",
      "content_html": "\u003cp\u003eHave you ever made a typo? No, me neither, but in case you ever do\u0026hellip;\u003c/p\u003e\n\u003cp\u003eI run a lot of commands via the shell history using \u003ca href=\"https://github.com/junegunn/fzf#key-bindings-for-command-line\"\u003efzf and CTRL-R\u003c/a\u003e to save\nre-typing, and ironically, mispellings. However, if you \u003cem\u003edo\u003c/em\u003e mispell something\nit ends up in the history forevermore, taunting you upon each invocation of\n\u003ca href=\"https://github.com/junegunn/fzf/blob/master/shell/key-bindings.fish#L109\"\u003efzf-history-widget\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eIt turns out that \u003ca href=\"https://fishshell.com/docs/current/cmds/history.html#cmd-history\"\u003efish ships with a neat \u003ccode\u003ehistory\u003c/code\u003e command\u003c/a\u003e that allows you\nto manipulate the history is various ways including searching, deleting, and\ncompletely clearing.\u003c/p\u003e\n\u003cp\u003eThe command I want to remove in this case is \u003ccode\u003emix echo\u003c/code\u003e (which should be \u003ccode\u003emix ecto\u003c/code\u003e).\u003c/p\u003e\n\u003cp\u003eI use the \u003ccode\u003e--contains\u003c/code\u003e option here which will present a prompt showing the\nentries and allowing you to delete individual entries or remove all.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ \u003cspan style=\"color:#0086b3\"\u003ehistory\u003c/span\u003e delete --contains \u003cspan style=\"color:#d14\"\u003e\u0026#34;mix echo\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e1\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#0086b3\"\u003ehistory\u003c/span\u003e search --contains \u003cspan style=\"color:#d14\"\u003e\u0026#34;mix echo\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e2\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e mix echo.migrate\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e3\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e mix echo.create\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e4\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e mix echo.reset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e5\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e mix echo.setup\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEnter nothing to cancel the delete, or\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEnter one or more of the entry IDs separated by a space, or\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEnter \u003cspan style=\"color:#d14\"\u003e\u0026#34;all\u0026#34;\u003c/span\u003e to delete all the matching entries.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003eI\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e Delete which entries? \u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you want to search before running \u003ccode\u003edelete\u003c/code\u003e you can do \u003ccode\u003ehistory search --contains \u0026quot;\u0026lt;search\u0026gt;\u0026quot;\u003c/code\u003e which will return a list of items for you to check.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eHave you ever made a typo? No, me neither, but in case you ever do\u0026hellip;\u003c/p\u003e\n\u003cp\u003eI run a lot of commands via the shell history using \u003ca href=\"https://github.com/junegunn/fzf#key-bindings-for-command-line\"\u003efzf and CTRL-R\u003c/a\u003e to save\nre-typing, and ironically, mispellings. However, if you \u003cem\u003edo\u003c/em\u003e mispell something\nit ends up in the history forevermore, taunting you upon each invocation of\n\u003ca href=\"https://github.com/junegunn/fzf/blob/master/shell/key-bindings.fish#L109\"\u003efzf-history-widget\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eIt turns out that \u003ca href=\"https://fishshell.com/docs/current/cmds/history.html#cmd-history\"\u003efish ships with a neat \u003ccode\u003ehistory\u003c/code\u003e command\u003c/a\u003e that allows you\nto manipulate the history is various ways including searching, deleting, and\ncompletely clearing.\u003c/p\u003e",
      "date_published": "2020-02-21T00:00:00+00:00",
      "date_modified": "2020-02-21T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/routing-docker-traffic-through-a-vpn-connection/",
      "url": "https://elver.me/blog/routing-docker-traffic-through-a-vpn-connection/","title": "Routing Docker traffic through a VPN connection",
      "content_html": "\u003cp\u003eI\u0026rsquo;ve recently taken to using Docker to install and run various software\non my home server. Something that so far, it excels at - the people at\n\u003ca href=\"https://linuxserver.io\"\u003elinuxserver.io\u003c/a\u003e are doing great work! I\u0026rsquo;ve had a rocky time with Docker in\nthe past after having had it foisted upon me for development work, which I did\n\u003cem\u003enot\u003c/em\u003e enjoy, but I can see the benefits for certain situations.\u003c/p\u003e\n\u003cp\u003eI found myself needing to run the traffic from one particular container\n(\u003ca href=\"https://github.com/linuxserver/docker-jackett\"\u003eJackett\u003c/a\u003e) over a VPN connection so that it could by-pass country-specific\nrestrictions. As a noob Docker user, this caused some confusion, but I eventually\nstumbled upon the \u003ccode\u003e--net\u003c/code\u003e parameter to \u003ccode\u003edocker create\u003c/code\u003e and \u003ccode\u003erun\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eUsing this parameter it\u0026rsquo;s possible to tell a container to use the network of\nanother. You can run an OpenVPN client container, which will initiate a secure\nconnection, and configure other containers to use its network. The beauty of\nthis setup is that you don\u0026rsquo;t need to learn or manage any complicated \u003ccode\u003eip_tables\u003c/code\u003e\nrules or any other network configuration, you can just point one container at\nanother and have the traffic secured.\u003c/p\u003e\n\u003cp\u003eAll I needed now was a suitable Docker image. Eventually, I got lucky and\n\u003ca href=\"https://github.com/bubuntux/nordvpn\"\u003efound an image that supported my exact VPN provider\u003c/a\u003e, \u003ca href=\"https://nordvpn.com/\"\u003eNordVPN\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThis is how I created and started the OpenVPN container. Once this was running,\na secure VPN connection was established to NordVPN.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker run \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  --name vpn \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  --cap-add\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003eNET_ADMIN \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  --device /dev/net/tun \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  -p 9117:9117 \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  -e \u003cspan style=\"color:#008080\"\u003eNETWORK\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e192.168.1.0/24 \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  -e \u003cspan style=\"color:#008080\"\u003eUSER\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003eusername \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  -e \u003cspan style=\"color:#008080\"\u003ePASS\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#39;password\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  bubuntux/nordvpn\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eMost of these options are standard, but the \u003ccode\u003e-p 9117:9117\u003c/code\u003e parameter on line 5\nneeds explanation. This is the port mapping that Jackett uses by default. When\nwe use another container\u0026rsquo;s network it\u0026rsquo;s necessary to expose the port(s) that our\nother containers use on the VPN container.\u003c/p\u003e\n\u003cp\u003eAnd here is how I setup Jackett to use the VPN container. The relevant line is\n\u003ccode\u003e--net=container:vpn\u003c/code\u003e. Note that I don\u0026rsquo;t have a \u003ccode\u003e-p 9117:9117\u003c/code\u003e line here like I\nwould if I was not using \u003ccode\u003e--net=container:vpn\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker run \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  --name\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003ejackett \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  --restart unless-stopped \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  -e \u003cspan style=\"color:#008080\"\u003ePUID\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#099\"\u003e1000\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  -e \u003cspan style=\"color:#008080\"\u003ePGID\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#099\"\u003e1000\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  -e \u003cspan style=\"color:#008080\"\u003eTZ\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003eEurope/London \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  --net\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003econtainer:vpn \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  -v /opt/appdata/jackett:/config \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  -v /opt/appdata/jackett/downloads:/downloads \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  linuxserver/jackett\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow I can access Jackett at \u003ccode\u003ehttp://\u0026lt;host-ip\u0026gt;:9117\u003c/code\u003e and all traffic will be sent\nthrough the VPN container\u0026rsquo;s network!\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI\u0026rsquo;ve recently taken to using Docker to install and run various software\non my home server. Something that so far, it excels at - the people at\n\u003ca href=\"https://linuxserver.io\"\u003elinuxserver.io\u003c/a\u003e are doing great work! I\u0026rsquo;ve had a rocky time with Docker in\nthe past after having had it foisted upon me for development work, which I did\n\u003cem\u003enot\u003c/em\u003e enjoy, but I can see the benefits for certain situations.\u003c/p\u003e\n\u003cp\u003eI found myself needing to run the traffic from one particular container\n(\u003ca href=\"https://github.com/linuxserver/docker-jackett\"\u003eJackett\u003c/a\u003e) over a VPN connection so that it could by-pass country-specific\nrestrictions. As a noob Docker user, this caused some confusion, but I eventually\nstumbled upon the \u003ccode\u003e--net\u003c/code\u003e parameter to \u003ccode\u003edocker create\u003c/code\u003e and \u003ccode\u003erun\u003c/code\u003e.\u003c/p\u003e",
      "date_published": "2019-06-03T00:00:00+00:00",
      "date_modified": "2019-06-03T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/how-to-replace-a-failed-disk-in-a-zfs-mirror/",
      "url": "https://elver.me/blog/how-to-replace-a-failed-disk-in-a-zfs-mirror/","title": "How to replace a failed disk in a ZFS mirror",
      "content_html": "\u003cp\u003eI recently built a new file server for my media needs at home. Something I\u0026rsquo;ve\nbeen thinking about doing for \u003cem\u003eliterally\u003c/em\u003e years.  I chose to go with ZFS as the\nstorage technology after having used Linux software RAID for many years.\nI went with a mirrored setup for a lot of the reasons \u003ca href=\"http://www.openoid.net/zfs-you-should-use-mirror-vdevs-not-raidz/\"\u003eoutlined in this\narticle\u003c/a\u003e - performance, simplicity, and in particular, easy recovery from\ndisk failures.\u003c/p\u003e\n\u003cp\u003eThis is the setup I ended up with according to \u003ccode\u003ezpool status\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ zpool status\n  pool: storage\n state: ONLINE\n  scan: none requested\nconfig:\n\n\tNAME                                   STATE     READ WRITE CKSUM\n\tstorage                                ONLINE       0     0     0\n\t  mirror-0                             ONLINE       0     0     0\n\t    ata-WDC_WD80EFZX-68UW8N0_VJHDBDGX  ONLINE       0     0     0\n\t    ata-WDC_WD80EFAX-68KNBN0_VAGASE7L  ONLINE       0     0     0\n\t  mirror-1                             ONLINE       0     0     0\n\t    ata-WDC_WD80EFZX-68UW8N0_VJHD6BAX  ONLINE       0     0     0\n\t    ata-WDC_WD80EFAX-68KNBN0_VAGA5BPL  ONLINE       0     0     0\n\t  mirror-2                             ONLINE       0     0     0\n\t    ata-WDC_WD80EFZX-68UW8N0_VJHD982X  ONLINE       0     0     0\n\t    ata-WDC_WD80EFAX-68KNBN0_VAG9X8YL  ONLINE       0     0     0\n\nerrors: No known data errors\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWell, no sooner had I completed the ZFS setup (a very straightforward process)\nthan one of my disks started reporting SMART errors. I don\u0026rsquo;t think a disk that\nis weeks old should do this, so I decided to start the RMA process.\u003c/p\u003e\n\u003cp\u003eAnd this is how I replaced the disk.\u003c/p\u003e\n\u003ch2 id=\"replacing-the-disk\"\u003eReplacing the disk\u003c/h2\u003e\n\u003cp\u003eI started by physically removing the old disk, and replacing with a brand new\none. I originally setup my pool using the disk id from \u003ccode\u003e/dev/disk/by-id/\u003c/code\u003e, so\nidentifying the failed disk was very easy as the serial number is part of the\ndevice name\u003c/p\u003e\n\u003cp\u003eOnce I started back up, I ran \u003ccode\u003ezpool status\u003c/code\u003e and saw this output.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ zpools status\n  pool: storage\n state: DEGRADED\nstatus: One or more devices could not be used because the label is missing or\n\tinvalid.  Sufficient replicas exist for the pool to continue\n\tfunctioning in a degraded state.\naction: Replace the device using \u0026#39;zpool replace\u0026#39;.\n   see: http://zfsonlinux.org/msg/ZFS-8000-4J\n  scan: none requested\nconfig:\n\n\tNAME                                   STATE     READ WRITE CKSUM\n\tstorage                                DEGRADED     0     0     0\n\t  mirror-0                             ONLINE       0     0     0\n\t    ata-WDC_WD80EFZX-68UW8N0_VJHDBDGX  ONLINE       0     0     0\n\t    ata-WDC_WD80EFAX-68KNBN0_VAGASE7L  ONLINE       0     0     0\n\t  mirror-1                             ONLINE       0     0     0\n\t    ata-WDC_WD80EFZX-68UW8N0_VJHD6BAX  ONLINE       0     0     0\n\t    ata-WDC_WD80EFAX-68KNBN0_VAGA5BPL  ONLINE       0     0     0\n\t  mirror-2                             DEGRADED     0     0     0\n\t    ata-WDC_WD80EFZX-68UW8N0_VJHD982X  ONLINE       0     0     0\n\t    18311740819329882151               UNAVAIL      0     0     0  was /dev/disk/by-id/ata-WDC_WD80EFAX-68KNBN0_VAG9X8YL-part1\n\nerrors: No known data errors\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eZFS noticed that it had a missing disk, and was now in a \u003ccode\u003eDEGRADED\u003c/code\u003e state, but\ncrucially, everything was still working and available.\u003c/p\u003e\n\u003cp\u003eThe next step was to find out what the \u003cem\u003enew\u003c/em\u003e device is called. I did this by\nrunning \u003ccode\u003els -1 /dev/disk/by-id/\u003c/code\u003e and seeing which disk was new.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ ls -1 /dev/disk/by-id/ | grep ata\nata-WDC_WD80EFAX-68KNBN0_VAGA5BPL\nata-WDC_WD80EFAX-68KNBN0_VAGA5BPL-part1\nata-WDC_WD80EFAX-68KNBN0_VAGA5BPL-part9\nata-WDC_WD80EFAX-68KNBN0_VAGASE7L\nata-WDC_WD80EFAX-68KNBN0_VAGASE7L-part1\nata-WDC_WD80EFAX-68KNBN0_VAGASE7L-part9\nata-WDC_WD80EFAX-68LHPN0_7HJSWL7F\nata-WDC_WD80EFZX-68UW8N0_VJHD6BAX\nata-WDC_WD80EFZX-68UW8N0_VJHD6BAX-part1\nata-WDC_WD80EFZX-68UW8N0_VJHD6BAX-part9\nata-WDC_WD80EFZX-68UW8N0_VJHD982X\nata-WDC_WD80EFZX-68UW8N0_VJHD982X-part1\nata-WDC_WD80EFZX-68UW8N0_VJHD982X-part9\nata-WDC_WD80EFZX-68UW8N0_VJHDBDGX\nata-WDC_WD80EFZX-68UW8N0_VJHDBDGX-part1\nata-WDC_WD80EFZX-68UW8N0_VJHDBDGX-part9\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe new disk is the one on line 8 - \u003ccode\u003eata-WDC_WD80EFAX-68LHPN0_7HJSWL7F\u003c/code\u003e. It\nstands out in this example as all the other disk serial numbers start with \u0026ldquo;V\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eI now needed to tell ZFS to replace the missing disk with this one.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esudo zpool replace -f storage 18311740819329882151 /dev/disk/by-id/ata-WDC_WD80EFAX-68LHPN0_7HJSWL7F\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eZFS automatically started the resilvering process (copying data to the new\ndisk). I wasn\u0026rsquo;t sure how long that would take\u0026hellip;\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ zpool status\n  pool: storage\n state: DEGRADED\nstatus: One or more devices is currently being resilvered.  The pool will\n\tcontinue to function, possibly in a degraded state.\naction: Wait for the resilver to complete.\n  scan: resilver in progress since Thu Nov 15 17:01:06 2018\n\t7.97G scanned out of 7.51T at 233M/s, 9h22m to go\n\t2.56G resilvered, 0.10% done\nconfig:\n\n\tNAME                                     STATE     READ WRITE CKSUM\n\tstorage                                  DEGRADED     0     0     0\n\t  mirror-0                               ONLINE       0     0     0\n\t    ata-WDC_WD80EFZX-68UW8N0_VJHDBDGX    ONLINE       0     0     0\n\t    ata-WDC_WD80EFAX-68KNBN0_VAGASE7L    ONLINE       0     0     0\n\t  mirror-1                               ONLINE       0     0     0\n\t    ata-WDC_WD80EFZX-68UW8N0_VJHD6BAX    ONLINE       0     0     0\n\t    ata-WDC_WD80EFAX-68KNBN0_VAGA5BPL    ONLINE       0     0     0\n\t  mirror-2                               DEGRADED     0     0     0\n\t    ata-WDC_WD80EFZX-68UW8N0_VJHD982X    ONLINE       0     0     0\n\t    replacing-1                          DEGRADED     0     0     0\n\t      18311740819329882151               UNAVAIL      0     0     0  was /dev/disk/by-id/ata-WDC_WD80EFAX-68KNBN0_VAG9X8YL-part1\n\t      ata-WDC_WD80EFAX-68LHPN0_7HJSWL7F  ONLINE       0     0     0  (resilvering)\n\nerrors: No known data errors\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe resilvering completed in 5 hours and 53 minutes. A figure I\u0026rsquo;m very satisfied\nwith. In this mirrored setup the data is at risk whilst resilvering completes,\nso the quicker, the better.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ zpool status\n  pool: storage\n state: ONLINE\n  scan: resilvered 2.50T in 5h53m with 0 errors on Thu Nov 15 22:54:41 2018\nconfig:\n\n\tNAME                                   STATE     READ WRITE CKSUM\n\tstorage                                ONLINE       0     0     0\n\t  mirror-0                             ONLINE       0     0     0\n\t    ata-WDC_WD80EFZX-68UW8N0_VJHDBDGX  ONLINE       0     0     0\n\t    ata-WDC_WD80EFAX-68KNBN0_VAGASE7L  ONLINE       0     0     0\n\t  mirror-1                             ONLINE       0     0     0\n\t    ata-WDC_WD80EFZX-68UW8N0_VJHD6BAX  ONLINE       0     0     0\n\t    ata-WDC_WD80EFAX-68KNBN0_VAGA5BPL  ONLINE       0     0     0\n\t  mirror-2                             ONLINE       0     0     0\n\t    ata-WDC_WD80EFZX-68UW8N0_VJHD982X  ONLINE       0     0     0\n\t    ata-WDC_WD80EFAX-68LHPN0_7HJSWL7F  ONLINE       0     0     0\n\nerrors: No known data errors\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eZFS is easy to setup and use for the most part. It \u003cem\u003efeels\u003c/em\u003e solid. Stable.\nIf all disk replacements are this easy I will be very happy.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI recently built a new file server for my media needs at home. Something I\u0026rsquo;ve\nbeen thinking about doing for \u003cem\u003eliterally\u003c/em\u003e years.  I chose to go with ZFS as the\nstorage technology after having used Linux software RAID for many years.\nI went with a mirrored setup for a lot of the reasons \u003ca href=\"http://www.openoid.net/zfs-you-should-use-mirror-vdevs-not-raidz/\"\u003eoutlined in this\narticle\u003c/a\u003e - performance, simplicity, and in particular, easy recovery from\ndisk failures.\u003c/p\u003e\n\u003cp\u003eThis is the setup I ended up with according to \u003ccode\u003ezpool status\u003c/code\u003e.\u003c/p\u003e",
      "date_published": "2018-11-26T00:00:00+00:00",
      "date_modified": "2018-11-26T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/a-new-way-to-deploy-to-aws/",
      "url": "https://elver.me/blog/a-new-way-to-deploy-to-aws/","title": "A new way to deploy to Amazon S3",
      "content_html": "\u003cp\u003ePretty much since I began hosting this site on Amazon S3 I\u0026rsquo;ve been using\n\u003ca href=\"https://github.com/fredjean/middleman-s3_sync\"\u003emiddleman-s3_sync\u003c/a\u003e to do the heavy lifting of syncing files to the bucket. Unfortunately, somewhere along the way it stopped picking up certain\nfiles that should\u0026rsquo;ve been synced. I tried to be a good OSS citizen and find a fix, but\nafter delving into the Middleman innards, I couldn\u0026rsquo;t find the problem.\u003c/p\u003e\n\u003ch2 id=\"a-new-method\"\u003eA new method\u003c/h2\u003e\n\u003cp\u003eA new method of deploying the site was needed and recently I moved to\nusing Amazon\u0026rsquo;s own \u003ca href=\"https://aws.amazon.com/cli/\"\u003eaws cli\u003c/a\u003e utility. After years of providing next to\nnothing in regards to tooling, Amazon have released their own command line\nutility for interacting with AWS. And the best part is that it seems very nice .\u003c/p\u003e\n\u003cp\u003eThis is how I now deploy the site.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eaws s3 sync build s3://jordanelver.co.uk \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  --profile jordanelver.co.uk \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  --delete \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  --acl public-read \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#d14\"\u003e\u003c/span\u003e  --exclude *.DS_Store \u003cspan style=\"color:#d14\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003ebuild\u003c/code\u003e is the local directory to be synced, and \u003ccode\u003es3://jordanelver.co.uk\u003c/code\u003e is the remote\nS3 bucket.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e--delete\u003c/code\u003e says to delete files at the destination that are not in the \u003ccode\u003ebuild/\u003c/code\u003e\ndirectory.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e--profile\u003c/code\u003e controls which credentials and other settings to use when connecting\nto S3. More on this below.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e--acl public-read\u003c/code\u003e makes the synced files publicly readable, \u003ca href=\"http://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteAccessPermissionsReqd.html\"\u003eas required by S3\nstatic website hosting\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e--exclude\u003c/code\u003e excludes those pesky \u003ccode\u003e.DS_Store\u003c/code\u003e files that OS X litters around the\nfilesystem.\u003c/p\u003e\n\u003ch2 id=\"credentials\"\u003eCredentials\u003c/h2\u003e\n\u003cp\u003eCredentials are stored in \u003ccode\u003e~/.aws/credentials\u003c/code\u003e. I have a section like this.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003ejordanelver.co.uk\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#008080\"\u003eaws_access_key_id\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u0026lt;ACCESSKEY\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#008080\"\u003eaws_secret_access_key\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u0026lt;SECRET\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou\u0026rsquo;ll see that this matches the \u003ccode\u003e--profile jordanelver.co.uk\u003c/code\u003e line in the\n\u003ccode\u003es3 sync\u003c/code\u003e commandline.\u003c/p\u003e\n\u003cp\u003eThat covers authentication, but you can also specify additional configuration\nvalues in \u003ccode\u003e~/.aws/config\u003c/code\u003e. In this case, the S3 bucket region.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003eprofile jordanelver.co.uk\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#008080\"\u003eregion\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e eu-west-1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI used \u003ccode\u003eaws configure --profile jordanelver.co.uk\u003c/code\u003e to configure these values.\u003c/p\u003e\n\u003ch2 id=\"one-last-thing\"\u003eOne last thing\u003c/h2\u003e\n\u003cp\u003eOne last thing that deserves to be mentioned is the \u003ccode\u003e--dryrun\u003c/code\u003e flag.  It\u0026rsquo;s very\nhandy for testing what\u0026rsquo;s going to happen, before it happens. \u003ca href=\"https://github.com/jordelver/jordanelver.co.uk/blob/master/bin/deploy\"\u003eI ended up\nwrapping my sync command in a Ruby script\u003c/a\u003e to conditionally add \u003ccode\u003e--dryrun\u003c/code\u003e as\nand when I need it.\u003c/p\u003e\n\u003cp\u003eHappy syncing!\u003c/p\u003e\n",
      "summary": "\u003cp\u003ePretty much since I began hosting this site on Amazon S3 I\u0026rsquo;ve been using\n\u003ca href=\"https://github.com/fredjean/middleman-s3_sync\"\u003emiddleman-s3_sync\u003c/a\u003e to do the heavy lifting of syncing files to the bucket. Unfortunately, somewhere along the way it stopped picking up certain\nfiles that should\u0026rsquo;ve been synced. I tried to be a good OSS citizen and find a fix, but\nafter delving into the Middleman innards, I couldn\u0026rsquo;t find the problem.\u003c/p\u003e\n\u003ch2 id=\"a-new-method\"\u003eA new method\u003c/h2\u003e\n\u003cp\u003eA new method of deploying the site was needed and recently I moved to\nusing Amazon\u0026rsquo;s own \u003ca href=\"https://aws.amazon.com/cli/\"\u003eaws cli\u003c/a\u003e utility. After years of providing next to\nnothing in regards to tooling, Amazon have released their own command line\nutility for interacting with AWS. And the best part is that it seems very nice .\u003c/p\u003e",
      "date_published": "2017-10-29T00:00:00+00:00",
      "date_modified": "2017-10-29T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/split-lines-easily-in-vim/",
      "url": "https://elver.me/blog/split-lines-easily-in-vim/","title": "Split lines easily in Vim",
      "content_html": "\u003cp\u003eVim has a way of making me feel like a wizard one minute and a complete novice\nthe next. I constantly feel the need to revisit the basics, and this led me to\ntry and make \u003ccode\u003eJ\u003c/code\u003e stick in my Vim vocabulary.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eJ\u003c/code\u003e joins lines, I’ve been using it regularly for a while now; it’s finally\nfound its way into my muscle memory. It works like this.\u003c/p\u003e\n\u003cscript type=\"text/javascript\"\nsrc=\"https://asciinema.org/a/bozOxUMgIQV7TmK5OlbzUbazJ.js\"\nid=\"asciicast-bozOxUMgIQV7TmK5OlbzUbazJ\" async\u003e\u003c/script\u003e\n\u003cp\u003eI recently discovered a complementary plugin called \u003ca href=\"https://github.com/drzel/vim-split-line\"\u003evim-split-line\u003c/a\u003e. It\nmakes splitting lines as easy as joining. It mirrors \u003ccode\u003eJ\u003c/code\u003e, mapping by default to\n\u003ccode\u003eS\u003c/code\u003e. See how it works below.\u003c/p\u003e\n\u003cscript type=\"text/javascript\"\nsrc=\"https://asciinema.org/a/i48SaXAXDGkCVM9l8Pte7wziQ.js\"\nid=\"asciicast-i48SaXAXDGkCVM9l8Pte7wziQ\" async\u003e\u003c/script\u003e\n\u003cp\u003eThere are, no doubt, countless ways to do this using Vim (\u003ca href=\"https://github.com/drzel/vim-split-line#alternatives\"\u003ein fact, the author\nof the plugin points this out\u003c/a\u003e), but I really like the simplicity of this\nplugin. And it handles a couple of edgecases for you too such as auto indenting\nthe second line, and removing trailing whitespace.\u003c/p\u003e\n\u003cp\u003eLet’s hope this one sticks.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eVim has a way of making me feel like a wizard one minute and a complete novice\nthe next. I constantly feel the need to revisit the basics, and this led me to\ntry and make \u003ccode\u003eJ\u003c/code\u003e stick in my Vim vocabulary.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eJ\u003c/code\u003e joins lines, I’ve been using it regularly for a while now; it’s finally\nfound its way into my muscle memory. It works like this.\u003c/p\u003e\n\u003cscript type=\"text/javascript\"\nsrc=\"https://asciinema.org/a/bozOxUMgIQV7TmK5OlbzUbazJ.js\"\nid=\"asciicast-bozOxUMgIQV7TmK5OlbzUbazJ\" async\u003e\u003c/script\u003e\n\u003cp\u003eI recently discovered a complementary plugin called \u003ca href=\"https://github.com/drzel/vim-split-line\"\u003evim-split-line\u003c/a\u003e. It\nmakes splitting lines as easy as joining. It mirrors \u003ccode\u003eJ\u003c/code\u003e, mapping by default to\n\u003ccode\u003eS\u003c/code\u003e. See how it works below.\u003c/p\u003e",
      "date_published": "2017-09-25T00:00:00+00:00",
      "date_modified": "2017-09-25T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/capturing-output-from-heroku-with-tee/",
      "url": "https://elver.me/blog/capturing-output-from-heroku-with-tee/","title": "Capturing output from Heroku with tee",
      "content_html": "\u003cp\u003eHave you ever been working with a console on Heroku and needed to capture the\noutput? I was recently tasked with generating a quick \u0026ldquo;report\u0026rdquo;, and I decided\nto spin up a \u003ca href=\"http://guides.rubyonrails.org/command_line.html#rails-console\"\u003eRails console\u003c/a\u003e on Heroku to connect to the database and munge\nsome data into the right format.\u003c/p\u003e\n\u003cp\u003eThe problem is how to capture the output from Heroku. What are the options for\nsaving that on your computer? Well, you \u003cem\u003ecan\u003c/em\u003e write it to the filesystem, but\nthen how do you transfer the file? You could upload it to Amazon S3, or use\nFTP, or something else, but it starts getting complicated fast\u0026hellip;\u003c/p\u003e\n\u003cp\u003eInstead, we use \u003ca href=\"https://en.wikipedia.org/wiki/Tee_(command)\"\u003e\u003ccode\u003etee\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eHere we run a Rails console on Heroku but also redirect all output to a file\ncalled \u003ccode\u003eoutput.log\u003c/code\u003e.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eheroku run bundle exec rails console | tee output.log\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eWe can work with the console as usual, but also capture the whole session in a\nfile for later inspection. We will get all of the input and output (commands we\ntyped, and their responses) so there is often some post processing to do. But\nstill, this is a quick and dirty way to save output from Heroku\u003c/p\u003e\n",
      "summary": "\u003cp\u003eHave you ever been working with a console on Heroku and needed to capture the\noutput? I was recently tasked with generating a quick \u0026ldquo;report\u0026rdquo;, and I decided\nto spin up a \u003ca href=\"http://guides.rubyonrails.org/command_line.html#rails-console\"\u003eRails console\u003c/a\u003e on Heroku to connect to the database and munge\nsome data into the right format.\u003c/p\u003e\n\u003cp\u003eThe problem is how to capture the output from Heroku. What are the options for\nsaving that on your computer? Well, you \u003cem\u003ecan\u003c/em\u003e write it to the filesystem, but\nthen how do you transfer the file? You could upload it to Amazon S3, or use\nFTP, or something else, but it starts getting complicated fast\u0026hellip;\u003c/p\u003e",
      "date_published": "2017-09-17T00:00:00+00:00",
      "date_modified": "2017-09-17T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/renaming-files-like-a-pro/",
      "url": "https://elver.me/blog/renaming-files-like-a-pro/","title": "Renaming files like a pro",
      "content_html": "\u003cp\u003eRenaming files en masse was always something that I knew the commandline could\nexcel at but that I\u0026rsquo;d never managed to easily replicate daily. I\u0026rsquo;d always have\nto look up the shell syntax for looping over a list of files, and the rules for\nescaping paths, and so on and so forth. Whilst I\u0026rsquo;d like to improve my skills\nwith the shell, in the meantime I have discovered another option, \u003ccode\u003erename\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eNow, when I say \u003ccode\u003erename\u003c/code\u003e, there is \u003ca href=\"http://unix.stackexchange.com/questions/229230/whats-with-all-the-renames\"\u003esome confusion\u003c/a\u003e about various different\nversions of this utility. I\u0026rsquo;ve been using the version included with Ubuntu\nTrusty 14.04 and also \u003ca href=\"http://plasmasturm.org/code/rename/\"\u003ethe version included in Homebrew\u003c/a\u003e on macOS Sierra.\nThe examples below work with both of these versions.\u003c/p\u003e\n\u003cp\u003eA good understanding of regular expressions (the Perl variety) will allow you\nto get the most out of \u003ccode\u003erename\u003c/code\u003e but you can still use it in a basic fashion and\nget great results.\u003c/p\u003e\n\u003ch2 id=\"some-examples\"\u003eSome examples\u003c/h2\u003e\n\u003cp\u003eTake these files.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e01-run_the_jewels-run_the_jewels.flac\n02-run_the_jewels-banana_clipper_(feat._big_boi).flac\n03-run_the_jewels-36_inch_chain.flac\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eLet\u0026rsquo;s make the filenames uppercase (because we are monsters).\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ rename -n \u0026#39;y/a-z/A-Z/ *.flac\n01-run_the_jewels-run_the_jewels.flac renamed as 01-RUN_THE_JEWELS-RUN_THE_JEWELS.FLAC\n02-run_the_jewels-banana_clipper_(feat._big_boi).flac renamed as 02-RUN_THE_JEWELS-BANANA_CLIPPER_(FEAT._BIG_BOI).FLAC\n03-run_the_jewels-36_inch_chain.flac renamed as 03-RUN_THE_JEWELS-36_INCH_CHAIN.FLAC\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eYou\u0026rsquo;ll notice that \u003ccode\u003erename\u003c/code\u003e prints out the renaming it \u003cem\u003ewould\u003c/em\u003e do. This is\nbecause we\u0026rsquo;ve used the \u003ccode\u003e-n\u003c/code\u003e flag, which turns on dry runs allowing us to see\nthe changes that will be made before we commit to changing them. You\u0026rsquo;ll find\n\u003ccode\u003e-n\u003c/code\u003e invaluable when using \u003ccode\u003erename\u003c/code\u003e, especially when new to it.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s change dashes and underscores to spaces instead.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ rename -n \u0026#39;s/-|_/ /g\u0026#39; *.flac\n\u0026#39;01-run_the_jewels-run_the_jewels.flac\u0026#39; would be renamed to \u0026#39;01 run the jewels run the jewels.flac\u0026#39;\n\u0026#39;02-run_the_jewels-banana_clipper_(feat._big_boi).flac\u0026#39; would be renamed to \u0026#39;02 run the jewels banana clipper (feat. big boi).flac\u0026#39;\n\u0026#39;03-run_the_jewels-36_inch_chain.flac\u0026#39; would be renamed to \u0026#39;03 run the jewels 36 inch chain.flac\u0026#39;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOr remove \u003ccode\u003e-run_the_jewels-\u003c/code\u003e from the filename entirely.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ rename -n \u0026#39;s/-run_the_jewels-/ /g\u0026#39; *.flac\n\u0026#39;01-run_the_jewels-run_the_jewels.flac\u0026#39; would be renamed to \u0026#39;01 run_the_jewels.flac\u0026#39;\n\u0026#39;02-run_the_jewels-banana_clipper_(feat._big_boi).flac\u0026#39; would be renamed to \u0026#39;02 banana_clipper_(feat._big_boi).flac\u0026#39;\n\u0026#39;03-run_the_jewels-36_inch_chain.flac\u0026#39; would be renamed to \u0026#39;03 36_inch_chain.flac\u0026#39;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eGetting a bit more advanced, you can even use captures in your expression. Take\na directory of files with names like this.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eS03E01-oh-em-gee-i-love-telly.mkv\nS03E02-thrilling-watch.mkv\nS03E03-a-great-episode.mkv\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe\u0026rsquo;ll rename the filenames to look like \u003ccode\u003eS??E??.mkv\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ rename -n \u0026#39;s/.*(S\\d{1,2}E\\d{1,2}).*(.{3}$)/$1.$2/g\u0026#39; *.mkv\n\u0026#39;S03E01-oh-em-gee-i-love-telly.mkv\u0026#39; would be renamed to \u0026#39;S03E01.mkv\u0026#39;\n\u0026#39;S03E02-thrilling-watch.mkv\u0026#39; would be renamed to \u0026#39;S03E02.mkv\u0026#39;\n\u0026#39;S03E03-a-great-episode.mkv\u0026#39; would be renamed to \u0026#39;S03E03.mkv\u0026#39;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe regular expression matches the \u003ccode\u003eS??E??\u003c/code\u003e part of the filename as \u003ccode\u003e$1\u003c/code\u003e, and the extension \u003ccode\u003e.mkv\u003c/code\u003e\nas \u003ccode\u003e$2\u003c/code\u003e, and uses them both to construct a new filename like \u003ccode\u003eS03E01.mkv\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThat regular expression is quite a mouthfull, so I\u0026rsquo;d recommend using a tool\nto help you construct your expressions. I personally use \u003ca href=\"http://rubular.com/\"\u003eRubular\u003c/a\u003e even\nthough it\u0026rsquo;s Ruby-specific; it does a good job for me.\u003c/p\u003e\n\u003cp\u003eI hope you can get a sense of how powerful \u003ccode\u003erename\u003c/code\u003e can be. Regular expressions\nare the limit to how you can rename files.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eRenaming files en masse was always something that I knew the commandline could\nexcel at but that I\u0026rsquo;d never managed to easily replicate daily. I\u0026rsquo;d always have\nto look up the shell syntax for looping over a list of files, and the rules for\nescaping paths, and so on and so forth. Whilst I\u0026rsquo;d like to improve my skills\nwith the shell, in the meantime I have discovered another option, \u003ccode\u003erename\u003c/code\u003e.\u003c/p\u003e",
      "date_published": "2017-01-31T00:00:00+00:00",
      "date_modified": "2017-01-31T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/how-i-added-heading-anchors-with-middleman/",
      "url": "https://elver.me/blog/how-i-added-heading-anchors-with-middleman/","title": "How I added heading anchors with Middleman",
      "content_html": "\u003cp\u003eI really like being able to link directly to certain parts of web page.\nUnfortunately, many page authors fail to add the necessary anchors to make\nthis possible. Until recently, this included me!\u003c/p\u003e\n\u003cp\u003eHere is how I remedied the situation.\u003c/p\u003e\n\u003cp\u003eOutputting headings with id attributes \u003ca href=\"https://forum.middlemanapp.com/t/using-a-custom-redcarpet-renderer/1004\"\u003elooked like it should\u0026rsquo;ve already been\nsupported out of the box\u003c/a\u003e. I could see a \u003ccode\u003etoc_data\u003c/code\u003e option, but for some\nreason this didn\u0026rsquo;t work for me, so I created a custom Markdown renderer\ninstead.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#0086b3\"\u003erequire\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;middleman-core/renderers/redcarpet\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#458;font-weight:bold\"\u003eCustomRenderer\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eMiddleman\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e::\u003c/span\u003e\u003cspan style=\"color:#008080\"\u003eRenderers\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e::\u003c/span\u003e\u003cspan style=\"color:#008080\"\u003eMiddlemanRedcarpetHTML\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#900;font-weight:bold\"\u003eheader\u003c/span\u003e(text, header_level)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#d14\"\u003e\u0026#34;\u0026lt;h%s id=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\\\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e%s\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\\\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026gt;%s\u0026lt;/h%s\u0026gt;\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003eheader_level, text\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eparameterize, text, header_level\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI inherit from the default renderer so that I can replace the \u003ccode\u003eheader\u003c/code\u003e\nimplementation to add the necessary \u003ccode\u003eid\u003c/code\u003e attributes.\u003c/p\u003e\n\u003cp\u003eI required it in my \u003ccode\u003econfig.rb\u003c/code\u003e and then configured \u003ca href=\"https://github.com/vmg/redcarpet\"\u003eRedcarpet\u003c/a\u003e to use the\nnew renderer.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset \u003cspan style=\"color:#990073\"\u003e:markdown\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#990073\"\u003efenced_code_blocks\u003c/span\u003e: \u003cspan style=\"color:#000;font-weight:bold\"\u003etrue\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#990073\"\u003esmartypants\u003c/span\u003e: \u003cspan style=\"color:#000;font-weight:bold\"\u003etrue\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#990073\"\u003erenderer\u003c/span\u003e: \u003cspan style=\"color:#008080\"\u003eCustomRenderer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThere we have it. Heading tags now have ids, so we can link directly to them.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI really like being able to link directly to certain parts of web page.\nUnfortunately, many page authors fail to add the necessary anchors to make\nthis possible. Until recently, this included me!\u003c/p\u003e\n\u003cp\u003eHere is how I remedied the situation.\u003c/p\u003e\n\u003cp\u003eOutputting headings with id attributes \u003ca href=\"https://forum.middlemanapp.com/t/using-a-custom-redcarpet-renderer/1004\"\u003elooked like it should\u0026rsquo;ve already been\nsupported out of the box\u003c/a\u003e. I could see a \u003ccode\u003etoc_data\u003c/code\u003e option, but for some\nreason this didn\u0026rsquo;t work for me, so I created a custom Markdown renderer\ninstead.\u003c/p\u003e",
      "date_published": "2016-03-19T00:00:00+00:00",
      "date_modified": "2016-03-19T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/free-ssl-with-amazon-certificate-manager-and-cloudfront/",
      "url": "https://elver.me/blog/free-ssl-with-amazon-certificate-manager-and-cloudfront/","title": "Free SSL with Amazon Certificate Manager and Cloudfront",
      "content_html": "\u003cp\u003eFollowing my \u003ca href=\"/blog/moving-to-amazon-s3/\"\u003erecent move to Amazon S3\u003c/a\u003e I\u0026rsquo;ve now gone one step further and\nadded \u003ca href=\"https://aws.amazon.com/cloudfront/\"\u003eCloudfront\u003c/a\u003e to the mix. Cloudfront is a \u003ca href=\"https://en.wikipedia.org/wiki/Content_delivery_network\"\u003eCDN\u003c/a\u003e; it puts web pages\ncloser to the client so they\u0026rsquo;re faster. Faster is better. However, my\nmotivation for adding Cloudfront was not speed, it was SSL.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;d already been toying with the idea of adding SSL to this site with\n\u003ca href=\"https://www.cloudflare.com/\"\u003eCloudflare\u003c/a\u003e when fellow \u003ca href=\"http://www.meetup.com/bathruby/\"\u003eBath Ruby\u003c/a\u003e attendee Paul Leader \u003ca href=\"https://twitter.com/NoNeeeed/status/690544764706504705\"\u003etold me\nthat Amazon were offering free SSL\u003c/a\u003e through their \u003ca href=\"https://aws.amazon.com/blogs/aws/new-aws-certificate-manager-deploy-ssltls-based-apps-on-aws/\"\u003erecently announced\nAmazon Certificate Manager\u003c/a\u003e product. I decided to give it a try.\u003c/p\u003e\n\u003ch2 id=\"why-ssl\"\u003eWhy SSL?\u003c/h2\u003e\n\u003cp\u003ePrivacy and trust. Visitors to this site are getting the version of this page\nthat I publish. No one can interfere or tamper with it. No ads or malware can\nbe inserted. And why not? SSL is no longer expensive to implement. Gone are the\ndays of being extorted by companies selling SSL certificates. SSL is now freely\navailable from the likes of Cloudflare, Amazon and, perhaps more importantly,\n\u003ca href=\"https://letsencrypt.org/\"\u003eLet\u0026rsquo;s Encrypt\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"amazon-certificate-manager\"\u003eAmazon Certificate Manager?\u003c/h2\u003e\n\u003cp\u003eAs with all Amazon products it\u0026rsquo;s often difficult to understand a) What it is;\nand b) What it\u0026rsquo;s for. Amazon Certificate Manager allows you to create SSL\ncertificates for free and easily use them with Amazon products. Currently,\n\u003ca href=\"https://aws.amazon.com/elasticloadbalancing/\"\u003eELB\u003c/a\u003e and Cloudfront are supported. The best part is that they automatically\nhandle renewals for you! Once setup, there is no maintenance to do.\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003ch3 id=\"distribution-setup\"\u003eDistribution setup\u003c/h3\u003e\n\u003cp\u003eCloudfront has a concept called Distributions. A distribution is a set of files\nthat Amazon will distribute across the world to be served from it\u0026rsquo;s \u003ca href=\"https://aws.amazon.com/about-aws/global-infrastructure/\"\u003eedge\nlocations\u003c/a\u003e meaning the files will be geographically closer to the client\nrequesting them.\u003c/p\u003e\n\u003cp\u003eSetting up the distribution looks fairly complex but I went with the defaults\nand it seems to work well. You can point a distribution straight to an S3\nbucket but I found that not all of my pages were picked up by Cloudfront if I\ndid it that way. Index pages were missed for some reason. The fix was to\npoint the distribution at the Amazon S3 URL instead. The pages were then all\ncorrectly picked up.\u003c/p\u003e\n\u003ch3 id=\"dns-changes\"\u003eDNS changes\u003c/h3\u003e\n\u003cp\u003eLike using S3 directly a very simple change is required. I already had an\n\u003ccode\u003eALIAS\u003c/code\u003e record to point from \u003ccode\u003ejordanelver.co.uk\u003c/code\u003e to\n\u003ccode\u003ejordanelver-co-uk.s3-website-eu-west-1.amazonaws.com\u003c/code\u003e. I\u0026rsquo;ve now changed that\n\u003ccode\u003eALIAS\u003c/code\u003e to point to the Cloudfront address instead, which is\n\u003ccode\u003ed1jmxhequyb081.cloudfront.net\u003c/code\u003e.\u003c/p\u003e\n\u003ch3 id=\"invalidating-the-cache\"\u003eInvalidating the cache\u003c/h3\u003e\n\u003cp\u003eNow that the site content is spread across Amazon\u0026rsquo;s network and cached\nappropriately, the cache will need to be invalidated when new files are\ndeployed to S3 or we\u0026rsquo;ll be serving stale content. \u003ca href=\"https://github.com/andrusha/middleman-cloudfront\"\u003eThere is a Middleman\nextension to handle this\u003c/a\u003e but at the moment I\u0026rsquo;m manually invalidating the\ncache through the Amazon Cloudfront console. As I deploy infrequently it isn\u0026rsquo;t\na problem for me to do it manually at the moment but I might end up using\nthat extension eventually.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eI thought that the site was fairly speedy when served as a \u003ca href=\"https://github.com/rack/rack\"\u003erack\u003c/a\u003e app on\nHeroku. The speed seemed to increase when I moved to S3 but now it\u0026rsquo;s on\nCloudfront it \u003cem\u003efeels\u003c/em\u003e noticably faster! Combine those speed increases with free\nSSL and I\u0026rsquo;m very happy.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eFollowing my \u003ca href=\"/blog/moving-to-amazon-s3/\"\u003erecent move to Amazon S3\u003c/a\u003e I\u0026rsquo;ve now gone one step further and\nadded \u003ca href=\"https://aws.amazon.com/cloudfront/\"\u003eCloudfront\u003c/a\u003e to the mix. Cloudfront is a \u003ca href=\"https://en.wikipedia.org/wiki/Content_delivery_network\"\u003eCDN\u003c/a\u003e; it puts web pages\ncloser to the client so they\u0026rsquo;re faster. Faster is better. However, my\nmotivation for adding Cloudfront was not speed, it was SSL.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;d already been toying with the idea of adding SSL to this site with\n\u003ca href=\"https://www.cloudflare.com/\"\u003eCloudflare\u003c/a\u003e when fellow \u003ca href=\"http://www.meetup.com/bathruby/\"\u003eBath Ruby\u003c/a\u003e attendee Paul Leader \u003ca href=\"https://twitter.com/NoNeeeed/status/690544764706504705\"\u003etold me\nthat Amazon were offering free SSL\u003c/a\u003e through their \u003ca href=\"https://aws.amazon.com/blogs/aws/new-aws-certificate-manager-deploy-ssltls-based-apps-on-aws/\"\u003erecently announced\nAmazon Certificate Manager\u003c/a\u003e product. I decided to give it a try.\u003c/p\u003e",
      "date_published": "2016-02-16T00:00:00+00:00",
      "date_modified": "2016-02-16T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/moving-to-amazon-s3/",
      "url": "https://elver.me/blog/moving-to-amazon-s3/","title": "Moving to Amazon S3",
      "content_html": "\u003cp\u003eA couple of years ago \u003ca href=\"/blog/how-i-deployed-middleman-to-heroku/\"\u003eI wrote about how I deployed this blog to Heroku\u003c/a\u003e.\nSince then everything has changed; it is now served from \u003ca href=\"https://aws.amazon.com/s3/\"\u003eAmazon S3\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"why-change\"\u003eWhy change?\u003c/h2\u003e\n\u003cp\u003eIt was prompted by \u003ca href=\"https://blog.heroku.com/archives/2015/6/15/dynos-pricing-ga\"\u003eHeroku\u0026rsquo;s price changes last year\u003c/a\u003e. I think their new\npricing model is completely reasonable, and their \u0026ldquo;free\u0026rdquo; product still\ngenerous, but Heroku \u003cem\u003ecan\u003c/em\u003e get expensive. This low traffic site will cost very\nlittle to host with Amazon.\u003c/p\u003e\n\u003cp\u003eIt seemed like a no-brainer to try it out, and this is how I did it.\u003c/p\u003e\n\u003ch2 id=\"setting-up-static-website-hosting\"\u003eSetting up Static Website Hosting\u003c/h2\u003e\n\u003cp\u003eAmazon offer Static Website Hosting as a feature of S3. This serves a\nbucket over HTTP at a URL that Amazon gives you. The first step was to create a\nbucket to host the files. This is easily done through the \u003ca href=\"https://aws.amazon.com/console/\"\u003eAmazon S3\nconsole\u003c/a\u003e. I called mine jordanelver.co.uk, not because I\u0026rsquo;m self-obsessed,\nbut because that is the name of this website.\u003c/p\u003e\n\u003cp\u003eConfiguring the bucket to serve the site was straight forward. Once I\u0026rsquo;d enabled\nStatic Website Hosting in the bucket properties, the site was available at\n\u003ccode\u003ejordanelver-co-uk.s3-website-eu-west-1.amazonaws.com\u003c/code\u003e. Of course, I hadn\u0026rsquo;t\ntransferred anything to Amazon at this point, and the permissions were not yet\nset, so nothing but an error message was served at this point.\u003c/p\u003e\n\u003ch2 id=\"permissions\"\u003ePermissions\u003c/h2\u003e\n\u003cp\u003eSetting the correct permissions was by far the most confusing part of the whole\nprocess. There are two parts to this 1) creating a user to interact with S3;\nand 2) setting the correct bucket permissions.\u003c/p\u003e\n\u003cp\u003eI first setup an \u003ca href=\"http://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html\"\u003eIAM user\u003c/a\u003e that had access to the correct bucket.  This\nallowed me to upload the files to Amazon. These credentials are used later as\npart of the automated deployment.\u003c/p\u003e\n\u003cp\u003eThe second part is allowing the files in S3 to be served over HTTP to the public.\nBy default files in S3 are not publically available, for obvious reasons. I\nused the following policy to allow the files to be read. This is added through\nthe bucket properties.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e{\n  \u0026#34;Version\u0026#34;: \u0026#34;2012-10-17\u0026#34;,\n  \u0026#34;Statement\u0026#34;: [\n    {\n      \u0026#34;Sid\u0026#34;: \u0026#34;PublicReadGetObject\u0026#34;,\n      \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;,\n      \u0026#34;Principal\u0026#34;: \u0026#34;*\u0026#34;,\n      \u0026#34;Action\u0026#34;: \u0026#34;s3:GetObject\u0026#34;,\n      \u0026#34;Resource\u0026#34;: \u0026#34;arn:aws:s3:::jordanelver.co.uk/*\u0026#34;\n    }\n  ]\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"deploying-with-middleman\"\u003eDeploying with Middleman\u003c/h2\u003e\n\u003cp\u003eMoving away from Heroku meant losing the ability to deploy using \u003ccode\u003egit push\u003c/code\u003e so\nI went looking for other methods. Luckily, \u003ca href=\"https://directory.middlemanapp.com/#/extensions/deployment/\"\u003eMiddleman has many extensions\navailable\u003c/a\u003e to help with automating deployment. I decided on\n\u003ca href=\"https://github.com/fredjean/middleman-s3_sync\"\u003eMiddleman::S3Sync\u003c/a\u003e as it only transfers files that have changed, rather\nthan transferring everything each time, which seems like a good thing.\u003c/p\u003e\n\u003cp\u003eI added \u003ccode\u003egem 'middleman-s3_sync'\u003c/code\u003e to my \u003ccode\u003eGemfile\u003c/code\u003e, ran \u003ccode\u003ebundle\u003c/code\u003e and added the\nfollowing config to my \u003ccode\u003econfig.rb\u003c/code\u003e file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eactivate \u003cspan style=\"color:#990073\"\u003e:s3_sync\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003es3_sync\u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  s3_sync\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003edelete                     \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  s3_sync\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eafter_build                \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  s3_sync\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eprefer_gzip                \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  s3_sync\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003epath_style                 \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  s3_sync\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003ereduced_redundancy_storage \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  s3_sync\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eacl                        \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;public-read\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  s3_sync\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eencryption                 \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  s3_sync\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eversion_bucket             \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  s3_sync\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eindex_document             \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;index.html\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  s3_sync\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eerror_document             \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;404.html\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe sync process needs to know what the bucket name is, the Amazon region, and\nthe user credentials to upload the files. \u003ca href=\"https://github.com/fredjean/middleman-s3_sync#setting-aws-credentials\"\u003eThere are several supported\nmethods\u003c/a\u003e and I choose to use a \u003ccode\u003e.s3_sync\u003c/code\u003e file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#555\"\u003e---\u003c/span\u003e\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e\u003c/span\u003e\u003cspan style=\"color:#000080\"\u003ebucket\u003c/span\u003e:\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003ejordanelver.co.uk\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e\u003c/span\u003e\u003cspan style=\"color:#000080\"\u003eregion\u003c/span\u003e:\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003eeu-west-1\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e\u003c/span\u003e\u003cspan style=\"color:#000080\"\u003eaws_access_key_id\u003c/span\u003e:\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u0026lt;ACCESS_KEY_ID\u0026gt;\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#bbb\"\u003e\u003c/span\u003e\u003cspan style=\"color:#000080\"\u003eaws_secret_access_key\u003c/span\u003e:\u003cspan style=\"color:#bbb\"\u003e \u003c/span\u003e\u0026lt;SECRET_ACCESS_KEY\u0026gt;\u003cspan style=\"color:#bbb\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDeploying the site is now as simple as first building with \u003ccode\u003emiddleman build\u003c/code\u003e and\nthen syncing with \u003ccode\u003emiddleman s3_sync\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"dns-changes\"\u003eDNS changes\u003c/h2\u003e\n\u003cp\u003eThe final step is to hook up the domain name to the bucket. I use \u003ca href=\"https://dnsimple.com/r/d7a9918c2a5dd7\"\u003eDNSimple\u003c/a\u003e\nto host my DNS which makes the changes required simple. I setup an \u003ccode\u003eALIAS\u003c/code\u003e\nrecord to point from \u003ccode\u003ejordanelver.co.uk\u003c/code\u003e to\n\u003ccode\u003ejordanelver-co-uk.s3-website-eu-west-1.amazonaws.com\u003c/code\u003e and the site is now\nserved at this domain name.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;m very happy with this setup so far. Once you\u0026rsquo;ve set it up the first time, the\nprocess can easily be replicated to add additional sites. I can see that I\u0026rsquo;ll\ncontinue to use this method of hosting for my \u0026ldquo;holding page\u0026rdquo; and static sites\nneeds.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eA couple of years ago \u003ca href=\"/blog/how-i-deployed-middleman-to-heroku/\"\u003eI wrote about how I deployed this blog to Heroku\u003c/a\u003e.\nSince then everything has changed; it is now served from \u003ca href=\"https://aws.amazon.com/s3/\"\u003eAmazon S3\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"why-change\"\u003eWhy change?\u003c/h2\u003e\n\u003cp\u003eIt was prompted by \u003ca href=\"https://blog.heroku.com/archives/2015/6/15/dynos-pricing-ga\"\u003eHeroku\u0026rsquo;s price changes last year\u003c/a\u003e. I think their new\npricing model is completely reasonable, and their \u0026ldquo;free\u0026rdquo; product still\ngenerous, but Heroku \u003cem\u003ecan\u003c/em\u003e get expensive. This low traffic site will cost very\nlittle to host with Amazon.\u003c/p\u003e\n\u003cp\u003eIt seemed like a no-brainer to try it out, and this is how I did it.\u003c/p\u003e",
      "date_published": "2016-01-25T00:00:00+00:00",
      "date_modified": "2016-01-25T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/working-with-vim-colorschemes/",
      "url": "https://elver.me/blog/working-with-vim-colorschemes/","title": "Working with Vim highlight groups",
      "content_html": "\u003cp\u003eHighlight groups are used in Vim to control how the user interface will look.\nFor example, to change the colour of highlighted search items, you would add\nthis line to your \u003ccode\u003e~/.vimrc\u003c/code\u003e or colorscheme in \u003ccode\u003e~/.vim/colors/\u003c/code\u003e.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ehighlight Search ctermfg=0 ctermbg=226\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThis would make highlighted search items show up with a yellow background and\nblack text (when running in a terminal). This allows customisation of\neverything you see on screen. However, given the breadth of customisation\npossible it can be difficult to figure out which groups apply to the particular\nitem you want to change.\u003c/p\u003e\n\u003ch2 id=\"showing-highlight-groups\"\u003eShowing highlight groups\u003c/h2\u003e\n\u003cp\u003eThis function will show what groups are being applied. Add to your \u003ccode\u003e~/.vimrc\u003c/code\u003e,\nplace your cursor over the item in question, and press \u003ccode\u003e\u0026lt;leader\u0026gt;sp\u003c/code\u003e to output\nthe groups.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-viml\" data-lang=\"viml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enmap \u0026lt;leader\u0026gt;sp :call \u0026lt;SID\u0026gt;SynStack()\u0026lt;CR\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003efunction\u003c/span\u003e! \u0026lt;SID\u0026gt;SynStack()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003eif\u003c/span\u003e !exists(\u003cspan style=\"color:#d14\"\u003e\u0026#34;*synstack\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    return\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003eendif\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  echo \u003cspan style=\"color:#000;font-weight:bold\"\u003emap\u003c/span\u003e(synstack(line(\u003cspan style=\"color:#d14\"\u003e\u0026#39;.\u0026#39;\u003c/span\u003e), col(\u003cspan style=\"color:#d14\"\u003e\u0026#39;.\u0026#39;\u003c/span\u003e)), \u003cspan style=\"color:#d14\"\u003e\u0026#39;synIDattr(v:val, \u0026#34;name\u0026#34;)\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eendfunc\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor example, placing my cursor over a comment in a Ruby file gives this output.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e[\u0026#39;rubyMultilineComment\u0026#39;, \u0026#39;rubyComment\u0026#39;]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eYou can now change how Ruby comments look using these groups.\u003c/p\u003e\n\u003cp\u003eThis should greatly help when modifying colorschemes.\u003c/p\u003e\n\u003ch2 id=\"outputting-all-highlight-groups\"\u003eOutputting all highlight groups\u003c/h2\u003e\n\u003cp\u003eIt can also be very useful to see how all highlight groups look.\u003c/p\u003e\n\u003cp\u003eWhilst browsing the highlight help page recently (\u003ccode\u003e:help highlight\u003c/code\u003e) I found\nthat you can output all groups currently active using a script that comes with\nVim. Running \u003ccode\u003e:so $VIMRUNTIME/syntax/hitest.vim\u003c/code\u003e will show something similar to\nbelow.\u003c/p\u003e\n\u003cimg src=\"/images/highlight-groups.png\" /\u003e\n\u003cp\u003eThere are many more lines in the full output, but it should be clear that this\nis very useful when debugging colorschemes.\u003c/p\u003e\n\u003cp\u003eVim colorschemes should now be easier to create and modify.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eHighlight groups are used in Vim to control how the user interface will look.\nFor example, to change the colour of highlighted search items, you would add\nthis line to your \u003ccode\u003e~/.vimrc\u003c/code\u003e or colorscheme in \u003ccode\u003e~/.vim/colors/\u003c/code\u003e.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ehighlight Search ctermfg=0 ctermbg=226\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThis would make highlighted search items show up with a yellow background and\nblack text (when running in a terminal). This allows customisation of\neverything you see on screen. However, given the breadth of customisation\npossible it can be difficult to figure out which groups apply to the particular\nitem you want to change.\u003c/p\u003e",
      "date_published": "2015-05-27T00:00:00+00:00",
      "date_modified": "2015-05-27T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/backups-for-the-truly-paranoid/",
      "url": "https://elver.me/blog/backups-for-the-truly-paranoid/","title": "Backups for the truly paranoid",
      "content_html": "\u003cp\u003eI\u0026rsquo;ve been meaning to sort out a proper backup solution for a long, long time.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve never lost data on my main machine, but I\u0026rsquo;ve been lucky. It does happen, I\nrecently had a file server with disks in \u003ca href=\"http://en.wikipedia.org/wiki/Standard_RAID_levels#RAID_5\"\u003eRAID5\u003c/a\u003e fail. I\u0026rsquo;m most concerned\nabout hardware failure. I\u0026rsquo;m fairly conservative when it comes to deleting\nfiles, so I don\u0026rsquo;t tend to do it by accident. I\u0026rsquo;m sure it will happen one day,\nbut I think I\u0026rsquo;m much more likely to suffer data loss via catastrophic hardware\nfailure than by accidental deletion.\u003c/p\u003e\n\u003ch2 id=\"strategy\"\u003eStrategy\u003c/h2\u003e\n\u003cp\u003eMy general strategy is to have several backups, in different locations, for\ndifferent purposes. To spread the data out. My current setup has a local clone,\na local incremental backup, and an offsite incremental backup.\u003c/p\u003e\n\u003ch3 id=\"local-clone\"\u003eLocal clone\u003c/h3\u003e\n\u003cp\u003eThe idea of having a local clone is to get back up and running as soon as\npossible in the event of hardware failure or loss. I can boot from the clone\nand carry on working or use it to restore to a new computer.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m using \u003ca href=\"http://www.shirt-pocket.com/SuperDuper/\"\u003eSuperDuper!\u003c/a\u003e to create a bootable clone to an external hard drive\nevery night. \u003ca href=\"https://www.bombich.com/\"\u003eCarbon Copy Cloner\u003c/a\u003e is another popular alternative, but I\u0026rsquo;ve\nfound SuperDuper! meets my needs at the moment.\u003c/p\u003e\n\u003cp\u003eI used my clone to restore recently due to a non-catastrophic problem with a\nnew computer, so I\u0026rsquo;m fairly certain that it will work when it\u0026rsquo;s needed.  I will\nboot from it periodically to test.\u003c/p\u003e\n\u003ch3 id=\"local-incremental-backup\"\u003eLocal incremental backup\u003c/h3\u003e\n\u003cp\u003eFor a local incremental backup I\u0026rsquo;m using \u003ca href=\"http://en.wikipedia.org/wiki/Time_Machine_(OS_X)\"\u003eTime Machine\u003c/a\u003e to an external hard\ndrive. This allows me to retrieve files from different time periods.  Time\nMachine backs up on an hourly basis.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s free and stays out of the way most of the time.\u003c/p\u003e\n\u003ch3 id=\"offsite-incremental-backup\"\u003eOffsite incremental backup\u003c/h3\u003e\n\u003cp\u003eFor the offsite backup, I\u0026rsquo;m only uploading user data, not system files. This\nis not a full system backup. I don\u0026rsquo;t intend to ever use this backup except for\nin truly disasterous sitations i.e. my computer and local backup disks are\neither destroyed or lost.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m using \u003ca href=\"https://www.haystacksoftware.com/arq/\"\u003eArq\u003c/a\u003e to do the backing up. Arq has a good reputation and can\nbackup to \u003ca href=\"https://www.google.co.uk/drive/\"\u003eGoogle Drive\u003c/a\u003e, \u003ca href=\"https://www.dropbox.com\"\u003eDropbox\u003c/a\u003e, \u003ca href=\"http://aws.amazon.com/s3/\"\u003eAmazon S3 or Glacier\u003c/a\u003e and\n\u003ca href=\"https://onedrive.live.com\"\u003eMicrosoft OneDrive\u003c/a\u003e. It can also backup to \u003ca href=\"https://cloud.google.com/storage/docs/nearline-storage\"\u003eGoogle Nearline\u003c/a\u003e which is a\nnew competitor to Amazon\u0026rsquo;s offerings. I decided to give Nearline a try.\u003c/p\u003e\n\u003cp\u003eThe initial backup of approximately 200GB took around 5 days. Unfortunately, I\nexperienced some errors before it completed. It did complete in the end, and\nseems fine, but any sort of error message does not inspire confidence.\u003c/p\u003e\n\u003cp\u003eThis backup should only cost a few dollars per month. It does seem to be\nbacking up more data than I expected though, so I will need to monitor this to\nstop it getting too large and increasing the cost.\u003c/p\u003e\n\u003cp\u003eI can see me trying out other services such as \u003ca href=\"https://www.backblaze.com\"\u003eBackblaze\u003c/a\u003e or\n\u003ca href=\"http://www.code42.com/crashplan/\"\u003eCrashplan\u003c/a\u003e in the future.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eThis setup is still a work in progress. There are some parts that I\u0026rsquo;m happy\nwith and others which make me feel uneasy. I can see my offsite backup changing\nin the future due to the flakiness I\u0026rsquo;ve experienced with Arq and Google.\nHowever, I do feel that there is a good level of data redundancy and the\nbackups are spread out enough. I should now hopefully be able to sleep better\nat night.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI\u0026rsquo;ve been meaning to sort out a proper backup solution for a long, long time.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve never lost data on my main machine, but I\u0026rsquo;ve been lucky. It does happen, I\nrecently had a file server with disks in \u003ca href=\"http://en.wikipedia.org/wiki/Standard_RAID_levels#RAID_5\"\u003eRAID5\u003c/a\u003e fail. I\u0026rsquo;m most concerned\nabout hardware failure. I\u0026rsquo;m fairly conservative when it comes to deleting\nfiles, so I don\u0026rsquo;t tend to do it by accident. I\u0026rsquo;m sure it will happen one day,\nbut I think I\u0026rsquo;m much more likely to suffer data loss via catastrophic hardware\nfailure than by accidental deletion.\u003c/p\u003e",
      "date_published": "2015-04-29T00:00:00+00:00",
      "date_modified": "2015-04-29T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/how-to-compile-the-tmk-keyboard-firmware-for-ergodox-on-mac-os-x/",
      "url": "https://elver.me/blog/how-to-compile-the-tmk-keyboard-firmware-for-ergodox-on-mac-os-x/","title": "How to compile the tmk_keyboard firmware for ErgoDox on Mac OS X",
      "content_html": "\u003cp\u003eI received my \u003ca href=\"http://ergodox.org/\"\u003eErgoDox keyboard\u003c/a\u003e from \u003ca href=\"https://www.massdrop.com/buy/ergodox?s=ergodox\"\u003eMassdrop\u003c/a\u003e a month or so ago. After\na false start because of some missing parts, and with the \u003ca href=\"https://twitter.com/richbezz\"\u003ehelp of a friend\u003c/a\u003e\nhandy with a soldering iron, it is now up and running.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve wanted to try the \u003ca href=\"https://normanlayout.info/\"\u003eNorman layout\u003c/a\u003e for a while. This seemed like the\nperfect opportunity. The ErgoDox is so different anyway, changing the layout\nwould should not pose too much of a problem? That\u0026rsquo;s the theory. Unfortunately,\nthe \u003ca href=\"https://github.com/benblazak/ergodox-firmware\"\u003e\u0026ldquo;official\u0026rdquo; ErgoDox firmware\u003c/a\u003e doesn\u0026rsquo;t come with Norman. I didn\u0026rsquo;t want to\nstart hacking at keyboard firmware before even getting it up and running, so I\ndecided to try \u003ca href=\"https://github.com/tmk/tmk_keyboard\"\u003etmk_keyboard\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThis is how I got it working.\u003c/p\u003e\n\u003ch2 id=\"install-dependencies\"\u003eInstall dependencies\u003c/h2\u003e\n\u003cp\u003eWe need to install \u003ca href=\"http://www.nongnu.org/avr-libc/\"\u003eAVR Libc\u003c/a\u003e. This package contains C libraries for AVR\nmicrocontrollers (used by the \u003ca href=\"https://www.pjrc.com/teensy/\"\u003eTeensy\u003c/a\u003e microcontroller in the ErgoDox) and most\nimportantly for us, a version of GCC that will allow us to build the firmware.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eAVR Libc is a Free Software project whose goal is to provide a high quality C\nlibrary for use with GCC on Atmel AVR microcontrollers.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eYou can install the dependencies using \u003ca href=\"http://brew.sh/\"\u003eHomebrew\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ brew tap larsimmisch/avr\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ brew install avr-libc\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"build-the-firmware\"\u003eBuild the firmware\u003c/h2\u003e\n\u003cp\u003eThere are many versions of the \u003ccode\u003etmk_keyboard\u003c/code\u003e firmware available on GitHub. I\u0026rsquo;m\nusing \u003ca href=\"https://github.com/tenderlove/tmk_keyboard\"\u003eTenderlove\u0026rsquo;s fork\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eGet the source code.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit clone git@github.com:tenderlove/tmk_keyboard.git\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eChange to the ErgoDox keyboard firmware directory.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#0086b3\"\u003ecd\u003c/span\u003e tmk_keyboard/keyboard/ergodox\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBuild the firmware.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake clean\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake -f Makefile\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou should now have a file called \u003ccode\u003eergodox_pjrc.hex\u003c/code\u003e in the directory. This is\nwhat you need to load onto the keyboard.\u003c/p\u003e\n\u003ch2 id=\"install-the-firmware\"\u003eInstall the firmware\u003c/h2\u003e\n\u003cp\u003eI used the Teensy Loader app. Follow the \u003ca href=\"http://www.pjrc.com/teensy/loader_mac.html\"\u003einstructions on their website\u003c/a\u003e\nchoosing the \u003ccode\u003eergodox_pjrc.hex\u003c/code\u003e file that we just built.\u003c/p\u003e\n\u003cp\u003eHopefully, that should be it. Good luck fellow ErgoDox owners!\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI received my \u003ca href=\"http://ergodox.org/\"\u003eErgoDox keyboard\u003c/a\u003e from \u003ca href=\"https://www.massdrop.com/buy/ergodox?s=ergodox\"\u003eMassdrop\u003c/a\u003e a month or so ago. After\na false start because of some missing parts, and with the \u003ca href=\"https://twitter.com/richbezz\"\u003ehelp of a friend\u003c/a\u003e\nhandy with a soldering iron, it is now up and running.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve wanted to try the \u003ca href=\"https://normanlayout.info/\"\u003eNorman layout\u003c/a\u003e for a while. This seemed like the\nperfect opportunity. The ErgoDox is so different anyway, changing the layout\nwould should not pose too much of a problem? That\u0026rsquo;s the theory. Unfortunately,\nthe \u003ca href=\"https://github.com/benblazak/ergodox-firmware\"\u003e\u0026ldquo;official\u0026rdquo; ErgoDox firmware\u003c/a\u003e doesn\u0026rsquo;t come with Norman. I didn\u0026rsquo;t want to\nstart hacking at keyboard firmware before even getting it up and running, so I\ndecided to try \u003ca href=\"https://github.com/tmk/tmk_keyboard\"\u003etmk_keyboard\u003c/a\u003e.\u003c/p\u003e",
      "date_published": "2015-03-30T00:00:00+00:00",
      "date_modified": "2015-03-30T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/editing-the-commandline-in-vim/",
      "url": "https://elver.me/blog/editing-the-commandline-in-vim/","title": "Editing the command line in Vim",
      "content_html": "\u003cp\u003eConstructing long commands on the command line can quickly get out of control.\nI use Vim keybindings in ZSH, which helps greatly. They allow you to navigate\nthe shell using familiar shortcuts from Vim. Wouldn\u0026rsquo;t it be neat if you could\nedit those commands directly in Vim though?\u003c/p\u003e\n\u003cp\u003eYou can.\u003c/p\u003e\n\u003cimg src=\"/images/edit-in-vim.gif\" /\u003e\n\u003cp\u003eAdd this to your \u003ccode\u003e~/.zshrc\u003c/code\u003e file and reload your terminal.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eautoload edit-command-line\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ezle -N edit-command-line\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebindkey -M vicmd v edit-command-line\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow when you hit \u003ccode\u003eESC\u003c/code\u003e and then \u003ccode\u003ev\u003c/code\u003e, the current command line will open in Vim.\nSaving and exiting (\u003ccode\u003e:wq\u003c/code\u003e) will return you to the command line with the changes\nyou made.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eConstructing long commands on the command line can quickly get out of control.\nI use Vim keybindings in ZSH, which helps greatly. They allow you to navigate\nthe shell using familiar shortcuts from Vim. Wouldn\u0026rsquo;t it be neat if you could\nedit those commands directly in Vim though?\u003c/p\u003e\n\u003cp\u003eYou can.\u003c/p\u003e\n\u003cimg src=\"/images/edit-in-vim.gif\" /\u003e\n\u003cp\u003eAdd this to your \u003ccode\u003e~/.zshrc\u003c/code\u003e file and reload your terminal.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eautoload edit-command-line\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ezle -N edit-command-line\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebindkey -M vicmd v edit-command-line\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow when you hit \u003ccode\u003eESC\u003c/code\u003e and then \u003ccode\u003ev\u003c/code\u003e, the current command line will open in Vim.\nSaving and exiting (\u003ccode\u003e:wq\u003c/code\u003e) will return you to the command line with the changes\nyou made.\u003c/p\u003e",
      "date_published": "2015-02-02T00:00:00+00:00",
      "date_modified": "2015-02-02T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/two-handy-curl-tips-for-working-with-restful-apis/",
      "url": "https://elver.me/blog/two-handy-curl-tips-for-working-with-restful-apis/","title": "Two handy CURL tips for working with RESTful APIs",
      "content_html": "\u003cp\u003eI was recently working on a REST API for an iPhone app. I used \u003ca href=\"http://curl.haxx.se/\"\u003ecurl\u003c/a\u003e\nalongside automated tests to manually test as I built. curl is a great tool but\nif you need to specify lots of arguments, as I did, it can soon become\nunwieldy.\u003c/p\u003e\n\u003cp\u003eThese two tips helped me out.\u003c/p\u003e\n\u003ch2 id=\"1-read-arguments-from-a-file\"\u003e1) Read arguments from a file\u003c/h2\u003e\n\u003cp\u003eI had many arguments to pass when testing the API including the \u003ccode\u003eAccept\u003c/code\u003e and\n\u003ccode\u003eAuthorisation\u003c/code\u003e headers. With those alone, the argument list is already pretty\nbig.\u003c/p\u003e\n\u003cp\u003eLuckily, the authors of curl have thought of this and have \u003ca href=\"http://curl.haxx.se/docs/manpage.html#-K\"\u003ebuilt-in an option\u003c/a\u003e\nfor them to be read from a file.\u003c/p\u003e\n\u003cp\u003eCreate a file for your arguments and list them inside. Mine looked something\nlike this.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e-H \u0026quot;Accept:application/vnd.vendor-v1+json\u0026quot;\n-H \u0026quot;Authorization:Basic cZHV0Z29vdVhBR1...\u0026quot;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThen tell curl where to find them.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ecurl \u0026lt;url\u0026gt; -K path/to/file\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"2-reading-post-data-from-a-file\"\u003e2) Reading POST data from a file\u003c/h2\u003e\n\u003cp\u003eI needed to POST a lot of JSON data at various API endpoints. As with\narguments, you can also tell curl to \u003ca href=\"http://curl.haxx.se/docs/manpage.html#-d\"\u003eread the data from a file\u003c/a\u003e. This is\nespecially useful as formatting JSON data on the command line is no fun!\u003c/p\u003e\n\u003cp\u003eTo make curl read the data from a file, prefix the filename with an @ symbol.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ecurl \u0026lt;url\u0026gt; –data @filename\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThese tips should hopefully make using curl easier.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI was recently working on a REST API for an iPhone app. I used \u003ca href=\"http://curl.haxx.se/\"\u003ecurl\u003c/a\u003e\nalongside automated tests to manually test as I built. curl is a great tool but\nif you need to specify lots of arguments, as I did, it can soon become\nunwieldy.\u003c/p\u003e\n\u003cp\u003eThese two tips helped me out.\u003c/p\u003e\n\u003ch2 id=\"1-read-arguments-from-a-file\"\u003e1) Read arguments from a file\u003c/h2\u003e\n\u003cp\u003eI had many arguments to pass when testing the API including the \u003ccode\u003eAccept\u003c/code\u003e and\n\u003ccode\u003eAuthorisation\u003c/code\u003e headers. With those alone, the argument list is already pretty\nbig.\u003c/p\u003e",
      "date_published": "2015-01-27T00:00:00+00:00",
      "date_modified": "2015-01-27T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/disable-spell-check-on-form-fields/",
      "url": "https://elver.me/blog/disable-spell-check-on-form-fields/","title": "Disable spell check on form fields",
      "content_html": "\u003cp\u003eTIL that you can turn off spell checking for text input form fields.\u003c/p\u003e\n\u003cp\u003eI know, this sounds like a terrible idea. Similar atrocities such as turning\noff cut and paste have been committed by others and are very annoying indeed.\nHowever, used carefully and in the right context, it\u0026rsquo;s good to know that this\nis possible.\u003c/p\u003e\n\u003cp\u003eMy particular use case was for a form field designed to accept a Foursquare ID.\nThe value entered would only ever be a mixture of numbers and letters and would\n\u003cstrong\u003ealways\u003c/strong\u003e fail a spell check. The red dotted line signals to the user that\nthey have made a mistake, even if their input is valid.\u003c/p\u003e\n\u003cimg src=\"/images/spellcheck_before.png\"\u003e\n\u003cp\u003eThis is one such case where I think turning off spell checking is a good idea.\u003c/p\u003e\n\u003cp\u003eTo do this, you simply add the \u003ccode\u003espellcheck\u003c/code\u003e attribute to your input tag and set\nit to \u003ccode\u003efalse\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#000080\"\u003einput\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003ename\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;foursquare_id\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003etype\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;text\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003espellcheck\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;false\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cimg src=\"/images/spellcheck_after.png\"\u003e\n\u003cp\u003eAnd, there we go, no spell checking on the form field.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eTIL that you can turn off spell checking for text input form fields.\u003c/p\u003e\n\u003cp\u003eI know, this sounds like a terrible idea. Similar atrocities such as turning\noff cut and paste have been committed by others and are very annoying indeed.\nHowever, used carefully and in the right context, it\u0026rsquo;s good to know that this\nis possible.\u003c/p\u003e\n\u003cp\u003eMy particular use case was for a form field designed to accept a Foursquare ID.\nThe value entered would only ever be a mixture of numbers and letters and would\n\u003cstrong\u003ealways\u003c/strong\u003e fail a spell check. The red dotted line signals to the user that\nthey have made a mistake, even if their input is valid.\u003c/p\u003e",
      "date_published": "2014-07-13T00:00:00+00:00",
      "date_modified": "2014-07-13T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/ruby-method-objects/",
      "url": "https://elver.me/blog/ruby-method-objects/","title": "Ruby method objects",
      "content_html": "\u003cp\u003eIf we want to capitalise all the values in an array, we can use \u003ccode\u003e#map\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;apple\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;pear\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;banana\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e].\u003c/span\u003emap { \u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003efruit\u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003e fruit\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eupcase }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;APPLE\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;PEAR\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;BANANA\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAlternatively, a more terse version is available using the \u003ccode\u003eSymbol#to_proc\u003c/code\u003e trick.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;apple\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;pear\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;banana\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e].\u003c/span\u003emap(\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#990073\"\u003e:upcase\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;APPLE\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;PEAR\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;BANANA\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can even achieve the same thing using an explicit \u003ccode\u003eProc\u003c/code\u003e object.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;apple\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;pear\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;banana\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e].\u003c/span\u003emap(\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#008080\"\u003eProc\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003enew { \u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003efruit\u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003e fruit\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eupcase })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;APPLE\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;PEAR\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;BANANA\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"but-i-wanna-call-my-own-method\"\u003eBut, I wanna call my own method!\u003c/h2\u003e\n\u003cp\u003eBut what if you want to call a method of your own creation?\u003c/p\u003e\n\u003cp\u003eIn Ruby, methods are not objects. They are one of the few things in Ruby that\naren\u0026rsquo;t. That\u0026rsquo;s why we have the \u003ccode\u003eObject#method\u003c/code\u003e method.\u003c/p\u003e\n\u003cp\u003eWe need to get an object instance which we can pass to \u003ccode\u003e#map\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#458;font-weight:bold\"\u003eFoo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#458;font-weight:bold\"\u003eself\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003e\u003cspan style=\"color:#900;font-weight:bold\"\u003ebar\u003c/span\u003e(value)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    value\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eupcase\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;apple\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;pear\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;banana\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e].\u003c/span\u003emap(\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#008080\"\u003eFoo\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003emethod(\u003cspan style=\"color:#990073\"\u003e:bar\u003c/span\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;APPLE\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;PEAR\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;BANANA\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe first get an instance of the \u003ccode\u003e.bar\u003c/code\u003e method using \u003ccode\u003eFoo.method\u003c/code\u003e and then the\n\u003ccode\u003eMethod\u003c/code\u003e object is converted to a block using \u003ccode\u003e\u0026amp;\u003c/code\u003e and applied to every item in\nthe array.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eIf we want to capitalise all the values in an array, we can use \u003ccode\u003e#map\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;apple\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;pear\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;banana\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e].\u003c/span\u003emap { \u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003efruit\u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003e fruit\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eupcase }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;APPLE\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;PEAR\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;BANANA\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAlternatively, a more terse version is available using the \u003ccode\u003eSymbol#to_proc\u003c/code\u003e trick.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;apple\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;pear\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;banana\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e].\u003c/span\u003emap(\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#990073\"\u003e:upcase\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;APPLE\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;PEAR\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;BANANA\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can even achieve the same thing using an explicit \u003ccode\u003eProc\u003c/code\u003e object.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;apple\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;pear\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;banana\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e].\u003c/span\u003emap(\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#008080\"\u003eProc\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003enew { \u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003efruit\u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003e fruit\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eupcase })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;APPLE\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;PEAR\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#34;BANANA\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"but-i-wanna-call-my-own-method\"\u003eBut, I wanna call my own method!\u003c/h2\u003e\n\u003cp\u003eBut what if you want to call a method of your own creation?\u003c/p\u003e",
      "date_published": "2014-06-25T00:00:00+00:00",
      "date_modified": "2014-06-25T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/sorting-columnds-of-text-in-vim-using-sort/",
      "url": "https://elver.me/blog/sorting-columnds-of-text-in-vim-using-sort/","title": "Sorting columns of text in Vim using sort",
      "content_html": "\u003cp\u003eI recently wanted to get some stats on some Mongo collections to see a) which\ncollection had the most documents; and b) which collections were the biggest by\ndata size.\u003c/p\u003e\n\u003cp\u003eI copy and pasted the stats from our hosted Mongo provider, MongoHQ, and then\nsorted them in Vim. This is what the text looked like after pasting into Vim\n(with the \u003ccode\u003e:set paste\u003c/code\u003e option set).\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eaffiliates  1038  680 KB\narticle_ratings 699 168 KB\nauthors 30  40 KB\nfs.chunks 3401  633.89 MB\nfs.files  1476  680 KB\nnodes 1432  24.29 MB\nnodes_search  91  2.8 MB\nnodes_tags  272 40 KB\npage_views  107769  16.37 MB\npage_views_map  212 40 KB\nrecommendations 34305 45.1 MB\nrewrite_rules 209 168 KB\nsign_ups  10331 12.52 MB\nsitemaps  1 14.84 MB\nsuppliers 13  8 KB\ntariff_price_check_reports  34  540 KB\ntariff_price_checks 1129  968 KB\ntariffs 5 680 KB\nusers 17  64 KB\nusers_tags  2 8 KB\nversions  18031 156.64 MB\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFirst I sorted the text into \u0026ldquo;proper\u0026rdquo; columns using the \u003ccode\u003ecolumn\u003c/code\u003e utility.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e:%!column -t\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWhich resulted in nice, spaced out, columns.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eaffiliates                  1038    680     KB\narticle_ratings             699     168     KB\nauthors                     30      40      KB\nfs.chunks                   3401    633.89  MB\nfs.files                    1476    680     KB\nnodes                       1432    24.29   MB\nnodes_search                91      2.8     MB\nnodes_tags                  272     40      KB\npage_views                  107769  16.37   MB\npage_views_map              212     40      KB\nrecommendations             34305   45.1    MB\nrewrite_rules               209     168     KB\nsign_ups                    10331   12.52   MB\nsitemaps                    1       14.84   MB\nsuppliers                   13      8       KB\ntariff_price_check_reports  34      540     KB\ntariff_price_checks         1129    968     KB\ntariffs                     5       680     KB\nusers                       17      64      KB\nusers_tags                  2       8       KB\nversions                    18031   156.64  MB\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTo sort the text by the total number of documents in the collection, I did this.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e:%!sort -k2nr\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis sorted by the second column (\u003ccode\u003e-k2\u003c/code\u003e), treats the text as a number (\u003ccode\u003en\u003c/code\u003e) and\nthen sorts in reverse (\u003ccode\u003er\u003c/code\u003e), which results in.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epage_views                  107769  16.37   MB\nrecommendations             34305   45.1    MB\nversions                    18031   156.64  MB\nsign_ups                    10331   12.52   MB\nfs.chunks                   3401    633.89  MB\nfs.files                    1476    680     KB\nnodes                       1432    24.29   MB\ntariff_price_checks         1129    968     KB\naffiliates                  1038    680     KB\narticle_ratings             699     168     KB\nnodes_tags                  272     40      KB\npage_views_map              212     40      KB\nrewrite_rules               209     168     KB\nnodes_search                91      2.8     MB\ntariff_price_check_reports  34      540     KB\nauthors                     30      40      KB\nusers                       17      64      KB\nsuppliers                   13      8       KB\ntariffs                     5       680     KB\nusers_tags                  2       8       KB\nsitemaps                    1       14.84   MB\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThen, I sort by the the 4th column (\u003ccode\u003e-k4\u003c/code\u003e), followed by the 3rd column, but this\ntime we require a few more switches. We ignore leading blank spaces (\u003ccode\u003eb\u003c/code\u003e), and\nthis time we sort using a general numeric sort (\u003ccode\u003eg\u003c/code\u003e).\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e:%!sort -k4 -bk3g\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAnd there we have it, sorted by file size.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esuppliers                   13      8       KB\nusers_tags                  2       8       KB\nauthors                     30      40      KB\nnodes_tags                  272     40      KB\npage_views_map              212     40      KB\nusers                       17      64      KB\narticle_ratings             699     168     KB\nrewrite_rules               209     168     KB\ntariff_price_check_reports  34      540     KB\naffiliates                  1038    680     KB\nfs.files                    1476    680     KB\ntariffs                     5       680     KB\ntariff_price_checks         1129    968     KB\nnodes_search                91      2.8     MB\nsign_ups                    10331   12.52   MB\nsitemaps                    1       14.84   MB\npage_views                  107769  16.37   MB\nnodes                       1432    24.29   MB\nrecommendations             34305   45.1    MB\nversions                    18031   156.64  MB\nfs.chunks                   3401    633.89  MB\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e*nix is cool.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI recently wanted to get some stats on some Mongo collections to see a) which\ncollection had the most documents; and b) which collections were the biggest by\ndata size.\u003c/p\u003e\n\u003cp\u003eI copy and pasted the stats from our hosted Mongo provider, MongoHQ, and then\nsorted them in Vim. This is what the text looked like after pasting into Vim\n(with the \u003ccode\u003e:set paste\u003c/code\u003e option set).\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eaffiliates  1038  680 KB\narticle_ratings 699 168 KB\nauthors 30  40 KB\nfs.chunks 3401  633.89 MB\nfs.files  1476  680 KB\nnodes 1432  24.29 MB\nnodes_search  91  2.8 MB\nnodes_tags  272 40 KB\npage_views  107769  16.37 MB\npage_views_map  212 40 KB\nrecommendations 34305 45.1 MB\nrewrite_rules 209 168 KB\nsign_ups  10331 12.52 MB\nsitemaps  1 14.84 MB\nsuppliers 13  8 KB\ntariff_price_check_reports  34  540 KB\ntariff_price_checks 1129  968 KB\ntariffs 5 680 KB\nusers 17  64 KB\nusers_tags  2 8 KB\nversions  18031 156.64 MB\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFirst I sorted the text into \u0026ldquo;proper\u0026rdquo; columns using the \u003ccode\u003ecolumn\u003c/code\u003e utility.\u003c/p\u003e",
      "date_published": "2014-03-12T00:00:00+00:00",
      "date_modified": "2014-03-12T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/how-i-deployed-middleman-to-heroku/",
      "url": "https://elver.me/blog/how-i-deployed-middleman-to-heroku/","title": "How I deployed Middleman to Heroku",
      "content_html": "\u003cp\u003eI\u0026rsquo;ve recently redesigned this website and it is now built using \u003ca href=\"http://www.middlemanapp.com\"\u003eMiddleman\u003c/a\u003e.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eMiddleman is a static site generator using all the shortcuts and tools in\nmodern web development\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eThis post is about how I deployed the site to \u003ca href=\"http://www.heroku.com\"\u003eHeroku\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"building-the-site\"\u003eBuilding the site\u003c/h2\u003e\n\u003cp\u003eMiddleman is a \u003cstrong\u003estatic\u003c/strong\u003e site generator, as such, we need to figure out how\nto get the site built when we deploy. This saves having to build the site and\ncommit the result before deploying.\u003c/p\u003e\n\u003cp\u003eHeroku will automatically attempt to execute a rake task called \u003ccode\u003eassets:precompile\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThis was originally for the benefit of Rails, but we can take advantage of this\nnow for our own needs.\u003c/p\u003e\n\u003cp\u003eI created a new \u003ccode\u003eRakefile\u003c/code\u003e and added the following.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enamespace \u003cspan style=\"color:#990073\"\u003e:assets\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  task \u003cspan style=\"color:#990073\"\u003e:precompile\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    sh \u003cspan style=\"color:#d14\"\u003e\u0026#39;middleman build\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe task simply shells out to call \u003ccode\u003emiddleman build\u003c/code\u003e which builds the site\nautomatically when the site is pushed to Heroku. Middleman will output all\nfiles to \u003ccode\u003e./build\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"serving-the-site\"\u003eServing the site\u003c/h2\u003e\n\u003cp\u003eThe process of serving a static Middleman site on Heroku is quite straight\nforward once you understand the basics. The site will be running as a Rack\napp, so we\u0026rsquo;ll need a \u003ccode\u003econfig.ru\u003c/code\u003e file. Here is what mine looks like.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#0086b3\"\u003erequire\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#39;rack\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#0086b3\"\u003erequire\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#39;rack/contrib/try_static\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e# Serve files from the build directory\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003euse \u003cspan style=\"color:#008080\"\u003eRack\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e::\u003c/span\u003e\u003cspan style=\"color:#008080\"\u003eTryStatic\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#990073\"\u003eroot\u003c/span\u003e: \u003cspan style=\"color:#d14\"\u003e\u0026#39;build\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#990073\"\u003eurls\u003c/span\u003e: \u003cspan style=\"color:#d14\"\u003e%w[/]\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#990073\"\u003etry\u003c/span\u003e: \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#39;.html\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#39;index.html\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#d14\"\u003e\u0026#39;/index.html\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erun \u003cspan style=\"color:#0086b3\"\u003elambda\u003c/span\u003e{ \u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003eenv\u003cspan style=\"color:#000;font-weight:bold\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  four_oh_four_page \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eFile\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eexpand_path(\u003cspan style=\"color:#d14\"\u003e\u0026#34;../build/404/index.html\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#999\"\u003e__FILE__\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#099\"\u003e404\u003c/span\u003e, { \u003cspan style=\"color:#d14\"\u003e\u0026#39;Content-Type\u0026#39;\u003c/span\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#39;text/html\u0026#39;\u003c/span\u003e}, \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eFile\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e.\u003c/span\u003eread(four_oh_four_page) \u003cspan style=\"color:#000;font-weight:bold\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003eRack::TryStatic\u003c/code\u003e section is how we serve up the static files that\nMiddleman builds when the site is pushed to Heroku. Middleman outputs all\nfiles into \u003ccode\u003e./build\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eIf no page is served from the \u003ccode\u003eRack::Trystatic\u003c/code\u003e app, the 404 page is served\nusing the next \u003ccode\u003erun\u003c/code\u003e section.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUPDATE:\u003c/strong\u003e Make sure to add \u003ccode\u003erack-contrib\u003c/code\u003e to your \u003ccode\u003eGemfile\u003c/code\u003e as pointed out by\n\u003ca href=\"https://twitter.com/fulljames/status/469128779777212417\"\u003e@fulljames\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eI decided to use the \u003ca href=\"http://puma.io\"\u003ePuma\u003c/a\u003e web server to do the \u003cem\u003eactual\u003c/em\u003e web serving of the\nfiles as I had never used it before and wanted to try it out. I added Puma to\nmy \u003ccode\u003eGemfile\u003c/code\u003e and created this \u003ccode\u003eProcfile\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#990073\"\u003eweb\u003c/span\u003e: bundle \u003cspan style=\"color:#0086b3\"\u003eexec\u003c/span\u003e puma \u003cspan style=\"color:#000;font-weight:bold\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#0086b3\"\u003ep\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003e$PORT\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ePuma is working great.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s as simple as that. Once pushed to Heroku, everything just works.\u003c/p\u003e\n\u003ch2 id=\"keeping-the-site-alive\"\u003eKeeping the site alive\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;m using a single free \u003ca href=\"https://devcenter.heroku.com/articles/dynos\"\u003eDyno\u003c/a\u003e to serve the site and it\u0026rsquo;s seriously fast. Granted,\nthis site is not receiving lots of traffic, but it\u0026rsquo;s still very quick.\u003c/p\u003e\n\u003cp\u003eThe only downside of a single Heroku Dyno is that it will idle after a period\nof inactivity, which can happen often unless you get lots of regular traffic.\u003c/p\u003e\n\u003cp\u003eI order to keep the site alive, the sites needs to be requested periodically.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m using \u003ca href=\"http://www.pingdom.com\"\u003ePingdom\u003c/a\u003e for this.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eMiddleman is really easy to work with, especially for a Rails developer, and\nHeroku serves the site very well. I would definitely recommend the combination\nof Middleman and Heroku.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI\u0026rsquo;ve recently redesigned this website and it is now built using \u003ca href=\"http://www.middlemanapp.com\"\u003eMiddleman\u003c/a\u003e.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eMiddleman is a static site generator using all the shortcuts and tools in\nmodern web development\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eThis post is about how I deployed the site to \u003ca href=\"http://www.heroku.com\"\u003eHeroku\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"building-the-site\"\u003eBuilding the site\u003c/h2\u003e\n\u003cp\u003eMiddleman is a \u003cstrong\u003estatic\u003c/strong\u003e site generator, as such, we need to figure out how\nto get the site built when we deploy. This saves having to build the site and\ncommit the result before deploying.\u003c/p\u003e",
      "date_published": "2014-02-17T00:00:00+00:00",
      "date_modified": "2014-02-17T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/the-middleman-build-environment/",
      "url": "https://elver.me/blog/the-middleman-build-environment/","title": "The Middleman build environment",
      "content_html": "\u003cp\u003eRails has long had the concept of environments built-in. That is, the ability\nto set the environment to \u003cstrong\u003edevelopment\u003c/strong\u003e, \u003cstrong\u003eproduction\u003c/strong\u003e or \u003cstrong\u003etest\u003c/strong\u003e, and only\nrun code when in one or more of those environments. For example, the\ndevelopment environment has class caching turned off by default, so that code\nis reloaded on every request, perfect whilst developing. In production, this is\nturned on, for much improved performance.\u003c/p\u003e\n\u003cp\u003eMiddleman has a similar idea, but the environments are \u003cstrong\u003ebuild\u003c/strong\u003e and\n\u003cstrong\u003edevelopment\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eI recently took advantage of this feature, specifically, the build environment.\u003c/p\u003e\n\u003cp\u003eThe build environment is set when the site is being built using \u003ccode\u003emiddleman build\u003c/code\u003e. I used this to only include Google Analytics tracking code when the\nsite is built. This stops local web browsing from affecting my web statistics.\u003c/p\u003e\n\u003cp\u003eIn my \u003ccode\u003elayout.erb\u003c/code\u003e, I\u0026rsquo;ve used the \u003ccode\u003ebuild?\u003c/code\u003e helper to conditionally include the\nrelevant JavaScript code.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-erb\" data-lang=\"erb\"\u003e\u0026lt;% if build? %\u0026gt;\n\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt;\n  var _gaq = _gaq || [];\n  _gaq.push([\u0026#39;_setAccount\u0026#39;, \u0026#39;xxxxxxxxxx\u0026#39;]);\n  _gaq.push([\u0026#39;_trackPageview\u0026#39;]);\n\n  (function() {\n    var ga = document.createElement(\u0026#39;script\u0026#39;); ga.type = \u0026#39;text/javascript\u0026#39;; ga.async = true;\n    ga.src = (\u0026#39;https:\u0026#39; == document.location.protocol ? \u0026#39;https://ssl\u0026#39; : \u0026#39;http://www\u0026#39;) + \u0026#39;.google-analytics.com/ga.js\u0026#39;;\n    var s = document.getElementsByTagName(\u0026#39;script\u0026#39;)[0]; s.parentNode.insertBefore(ga, s);\n  })();\n\u0026lt;/script\u0026gt;\n\n\u0026lt;% end %\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAs you can see, this is very simple, but also very useful for customising\ntemplates at build time.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eRails has long had the concept of environments built-in. That is, the ability\nto set the environment to \u003cstrong\u003edevelopment\u003c/strong\u003e, \u003cstrong\u003eproduction\u003c/strong\u003e or \u003cstrong\u003etest\u003c/strong\u003e, and only\nrun code when in one or more of those environments. For example, the\ndevelopment environment has class caching turned off by default, so that code\nis reloaded on every request, perfect whilst developing. In production, this is\nturned on, for much improved performance.\u003c/p\u003e\n\u003cp\u003eMiddleman has a similar idea, but the environments are \u003cstrong\u003ebuild\u003c/strong\u003e and\n\u003cstrong\u003edevelopment\u003c/strong\u003e.\u003c/p\u003e",
      "date_published": "2014-02-10T00:00:00+00:00",
      "date_modified": "2014-02-10T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/troubleshooting-heroku-deploys-with-empty-git-commits/",
      "url": "https://elver.me/blog/troubleshooting-heroku-deploys-with-empty-git-commits/","title": "Troubleshooting Heroku deploys with empty Git commits",
      "content_html": "\u003cp\u003eWhen troubleshooting Heroku deployments, you need to push to the Heroku remote\nover and over until you\u0026rsquo;ve found the problem. It\u0026rsquo;s necessary to add new Git\ncommits in order to force Heroku to re-compile your \u003ca href=\"https://devcenter.heroku.com/articles/slug-compiler\"\u003eapplication slug\u003c/a\u003e or else\nyou will get the \u003ccode\u003eEverything up-to-date\u003c/code\u003e message back from Git.\u003c/p\u003e\n\u003cp\u003eRather than making trivial changes all the time, you can add empty commits.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003egit commit --allow-empty -m \u0026quot;Force slug recompilation\u0026quot;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eIt still adds unnecessary commits to your repo, but it\u0026rsquo;s cleaner and at least\nyou\u0026rsquo;re not touching any code.\u003c/p\u003e\n\u003ch2 id=\"update\"\u003eUpdate\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"http://simonstarr.com\"\u003eSimon Starr\u003c/a\u003e has mentioned another method to achieve the same thing. Using\nGit with the \u003ccode\u003e--amend\u003c/code\u003e flag.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003egit commit --amend\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eUsually, this is used to add missing files to your previous commit or change\nthe commit message, but it \u003cem\u003ealso\u003c/em\u003e removes the previous commit and creates a new\none, which means that Heroku will update and re-compile without a problem.\u003c/p\u003e\n\u003cp\u003eBe careful though, this is not a good method if you\u0026rsquo;ve already shared your\ncommits with someone else, but it\u0026rsquo;s worth bearing in mind depending on the\nsituation as it saves you adding \u003cem\u003eany\u003c/em\u003e new commits to your repo. Thanks, Simon.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eWhen troubleshooting Heroku deployments, you need to push to the Heroku remote\nover and over until you\u0026rsquo;ve found the problem. It\u0026rsquo;s necessary to add new Git\ncommits in order to force Heroku to re-compile your \u003ca href=\"https://devcenter.heroku.com/articles/slug-compiler\"\u003eapplication slug\u003c/a\u003e or else\nyou will get the \u003ccode\u003eEverything up-to-date\u003c/code\u003e message back from Git.\u003c/p\u003e\n\u003cp\u003eRather than making trivial changes all the time, you can add empty commits.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003egit commit --allow-empty -m \u0026quot;Force slug recompilation\u0026quot;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eIt still adds unnecessary commits to your repo, but it\u0026rsquo;s cleaner and at least\nyou\u0026rsquo;re not touching any code.\u003c/p\u003e",
      "date_published": "2014-02-04T00:00:00+00:00",
      "date_modified": "2014-02-04T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/find-where-your-code-broke-with-git-bisect/",
      "url": "https://elver.me/blog/find-where-your-code-broke-with-git-bisect/","title": "Find where your code broke with git bisect",
      "content_html": "\u003cp\u003eYour code is broken. You know everything was working at a certain point in time\nor that a particular commit was ok, but you\u0026rsquo;re not sure which commit\n\u003cem\u003esince\u003c/em\u003e then has introduced the problem.\u003c/p\u003e\n\u003cp\u003eYou need \u003ccode\u003egit bisect\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFrom the \u003ca href=\"http://git-scm.com/docs/git-bisect\"\u003egit documentation\u003c/a\u003e.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003egit-bisect - Find by binary search the change that introduced a bug\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eUmm, right. Ok.\u003c/p\u003e\n\u003cp\u003eA \u003ccode\u003egit bisect\u003c/code\u003e starts by specifying a \u003cstrong\u003egood\u003c/strong\u003e and a \u003cstrong\u003ebad\u003c/strong\u003e revision. \u003ccode\u003egit\u003c/code\u003e will\nthen checkout each commit by splitting (or bisecting) the range of commits and\nrunning the supplied command until it returns with an exit code of zero.\u003c/p\u003e\n\u003cp\u003eAn exit code of zero means the command was successful.\u003c/p\u003e\n\u003cp\u003eThe command can be anything that \u003cstrong\u003ewill\u003c/strong\u003e return an exit code, which includes\nmost *nix binaries or scripts.\u003c/p\u003e\n\u003cp\u003eIn this example, we\u0026rsquo;re using \u003ca href=\"http://cukes.info\"\u003eCucumber\u003c/a\u003e as the command.\u003c/p\u003e\n\u003ch2 id=\"a-working-example-using-cucumber\"\u003eA working example using Cucumber\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egit bisect start\ngit bisect good \u0026lt;commit-where-stuff-is-working\u0026gt;\ngit bisect bad \u0026lt;commit-where-stuff-is-not-working\u0026gt;\ngit bisect run bundle exec cucumber features/a-feature.feature\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIf the tests fail, Cucumber will return a non-zero exit code and \u003ccode\u003egit\u003c/code\u003e will\nkeep trying other commits until it finds one that is successful. We will then\nknow when the code was \u0026ldquo;good\u0026rdquo;, and more importantly, the commit where it became\n\u0026ldquo;bad\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eWhen finished, reset your branch to the previous state before starting the \u003ccode\u003egit bisect\u003c/code\u003e.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003egit bisect reset\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAs you\u0026rsquo;d expect with \u003ccode\u003egit\u003c/code\u003e, it \u003ca href=\"http://git-scm.com/docs/git-bisect\"\u003ecan do \u003cem\u003ea lot\u003c/em\u003e more than this\u003c/a\u003e, but this is\nthe way in which I normally use it.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eYour code is broken. You know everything was working at a certain point in time\nor that a particular commit was ok, but you\u0026rsquo;re not sure which commit\n\u003cem\u003esince\u003c/em\u003e then has introduced the problem.\u003c/p\u003e\n\u003cp\u003eYou need \u003ccode\u003egit bisect\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFrom the \u003ca href=\"http://git-scm.com/docs/git-bisect\"\u003egit documentation\u003c/a\u003e.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003egit-bisect - Find by binary search the change that introduced a bug\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eUmm, right. Ok.\u003c/p\u003e\n\u003cp\u003eA \u003ccode\u003egit bisect\u003c/code\u003e starts by specifying a \u003cstrong\u003egood\u003c/strong\u003e and a \u003cstrong\u003ebad\u003c/strong\u003e revision. \u003ccode\u003egit\u003c/code\u003e will\nthen checkout each commit by splitting (or bisecting) the range of commits and\nrunning the supplied command until it returns with an exit code of zero.\u003c/p\u003e",
      "date_published": "2014-01-31T00:00:00+00:00",
      "date_modified": "2014-01-31T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/its-the-little-things/",
      "url": "https://elver.me/blog/its-the-little-things/","title": "It's the little things",
      "content_html": "\u003cp\u003eI\u0026rsquo;ve been thinking about the \u003ca href=\"http://littlebigdetails.com/\"\u003elittle things\u003c/a\u003e that can be changed to make a\nweb page easier to use. Seemingly small changes can add up to have a big impact\non the way people will interact with the page.\u003c/p\u003e\n\u003cp\u003eOn one particular site, I have a pretty standard widget used to select the\namount of products to show per page. I\u0026rsquo;m sure you\u0026rsquo;ve seen something similar to\nthat of below on many other websites.\u003c/p\u003e\n\u003cimg src=\"/images/dropdown.gif\" alt=\"Example of a dropdown list used to select the amount of products to show on the page\" /\u003e\n\u003cp\u003eThe way this is currently implemented, it takes \u003cstrong\u003ethree\u003c/strong\u003e clicks to complete\nthe intended operation. This could be reduced to \u003cstrong\u003etwo\u003c/strong\u003e clicks by adding some\nJavaScript to trigger the page change (and removing the button), but there is a\nproblem with this method: The options at \u003cstrong\u003enot all visible\u003c/strong\u003e at once. You have\nto click to see what the options are.\u003c/p\u003e\n\u003ch2 id=\"rethinking-the-implementation\"\u003eRethinking the implementation\u003c/h2\u003e\n\u003cp\u003eI decided to change the way this was implemented, moving to a horizontal list of links.\u003c/p\u003e\n\u003cimg src=\"/images/list.gif\" alt=\"Example of a list used to select the amount of products to show on the page\" /\u003e\n\u003cp\u003eThe advantages of this approach are:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eYou can see all options at once. Please, \u003ca href=\"http://www.amazon.co.uk/Dont-Make-Me-Think-Usability/dp/0321344758\"\u003edon\u0026rsquo;t make me think\u003c/a\u003e.\u003c/li\u003e\n\u003cli\u003eIt\u0026rsquo;s only a \u003cstrong\u003esingle\u003c/strong\u003e click to select an option.\u003c/li\u003e\n\u003cli\u003eIt\u0026rsquo;s better for accessibility because you don\u0026rsquo;t have to fiddle with a\ndropdown (and who wants to?).\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eI think this is a fairly good example of how changing small things, perhaps\neven those that seem inconsequential, can contribute to improving the overall\nexperience of the page, and delight the user.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI\u0026rsquo;ve been thinking about the \u003ca href=\"http://littlebigdetails.com/\"\u003elittle things\u003c/a\u003e that can be changed to make a\nweb page easier to use. Seemingly small changes can add up to have a big impact\non the way people will interact with the page.\u003c/p\u003e\n\u003cp\u003eOn one particular site, I have a pretty standard widget used to select the\namount of products to show per page. I\u0026rsquo;m sure you\u0026rsquo;ve seen something similar to\nthat of below on many other websites.\u003c/p\u003e",
      "date_published": "2011-07-30T00:00:00+00:00",
      "date_modified": "2011-07-30T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/rails-footnotes-problem/",
      "url": "https://elver.me/blog/rails-footnotes-problem/","title": "Rails Footnotes problem",
      "content_html": "\u003cp\u003eI recently had a head-scratching time trying to figure out why code that\nintegrates with a 3rd party payment processor had suddenly stopped working. The\ncode in question simply returns a text only response to the 3rd party, who then\nread, parse and act on it. Within my Rails controller:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erender \u003cspan style=\"color:#990073\"\u003e:text\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;Status=OK,Detail=Some details here\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI found the culprit by going through my git log to see what had changed\nrecently. Turns out it was the recent addition of Rails Footnotes causing the\nproblem. \u003ca href=\"http://rubydoc.info/gems/rails-footnotes/3.7.4/frames\"\u003eRails Footnotes\u003c/a\u003e is an awesome gem that adds various information\nabout your app to the bottom of every page including clickable links to\ncontrollers, views and partials. Clicking these links opens the file in your\ntext editor of choice.\u003c/p\u003e\n\u003cp\u003eRails Footnotes was appending its debugging HTML (as designed) to my text-only\nresponse, breaking the payment processors parsing. After an \u003ca href=\"http://railstips.org/blog/archives/2010/10/14/stop-googling/\"\u003einspection of the\nsource\u003c/a\u003e I noticed that Footnotes are only output if the content type\nincludes \u0026ldquo;html\u0026rdquo;, such as \u0026ldquo;text/html\u0026rdquo;.\u003c/p\u003e\n\u003ch2 id=\"the-fix\"\u003eThe fix\u003c/h2\u003e\n\u003cp\u003eIn my case the whole controller could (and probably should) be returned as\n\u0026ldquo;text/plain\u0026rdquo;. I hooked up a \u003ccode\u003ebefore_filter\u003c/code\u003e to add the \u003ccode\u003eContent-Type\u003c/code\u003e header to\nthe response and that fixed it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#458;font-weight:bold\"\u003eNotificationController\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eApplicationController\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#998;font-style:italic\"\u003e# Set the content type to text/plain so footnotes don\u0026#39;t show\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  before_filter \u003cspan style=\"color:#990073\"\u003e:set_content_type\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#900;font-weight:bold\"\u003eset_content_type\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    headers\u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#39;Content-Type\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#39;text/plain\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor versions of Rails Footnotes greater than 3.7.0 (which is Rails 3 only) you\ncan actually configure if Footnotes are added to the page using an initializer\n(or similar). Unfortunately, I\u0026rsquo;m still using Rails 2 on this project so I\ncurrently can\u0026rsquo;t take advantage of that.\u003c/p\u003e\n\u003ch2 id=\"in-conclusion\"\u003eIn conclusion\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"http://rubydoc.info/gems/rails-footnotes/3.7.4/frames\"\u003eRails Footnotes\u003c/a\u003e is a must-have, you really should check it out.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI recently had a head-scratching time trying to figure out why code that\nintegrates with a 3rd party payment processor had suddenly stopped working. The\ncode in question simply returns a text only response to the 3rd party, who then\nread, parse and act on it. Within my Rails controller:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erender \u003cspan style=\"color:#990073\"\u003e:text\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;Status=OK,Detail=Some details here\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI found the culprit by going through my git log to see what had changed\nrecently. Turns out it was the recent addition of Rails Footnotes causing the\nproblem. \u003ca href=\"http://rubydoc.info/gems/rails-footnotes/3.7.4/frames\"\u003eRails Footnotes\u003c/a\u003e is an awesome gem that adds various information\nabout your app to the bottom of every page including clickable links to\ncontrollers, views and partials. Clicking these links opens the file in your\ntext editor of choice.\u003c/p\u003e",
      "date_published": "2011-07-24T00:00:00+00:00",
      "date_modified": "2011-07-24T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/automatically-attaching-to-a-tmux-session-via-ssh/",
      "url": "https://elver.me/blog/automatically-attaching-to-a-tmux-session-via-ssh/","title": "Automatically attaching to a tmux session via SSH",
      "content_html": "\u003cp\u003eI\u0026rsquo;ve been using \u003ca href=\"http://tmux.sourceforge.net/\"\u003etmux\u003c/a\u003e as a\n\u003ca href=\"http://www.gnu.org/software/screen/\"\u003escreen\u003c/a\u003e replacement for a while now. I\nfind it easier to use and configure than screen. I tend to leave a tmux session\nrunning on servers that I administer so that everything is just as it was when\nI last connected. It\u0026rsquo;s very handy.\u003c/p\u003e\n\u003cp\u003eTo make this even more convenient, I wanted to be able to automatically attach\nto a running tmux session when connecting to servers using\n\u003ca href=\"http://en.wikipedia.org/wiki/Secure_Shell\"\u003eSSH\u003c/a\u003e. The SSH client already comes\nwith the ability to run a command when connecting. It works like this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003essh \u0026lt;hostname\u0026gt; \u0026lt;command\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUnfortunately, this didn\u0026rsquo;t work when I tried attaching to a tmux session.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003essh \u0026lt;hostname\u0026gt; tmux a\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enot a terminal\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAfter a bit of Googling, it turns out that you need to supply the \u003ccode\u003e-t\u003c/code\u003e option\nto the \u003ccode\u003essh\u003c/code\u003e command. The ssh man page describes the option as such:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e-t Force pseudo-tty allocation. This can be used to execute\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   arbitrary screen-based programs on a remote machine, \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   which can be very useful, e.g., when implementing menu\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   services.\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf we do that, we\u0026rsquo;re in business:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003essh \u0026lt;hostname\u0026gt; -t tmux a\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo make the command even shorter, I\u0026rsquo;ve been adding bash aliases to my\n\u003ccode\u003e~/.bash_profile\u003c/code\u003e for each server I connect to, like so:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#0086b3\"\u003ealias\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eservername\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;ssh servername -t tmux a\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow, I can just type \u003ccode\u003eservername\u003c/code\u003e and get a SSH connection to servername with\ntmux automatically attached. Issuing a \u003ccode\u003e\u0026lt;ctrl\u0026gt; + b + d\u003c/code\u003e will detach the tmux\nsession and disconnect the SSH connection.\u003c/p\u003e\n\u003cp\u003eCommand line magic!\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI\u0026rsquo;ve been using \u003ca href=\"http://tmux.sourceforge.net/\"\u003etmux\u003c/a\u003e as a\n\u003ca href=\"http://www.gnu.org/software/screen/\"\u003escreen\u003c/a\u003e replacement for a while now. I\nfind it easier to use and configure than screen. I tend to leave a tmux session\nrunning on servers that I administer so that everything is just as it was when\nI last connected. It\u0026rsquo;s very handy.\u003c/p\u003e\n\u003cp\u003eTo make this even more convenient, I wanted to be able to automatically attach\nto a running tmux session when connecting to servers using\n\u003ca href=\"http://en.wikipedia.org/wiki/Secure_Shell\"\u003eSSH\u003c/a\u003e. The SSH client already comes\nwith the ability to run a command when connecting. It works like this:\u003c/p\u003e",
      "date_published": "2010-11-27T00:00:00+00:00",
      "date_modified": "2010-11-27T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/how-to-upgrade-phusion-passenger-for-nginx/",
      "url": "https://elver.me/blog/how-to-upgrade-phusion-passenger-for-nginx/","title": "How to upgrade Phusion Passenger for nginx",
      "content_html": "\u003cp\u003eI keep forgetting the procedure to upgrade Passenger and nginx, so thought I\u0026rsquo;d\nnote them down here in case anyone finds it useful. These instructions assume\nthat:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eYou already have \u003ca href=\"http://www.modrails.com/\"\u003ePhusion Passenger\u003c/a\u003e installed and\nconfigured for \u003ca href=\"http://nginx.net/\"\u003enginx\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eYou\u0026rsquo;re using \u003ca href=\"http://www.rubyenterpriseedition.com/\"\u003eRuby Enterprise Edition\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"upgrade\"\u003eUpgrade\u003c/h2\u003e\n\u003cp\u003eDownload the latest nginx source and uncompress to a temporary directory.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#0086b3\"\u003ecd\u003c/span\u003e ~/sources\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewget http://sysoev.ru/nginx/nginx-0.7.64.tar.gz\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etar xzvf nginx-0.7.64.tar.gz\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eInstall the latest Passenger gem\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo /opt/ruby-enterprise-1.8.6-20090520/bin/gem install passenger\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRun the Passenger installer\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo /opt/ruby-enterprise-1.8.6-20090520/bin/passenger-install-nginx-module\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFollow the onscreen instructions, choose option 2 to use your own sources.\nSpecify the location of the nginx sources when prompted.\u003c/p\u003e\n\u003cp\u003eIf you want to use any other optional nginx modules, enable them when prompted.\nI want to use the SSL and Status modules, so I enable them like this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--with-http_ssl_module --with-http_stub_status_module\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe full configure line will look something like this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e./configure --prefix\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#39;/opt/nginx\u0026#39;\u003c/span\u003e --with-pcre\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#39;/tmp/pcre-7.8\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--add-module\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#39;/opt/ruby-enterprise-1.8.6-20090520/lib/ruby/gems/1.8/gems/passenger-2.2.5/ext/nginx\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--with-http_ssl_module --with-http_stub_status_module\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAfter the installer has finished, the new version will be installed in\n\u003ccode\u003e/opt/nginx\u003c/code\u003e and the old version will be renamed to \u003ccode\u003enginx.old\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"configure\"\u003eConfigure\u003c/h2\u003e\n\u003cp\u003eUpdate the \u003ccode\u003enginx.conf\u003c/code\u003e with the correct paths, you should only need to change\nthe path to the \u003ccode\u003epassenger_root\u003c/code\u003e directive.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehttp \u003cspan style=\"color:#000;font-weight:bold\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    passenger_root /opt/ruby-enterprise-1.8.6-20090520/lib/ruby/gems/1.8/gems/passenger-2.2.5;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"restart\"\u003eRestart\u003c/h2\u003e\n\u003cp\u003eI like to do a config file syntax check before I restart nginx, just in case\nof typos and the like.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo /opt/nginx/sbin/nginx -t -c /opt/nginx/conf/nginx.conf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf the check is successful, restart nginx.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo /etc/init.d/nginx restart\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou should now be running the latest version of Passenger and nginx.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eI keep forgetting the procedure to upgrade Passenger and nginx, so thought I\u0026rsquo;d\nnote them down here in case anyone finds it useful. These instructions assume\nthat:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eYou already have \u003ca href=\"http://www.modrails.com/\"\u003ePhusion Passenger\u003c/a\u003e installed and\nconfigured for \u003ca href=\"http://nginx.net/\"\u003enginx\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eYou\u0026rsquo;re using \u003ca href=\"http://www.rubyenterpriseedition.com/\"\u003eRuby Enterprise Edition\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"upgrade\"\u003eUpgrade\u003c/h2\u003e\n\u003cp\u003eDownload the latest nginx source and uncompress to a temporary directory.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#0086b3\"\u003ecd\u003c/span\u003e ~/sources\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewget http://sysoev.ru/nginx/nginx-0.7.64.tar.gz\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etar xzvf nginx-0.7.64.tar.gz\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eInstall the latest Passenger gem\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo /opt/ruby-enterprise-1.8.6-20090520/bin/gem install passenger\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRun the Passenger installer\u003c/p\u003e",
      "date_published": "2009-11-27T00:00:00+00:00",
      "date_modified": "2009-11-27T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/jquery-event-delegation/",
      "url": "https://elver.me/blog/jquery-event-delegation/","title": "jQuery Event Delegation",
      "content_html": "\u003cp\u003eRecently, I\u0026rsquo;ve been working on adding some extra features to a shopping cart\npage using \u003ca href=\"http://jquery.com/\"\u003ejQuery\u003c/a\u003e. The idea is that you select your\ncountry from a dropdown and an AJAX(Asynchronous JavaScript and XML) request is\nperformed, which updates a list of radio buttons on the page. If you select one\nof these radio buttons, it triggers another AJAX(Asynchronous JavaScript and\nXML) request, which updates the postage prices for the given items in the\nbasket.\u003c/p\u003e\n\u003cp\u003eEach of the radio buttons has an event like this bound to it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$(\u003cspan style=\"color:#d14\"\u003e\u0026#39;#options input\u0026#39;\u003c/span\u003e).click(\u003cspan style=\"color:#000;font-weight:bold\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#998;font-style:italic\"\u003e// lookup postage prices via ajax\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis triggers the AJAX(Asynchronous JavaScript and XML) request which looks up\nthe postage prices from the database. It works fine with the default set of\nradio buttons that are present on the page when it loads. The problem comes\nwhen new radio buttons are added to the page via AJAX(Asynchronous JavaScript\nand XML). When a new radio button is added to the list, it doesn\u0026rsquo;t\nautomatically have a click event (like the one above) bound to it like the\nexisting items do. The events are not automatically carried across to the new\nitem you\u0026rsquo;ve added.\u003c/p\u003e\n\u003cp\u003eWe can get around this problem by re-binding the events each time a new radio\nbutton is added to the page. But this can be messy and there is a much cleaner\nway of solving the problem.\u003c/p\u003e\n\u003ch2 id=\"event-delegation-to-the-rescue\"\u003eEvent delegation to the rescue\u003c/h2\u003e\n\u003cp\u003eThis is where event delegation comes in. Event delegation takes advantage of\nthe fact that browsers \u0026ldquo;bubble\u0026rdquo; events up the DOM(Document Object Model) when\nthey are triggered on a page. For example, given the HTML(HyperText Markup\nLanguage) below, when clicking the radio button the browser will generate a\nclick event which will \u0026ldquo;bubble\u0026rdquo; upwards so that first the \u003ccode\u003e\u0026lt;input\u0026gt;\u003c/code\u003e element\nreceives the event, followed by the \u003ccode\u003e\u0026lt;p\u0026gt;\u003c/code\u003e, then the \u003ccode\u003e\u0026lt;div\u0026gt;\u003c/code\u003e, and so on.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#000080\"\u003ediv\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eid\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;options\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#000080\"\u003ep\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#000080\"\u003einput\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003etype\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;radio\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003evalue\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;1\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eid\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;postage_method_1\u0026#34;\u003c/span\u003e /\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;/\u003cspan style=\"color:#000080\"\u003ep\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#000080\"\u003ep\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#000080\"\u003einput\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003etype\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;radio\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003evalue\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;2\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eid\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;postage_method_2\u0026#34;\u003c/span\u003e /\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;/\u003cspan style=\"color:#000080\"\u003ep\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#000080\"\u003ediv\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can take advantage of this \u0026ldquo;event bubbling\u0026rdquo;. Instead of binding the event to\neach individual \u003ccode\u003einput\u003c/code\u003e, we\u0026rsquo;ll bind it to a parent element instead and let the\nclick events from the inputs bubble up to the parent element. In this case, the\ndiv called options.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$(\u003cspan style=\"color:#d14\"\u003e\u0026#39;#options\u0026#39;\u003c/span\u003e).click(\u003cspan style=\"color:#000;font-weight:bold\"\u003efunction\u003c/span\u003e(event) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#000;font-weight:bold\"\u003eif\u003c/span\u003e ($(event.target).is(\u003cspan style=\"color:#d14\"\u003e\u0026#39;input\u0026#39;\u003c/span\u003e)){\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#998;font-style:italic\"\u003e// do ajax request\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow, each time we add an additional input to the page, it will automatically\nrespond to the same event as the rest of the inputs.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s worth noting that this event will get triggered whenever any element under\nthe \u003ccode\u003e#options\u003c/code\u003e div is clicked, so inside the event we check that the target of\nthe event was an \u003ccode\u003einput\u003c/code\u003e element. This will restrict the code inside the event\nso that it only executes when it\u0026rsquo;s an \u003ccode\u003einput\u003c/code\u003e that\u0026rsquo;s been clicked.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eEvent delegation greatly improves the readability of the code. It sounds scary,\nbut really isn\u0026rsquo;t. It also has the added benefit of speeding up the processing\nof the page because you\u0026rsquo;re not spending time looping through many DOM elements\n(which takes time) in order to add your event handlers.\u003c/p\u003e\n\u003cp\u003eCheck out the references below for a much better explanation of how event\ndelegation and binding of event handlers work.\u003c/p\u003e\n\u003ch2 id=\"references\"\u003eReferences\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"http://icant.co.uk/sandbox/eventdelegation/\"\u003eEvent Delegation versus Event Handling\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://lab.distilldesign.com/event-delegation/\"\u003eEvent Delegation with jQuery\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://www.danwebb.net/2008/2/8/event-delegation-made-easy-in-jquery\"\u003eEvent Delegation Made Easy\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eLearning jQuery - Working with Events, \u003ca href=\"http://www.learningjquery.com/2008/03/working-with-events-part-1\"\u003epart 1\u003c/a\u003e, and \u003ca href=\"http://www.learningjquery.com/2008/05/working-with-events-part-2\"\u003epart 2\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n",
      "summary": "\u003cp\u003eRecently, I\u0026rsquo;ve been working on adding some extra features to a shopping cart\npage using \u003ca href=\"http://jquery.com/\"\u003ejQuery\u003c/a\u003e. The idea is that you select your\ncountry from a dropdown and an AJAX(Asynchronous JavaScript and XML) request is\nperformed, which updates a list of radio buttons on the page. If you select one\nof these radio buttons, it triggers another AJAX(Asynchronous JavaScript and\nXML) request, which updates the postage prices for the given items in the\nbasket.\u003c/p\u003e",
      "date_published": "2009-06-03T00:00:00+00:00",
      "date_modified": "2009-06-03T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/htop-a-prettier-top/",
      "url": "https://elver.me/blog/htop-a-prettier-top/","title": "htop - a prettier top",
      "content_html": "\u003cp\u003eYou use \u003ca href=\"http://en.wikipedia.org/wiki/Top_%28Unix%29\"\u003etop\u003c/a\u003e right? No? Well,\nthat\u0026rsquo;s probably because top is not the friendliest program in the world. Yes,\nit gets the job done, but it could be prettier.\u003c/p\u003e\n\u003cp\u003eAs I was using top more and more for monitoring my VPS, I looked for something\na bit nicer.\u003c/p\u003e\n\u003cp\u003eThis is where \u003ca href=\"http://htop.sourceforge.net/\"\u003ehtop\u003c/a\u003e comes in. It\u0026rsquo;s top, but with\ncolours, progress bars, and a process tree view. Much better. Try it, you\nmight like it.\u003c/p\u003e\n\u003cimg src=\"/images/htop.jpg\" /\u003e\n",
      "summary": "\u003cp\u003eYou use \u003ca href=\"http://en.wikipedia.org/wiki/Top_%28Unix%29\"\u003etop\u003c/a\u003e right? No? Well,\nthat\u0026rsquo;s probably because top is not the friendliest program in the world. Yes,\nit gets the job done, but it could be prettier.\u003c/p\u003e\n\u003cp\u003eAs I was using top more and more for monitoring my VPS, I looked for something\na bit nicer.\u003c/p\u003e\n\u003cp\u003eThis is where \u003ca href=\"http://htop.sourceforge.net/\"\u003ehtop\u003c/a\u003e comes in. It\u0026rsquo;s top, but with\ncolours, progress bars, and a process tree view. Much better. Try it, you\nmight like it.\u003c/p\u003e",
      "date_published": "2008-12-31T00:00:00+00:00",
      "date_modified": "2008-12-31T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/changing-recent-commits-with-git/",
      "url": "https://elver.me/blog/changing-recent-commits-with-git/","title": "Changing Recent Commits with Git",
      "content_html": "\u003cp\u003eHave you ever committed to your repo and realised you\u0026rsquo;ve done something a bit\nsilly? I just did that. I moved to a new machine and forgot to setup my\n\u003ccode\u003e.gitconfig\u003c/code\u003e with the correct username and email, so when I committed, the\ncommit had the wrong user against it. Not a big problem, but it just looks a\nbit\u0026hellip;well, untidy.\u003c/p\u003e\n\u003cp\u003eFear not. Git is all-powerful and allows you to change your mistake even\nthough you\u0026rsquo;ve already committed it. Thanks to Tekkub on the\n\u003ca href=\"http://groups.google.com/group/github\"\u003eGitHub Google Group\u003c/a\u003e for\nthe nudge in the right direction.\u003c/p\u003e\n\u003ch2 id=\"how-does-i-dos-it\"\u003eHow does I dos it?\u003c/h2\u003e\n\u003cp\u003eLike this.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit reset --soft HEAD^\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will reset your working copy to the state before you committed and remove\nthe commit so you can make changes to your working copy (if needed) and\nre-commit.\u003c/p\u003e\n\u003cp\u003eRe-commit the changes.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit commit -m \u003cspan style=\"color:#d14\"\u003e\u0026#34;Changed something\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you\u0026rsquo;ve already pushed to \u0026ldquo;GitHub\u0026rdquo;:http://www.github.com or another external\nrepo, you can re-push, but you\u0026rsquo;ll need to add \u003ccode\u003e--force\u003c/code\u003e because we have removed\na commit locally and Git doesn\u0026rsquo;t like that.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit push origin --force\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI\u0026rsquo;m not sure whether this would be a good idea if someone else has cloned your\nrepo, but I\u0026rsquo;m ok because I am the only person who pushes to this one.\u003c/p\u003e\n\u003cp\u003eGit is great, but powerful, so be careful. I\u0026rsquo;m sure it would be easy to\ncompletely trash your repo with the wrong series of commands.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eHave you ever committed to your repo and realised you\u0026rsquo;ve done something a bit\nsilly? I just did that. I moved to a new machine and forgot to setup my\n\u003ccode\u003e.gitconfig\u003c/code\u003e with the correct username and email, so when I committed, the\ncommit had the wrong user against it. Not a big problem, but it just looks a\nbit\u0026hellip;well, untidy.\u003c/p\u003e\n\u003cp\u003eFear not. Git is all-powerful and allows you to change your mistake even\nthough you\u0026rsquo;ve already committed it. Thanks to Tekkub on the\n\u003ca href=\"http://groups.google.com/group/github\"\u003eGitHub Google Group\u003c/a\u003e for\nthe nudge in the right direction.\u003c/p\u003e",
      "date_published": "2008-12-16T00:00:00+00:00",
      "date_modified": "2008-12-16T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/google-analytics-ecommerce-tracking/",
      "url": "https://elver.me/blog/google-analytics-ecommerce-tracking/","title": "Google Analytics Ecommerce Tracking",
      "content_html": "\u003cp\u003eSo you\u0026rsquo;ve installed \u003ca href=\"http://www.google.com/analytics\"\u003eGoogle Analytics\u003c/a\u003e, the\nsuperb free web analytics software and it\u0026rsquo;s telling you all sorts of things\nabout your site, most of which you don\u0026rsquo;t even understand. You\u0026rsquo;re learning what\na \u003ca href=\"http://en.wikipedia.org/wiki/Bounce_Rate\"\u003eBounce Rate\u003c/a\u003e is and realising how\npowerful this thing really is.\u003c/p\u003e\n\u003cp\u003eWell, it gets even better. I\u0026rsquo;ve recently discovered its\u0026rsquo; Ecommerce tracking\nfeature.  It allows you to capture Ecommerce related statistics alongside your\nnormal web related statistics. Once you have it correctly setup for your site\nyou can see how much money you\u0026rsquo;ve taken in sales, how many transactions have\ntaken place, how many products have been purchased, and your Ecommerce\nconversion rate. Very nice.\u003c/p\u003e\n\u003ch2 id=\"make-it-so\"\u003eMake it so\u003c/h2\u003e\n\u003cp\u003eIn order to activate Ecommerce tracking, you need to firstly turn on the\nfeature within Google Analytics, and secondly add some extra JavaScript code to\nyour receipt or thanks page.\u003c/p\u003e\n\u003cp\u003eTo turn on the feature, go to the Profile Settings for your site, click Edit\n(top right) and under E-Commerce Website, select \u0026ldquo;Yes, an E-Commerce Site\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s the extra code you need to add to the receipt page straight from\n\u003ca href=\"http://www.google.com/support/googleanalytics/bin/answer.py?hl=en\u0026amp;answer=55528\"\u003eGoogle\u0026rsquo;s help page\u003c/a\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#000080\"\u003escript\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003etype\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;text/javascript\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003evar\u003c/span\u003e gaJsHost \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e ((\u003cspan style=\"color:#d14\"\u003e\u0026#34;https:\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#0086b3\"\u003edocument\u003c/span\u003e.location.protocol) \u003cspan style=\"color:#000;font-weight:bold\"\u003e?\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;https://ssl.\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;http://www.\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#0086b3\"\u003edocument\u003c/span\u003e.write(unescape(\u003cspan style=\"color:#d14\"\u003e\u0026#34;%3Cscript src=\u0026#39;\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e+\u003c/span\u003e gaJsHost \u003cspan style=\"color:#000;font-weight:bold\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;google-analytics.com/ga.js\u0026#39; type=\u0026#39;text/javascript\u0026#39;%3E%3C/script%3E\u0026#34;\u003c/span\u003e));\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#000080\"\u003escript\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#000080\"\u003escript\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003etype\u003c/span\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#d14\"\u003e\u0026#34;text/javascript\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003evar\u003c/span\u003e pageTracker \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u003c/span\u003e _gat._getTracker(\u003cspan style=\"color:#d14\"\u003e\u0026#34;UA-XXXXX-1\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epageTracker._trackPageview();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epageTracker._addTrans(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#d14\"\u003e\u0026#34;1234\u0026#34;\u003c/span\u003e,                                     \u003cspan style=\"color:#998;font-style:italic\"\u003e// Order ID\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#d14\"\u003e\u0026#34;Mountain View\u0026#34;\u003c/span\u003e,                            \u003cspan style=\"color:#998;font-style:italic\"\u003e// Affiliation\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#d14\"\u003e\u0026#34;11.99\u0026#34;\u003c/span\u003e,                                    \u003cspan style=\"color:#998;font-style:italic\"\u003e// Total\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#d14\"\u003e\u0026#34;1.29\u0026#34;\u003c/span\u003e,                                     \u003cspan style=\"color:#998;font-style:italic\"\u003e// Tax\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#d14\"\u003e\u0026#34;5\u0026#34;\u003c/span\u003e,                                        \u003cspan style=\"color:#998;font-style:italic\"\u003e// Shipping\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#d14\"\u003e\u0026#34;San Jose\u0026#34;\u003c/span\u003e,                                 \u003cspan style=\"color:#998;font-style:italic\"\u003e// City\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#d14\"\u003e\u0026#34;California\u0026#34;\u003c/span\u003e,                               \u003cspan style=\"color:#998;font-style:italic\"\u003e// State\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#d14\"\u003e\u0026#34;USA\u0026#34;\u003c/span\u003e                                       \u003cspan style=\"color:#998;font-style:italic\"\u003e// Country\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epageTracker._addItem(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#d14\"\u003e\u0026#34;1234\u0026#34;\u003c/span\u003e,                                     \u003cspan style=\"color:#998;font-style:italic\"\u003e// Order ID\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#d14\"\u003e\u0026#34;DD44\u0026#34;\u003c/span\u003e,                                     \u003cspan style=\"color:#998;font-style:italic\"\u003e// SKU\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#d14\"\u003e\u0026#34;T-Shirt\u0026#34;\u003c/span\u003e,                                  \u003cspan style=\"color:#998;font-style:italic\"\u003e// Product Name\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#d14\"\u003e\u0026#34;Green Medium\u0026#34;\u003c/span\u003e,                             \u003cspan style=\"color:#998;font-style:italic\"\u003e// Category\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#d14\"\u003e\u0026#34;11.99\u0026#34;\u003c/span\u003e,                                    \u003cspan style=\"color:#998;font-style:italic\"\u003e// Price\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#d14\"\u003e\u0026#34;1\u0026#34;\u003c/span\u003e                                         \u003cspan style=\"color:#998;font-style:italic\"\u003e// Quantity\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#998;font-style:italic\"\u003e\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epageTracker._trackTrans();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#000080\"\u003escript\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you\u0026rsquo;re already using Analytics, the first few lines will be familier to you.\nThey\u0026rsquo;re just the standard page tracking calls. The bits we\u0026rsquo;re interested in are\nthe \u003ccode\u003epageTacker._addTrans()\u003c/code\u003e, \u003ccode\u003epageTracker._addItem()\u003c/code\u003e and\n\u003ccode\u003epageTracker._trackTrans()\u003c/code\u003e methods.  You\u0026rsquo;ll need to fill in the relevant\ndetails, and more importantly repeat the \u003ccode\u003e_addItem\u003c/code\u003e call for each item within\nthe order. The method for doing this will obviously differ depending on your\nserver side choices, but for Rails I\u0026rsquo;ve got something similar to this.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-erb\" data-lang=\"erb\"\u003e\u0026lt;% @order.items.each do |item| %\u0026gt;\n\n  pageTracker._addItem(\n    \u0026lt;%= @order.order_id %\u0026gt;,                     // Order ID\n    \u0026lt;%= @order.sku %\u0026gt;,                          // SKU\n    \u0026lt;%= item.product.name %\u0026gt;,                   // Product Name\n    \u0026lt;%= item.product.category %\u0026gt;,               // Category\n    \u0026lt;%= item.unit_price %\u0026gt;,                     // Price\n    \u0026lt;%= item.quantity %\u0026gt;                        // Quantity\n  );\n\n\u0026lt;% end %\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eYou should now start to get E-Commerce stats showing up under the Ecommerce\nsection of Google Analytics.\u003c/p\u003e\n\u003ch2 id=\"references\"\u003eReferences\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"http://www.google.com/support/googleanalytics/bin/answer.py?hl=en\u0026amp;answer=55528\"\u003eGoogle Analytics Help\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://www.epikone.com/blog/2008/07/02/google-analytics-e-commerce-tracking-pt-4-tacking-lead-gen-forms/\"\u003eAnalytics Talk\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n",
      "summary": "\u003cp\u003eSo you\u0026rsquo;ve installed \u003ca href=\"http://www.google.com/analytics\"\u003eGoogle Analytics\u003c/a\u003e, the\nsuperb free web analytics software and it\u0026rsquo;s telling you all sorts of things\nabout your site, most of which you don\u0026rsquo;t even understand. You\u0026rsquo;re learning what\na \u003ca href=\"http://en.wikipedia.org/wiki/Bounce_Rate\"\u003eBounce Rate\u003c/a\u003e is and realising how\npowerful this thing really is.\u003c/p\u003e\n\u003cp\u003eWell, it gets even better. I\u0026rsquo;ve recently discovered its\u0026rsquo; Ecommerce tracking\nfeature.  It allows you to capture Ecommerce related statistics alongside your\nnormal web related statistics. Once you have it correctly setup for your site\nyou can see how much money you\u0026rsquo;ve taken in sales, how many transactions have\ntaken place, how many products have been purchased, and your Ecommerce\nconversion rate. Very nice.\u003c/p\u003e",
      "date_published": "2008-08-07T00:00:00+00:00",
      "date_modified": "2008-08-07T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/git-patching-flexibility/",
      "url": "https://elver.me/blog/git-patching-flexibility/","title": "Git Patching Flexibility",
      "content_html": "\u003cp\u003eI don\u0026rsquo;t know about you, but when I\u0026rsquo;m coding I\u0026rsquo;ll often need to make a change\nthat\u0026rsquo;s unrelated to the current feature or bug I am working on. I\u0026rsquo;ll notice a\nspelling mistake, a typo, or some other small code change. You don\u0026rsquo;t want to\ncommit the unrelated change as part of your overall commit because, well, it\u0026rsquo;s\nunrelated. It should be its own commit with its own commit message.  This will\nmake it much easier to read the commit log and cuts down on confusion.  People\nwon\u0026rsquo;t have to ask \u0026ldquo;Why did they change that? It has nothing to do with the\nchange\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eWhen I used Subversion, you had to remove the unrelated change, commit, then\nadd it back in and commit again. Maybe there was a nicer way, but I didn\u0026rsquo;t\nknow it. Git is much nicer.\u003c/p\u003e\n\u003cp\u003eWhen you stage a file ready for committing, Git allows you to only stage a\ncertain part or parts of the file, which it calls hunks.\u003c/p\u003e\n\u003cp\u003eSo, run:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit add --patch \u0026lt;filename\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eGit will show you a diff of the first difference that it wants to stage and\nwill ask if you\u0026rsquo;d like to stage the current hunk, like this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eStage this hunk \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003ey/n/a/d/s/?\u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e?\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe options in this example are:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ey - stage this hunk\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003en - \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e not stage this hunk\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ea - stage this and all the remaining hunks in the file\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ed - \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e not stage this hunk nor any of the remaining hunks in the file\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003es - split the current hunk into smaller hunks\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eChoose what you\u0026rsquo;d like to do with the current hunk. Git will continue to go\nthrough the rest of the hunks in the file, asking you what to do with each one.\u003c/p\u003e\n\u003cp\u003eWhen you\u0026rsquo;ve decided which hunks should be staged, commit as normal and only\nthose changes will form part of the commit. You can then add the rest of\nyour changes and commit again.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s the flexibility that\u0026rsquo;s great. It works with you, not against you.\u003c/p\u003e\n\u003cp\u003eFor a much more in depth look at the capabilities of \u003ccode\u003egit add --patch\u003c/code\u003e, see\nthe following article.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e[The Thing About Git](\u003ca href=\"http://tomayko.com/writings/the-thing-about-git\"\u003ehttp://tomayko.com/writings/the-thing-about-git\u003c/a\u003e by\n\u0026ldquo;Ryan Tomayko\u0026rdquo;:http://tomayko.com/)\u003c/li\u003e\n\u003c/ul\u003e\n",
      "summary": "\u003cp\u003eI don\u0026rsquo;t know about you, but when I\u0026rsquo;m coding I\u0026rsquo;ll often need to make a change\nthat\u0026rsquo;s unrelated to the current feature or bug I am working on. I\u0026rsquo;ll notice a\nspelling mistake, a typo, or some other small code change. You don\u0026rsquo;t want to\ncommit the unrelated change as part of your overall commit because, well, it\u0026rsquo;s\nunrelated. It should be its own commit with its own commit message.  This will\nmake it much easier to read the commit log and cuts down on confusion.  People\nwon\u0026rsquo;t have to ask \u0026ldquo;Why did they change that? It has nothing to do with the\nchange\u0026rdquo;\u003c/p\u003e",
      "date_published": "2008-08-04T00:00:00+00:00",
      "date_modified": "2008-08-04T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/how-to-generate-request-an-ssl-certificate/",
      "url": "https://elver.me/blog/how-to-generate-request-an-ssl-certificate/","title": "How to generate / request an SSL certificate",
      "content_html": "\u003cp\u003eGenerating an SSL certificate can be confusing if you\u0026rsquo;ve never done it\nbefore. Actually, it\u0026rsquo;s confusing if you have done it before. Hopefully this\nshould remind me how to do it in the future!\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePLEASE NOTE:\u003c/strong\u003e I am no expert on SSL, but this does the job for me.\u003c/p\u003e\n\u003cp\u003eI recently had to do this after the \u003ca href=\"http://www.ubuntu.com/usn/usn-612-1\"\u003eDebian security\nvulnerability\u003c/a\u003e affected one of my SSL\ncertificates.\u003c/p\u003e\n\u003cp\u003eI currently get my SSL cerficiates through \u003ca href=\"http://www.namecheap.com\"\u003eNameCheap\u003c/a\u003e\nfor $10.  They are re-sellers of RapidSSL and GeoTrust certificates. Mine is a\nRapidSSL.\u003c/p\u003e\n\u003ch2 id=\"generate-a-private-key-and-certificate-signing-request\"\u003eGenerate a private key and Certificate Signing Request\u003c/h2\u003e\n\u003cp\u003eWe need to generate an OpenSSL keypair and a \u003ca href=\"http://en.wikipedia.org/wiki/Certificate_signing_request\"\u003eCertificate Signing Request\n(CSR)\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThe keypair consists of two cryptographic keys. A public and private. The\npublic key is included with the CSR along with other applicant information such\nas name, company, etc. The private key is used to sign the CSR request.\u003c/p\u003e\n\u003cp\u003eA CSR is what you send to your chosen Certificate Authority (CA) to request\nthat they supply you with an SSL certificate. It includes your identifying\ninformation and the public key for your server/site.\u003c/p\u003e\n\u003cp\u003eGenerate it like this.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eopenssl req -new -newkey rsa:1024 -nodes -keyout example.key -out example.csr\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou\u0026rsquo;ll be prompted to enter information such as Common Name, Organisation,\nCountry etc.\u003c/p\u003e\n\u003cp\u003eIt should be fairly straight forward, but your CA will let you know if you\u0026rsquo;ve\ndone it wrong, I\u0026rsquo;m sure.\u003c/p\u003e\n\u003cp\u003eThis will create \u003ccode\u003eexample.key\u003c/code\u003e (the private key) and \u003ccode\u003eexample.csr\u003c/code\u003e (the CSR).\u003c/p\u003e\n\u003ch2 id=\"requesting-your-certificate\"\u003eRequesting your certificate\u003c/h2\u003e\n\u003cp\u003eThis part should be easy. Normally your CA will have a form on their website\nwhich allows you to paste in your CSR. This obviously varies from company to\ncompany. Once you have given them your CSR, they will first ask you for money,\nand then generate your CRT file. Yay!\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m only scratching the surface of SSL here. OpenSSL has a massive amount of\noptions. There is much to learn.\u003c/p\u003e\n\u003ch2 id=\"references\"\u003eReferences\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://help.ubuntu.com/community/OpenSSL\"\u003eMore information on OpenSSL commands\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n",
      "summary": "\u003cp\u003eGenerating an SSL certificate can be confusing if you\u0026rsquo;ve never done it\nbefore. Actually, it\u0026rsquo;s confusing if you have done it before. Hopefully this\nshould remind me how to do it in the future!\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePLEASE NOTE:\u003c/strong\u003e I am no expert on SSL, but this does the job for me.\u003c/p\u003e\n\u003cp\u003eI recently had to do this after the \u003ca href=\"http://www.ubuntu.com/usn/usn-612-1\"\u003eDebian security\nvulnerability\u003c/a\u003e affected one of my SSL\ncertificates.\u003c/p\u003e\n\u003cp\u003eI currently get my SSL cerficiates through \u003ca href=\"http://www.namecheap.com\"\u003eNameCheap\u003c/a\u003e\nfor $10.  They are re-sellers of RapidSSL and GeoTrust certificates. Mine is a\nRapidSSL.\u003c/p\u003e",
      "date_published": "2008-07-24T00:00:00+00:00",
      "date_modified": "2008-07-24T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/rails-deployment-with-git-vlad-and-ssh-agent-forwarding/",
      "url": "https://elver.me/blog/rails-deployment-with-git-vlad-and-ssh-agent-forwarding/","title": "Rails Deployment with Git, Vlad and SSH Agent Forwarding",
      "content_html": "\u003cp\u003eI\u0026rsquo;m switching my code repositories over to Git from Subversion. For the most\npart it\u0026rsquo;s going well. I\u0026rsquo;m having some issues with line endings, but that\u0026rsquo;s for\nanother post. I\u0026rsquo;m still getting to grips with day to day usage, but can already\nsee that it\u0026rsquo;s going to be a great improvement on Subversion.\u003c/p\u003e\n\u003cp\u003eThis is how I\u0026rsquo;ve setup Vlad, Git, and SSH to work together.\u003c/p\u003e\n\u003ch2 id=\"vlad-configuration\"\u003eVlad Configuration\u003c/h2\u003e\n\u003cp\u003eI use \u003ca href=\"http://rubyhitsquad.com/Vlad_the_Deployer.html\"\u003eVlad\u003c/a\u003e to deploy my\nprojects to the staging/live server, so I needed to re-configure Vlad to\nsupport Git. This was very simple to do. In my \u003ccode\u003eRakefile\u003c/code\u003e, I included the Git\nclass instead of the Subversion class. This removes the Subversion specific\ncommands and includes the Git specific commands instead.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-diff\" data-lang=\"diff\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#fdd\"\u003e-require \u0026#39;vlad/subversion\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#fdd\"\u003e\u003c/span\u003e\u003cspan style=\"color:#000;background-color:#dfd\"\u003e+require \u0026#39;vlad/git\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI also changed the repository address in my \u003ccode\u003econfig/deploy.rb\u003c/code\u003e file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-diff\" data-lang=\"diff\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#fdd\"\u003e-set :repository, \u0026#39;https://sub.version.server/repo\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;background-color:#fdd\"\u003e\u003c/span\u003e\u003cspan style=\"color:#000;background-color:#dfd\"\u003e+set :repository, \u0026#39;git@github.com:username/repo.git\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThese are the only changes I needed to make.\u003c/p\u003e\n\u003ch2 id=\"ssh-and-git-setup\"\u003eSSH and Git setup\u003c/h2\u003e\n\u003cp\u003eGit uses SSH keys to control access to repositories. As I\u0026rsquo;m using GitHub as my\ndeployment Git repository, I needed to identify myself with them. To do this, I\nsupplied them with my SSH public key. \u003ca href=\"http://github.com/guides/providing-your-ssh-key#linux\"\u003eGitHub have good instructions on how to\ndo this\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eAs the code is cloned from GitHub directly to the deployment server (not my\nmachine), my private SSH key would also need to be installed on the deployment\nserver.\u003c/p\u003e\n\u003cp\u003eVlad deployment now works! This will checkout the latest HEAD and deploy it to\nthe server. \u003ca href=\"http://scie.nti.st/2007/9/25/vlad-the-deployer-and-git\"\u003eYou can set the specific revision that you\nwant\u003c/a\u003e in your\n\u003ccode\u003econfig/deploy.rb\u003c/code\u003e as well if required. I\u0026rsquo;m not doing that at the moment.\u003c/p\u003e\n\u003ch2 id=\"so-whats-the-problem\"\u003eSo, what\u0026rsquo;s the problem?\u003c/h2\u003e\n\u003cp\u003eThe problem is that you need to install your private SSH key on the deployment\nserver. I didn\u0026rsquo;t want to do this in case the server was ever compromised.\nIf it was compromised, the attacker could also theoretically get into\nother systems using that key if it didn\u0026rsquo;t have a passphrase. This would be bad.\nMy key does have a passphrase, but why install it when you don\u0026rsquo;t really need to?\u003c/p\u003e\n\u003ch2 id=\"making-it-more-secure\"\u003eMaking it more secure\u003c/h2\u003e\n\u003cp\u003eFortunately, there\u0026rsquo;s a rather nifty solution to this problem — SSH agent\nforwarding.  In order to do so, you must first be using SSH Agent. SSH Agent\nallows you to authenticate yourself once per session, so you don\u0026rsquo;t have to\nenter your password every time you connect to the server. If you\u0026rsquo;re already\nusing SSH keys and have SSH Agent running on your system (which is the default\nin Ubuntu), you just need to do:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003essh-add ~/.ssh/\u0026lt;keyname\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou\u0026rsquo;ll be prompted for the passphrase to the key. Once you enter it, you should\nbe able to connect to any servers using that key without entering it again.\nThis is very handy anyway regardless of whether you\u0026rsquo;re going to be using\nforwarding or not.\u003c/p\u003e\n\u003cp\u003eSee \u003ca href=\"http://en.wikipedia.org/wiki/Ssh-agent#Setting_Up_Ssh_Agent\"\u003ethis wikipedia\npage\u003c/a\u003e for more\ninformation.\u003c/p\u003e\n\u003ch2 id=\"how-to-setup-the-forwarding\"\u003eHow to setup the forwarding\u003c/h2\u003e\n\u003cp\u003eThis is where the forwarding comes in. SSH can forward requests for\nauthentication back to the original SSH Agent process running on your machine.\nSo, you can connect to your deployment server and then connect to another\nserver without re-entering your passphrase or having to install any keys on the\ndeployment machine itself.  Much coolness. This negates the need to install any\nother keys on the deployment server itself.\u003c/p\u003e\n\u003cp\u003eThere is a very well explained article called \u003ca href=\"http://www.unixwiz.net/techtips/ssh-agent-forwarding.html\"\u003eAn Illustrated Guide to SSH\nAgent Forwarding\u003c/a\u003e\nover at \u003ca href=\"http://www.unixwiz.net\"\u003eUnixwiz.net\u003c/a\u003e that goes into a lot of detail\nabout how this all works. It\u0026rsquo;s well worth a read.\u003c/p\u003e\n\u003cp\u003eThis is how to set it up.\u003c/p\u003e\n\u003cp\u003eEdit your \u003ccode\u003e~/.ssh/config\u003c/code\u003e file and add something like this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eHost \u0026lt;name\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  HostName \u0026lt;ip or host\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  User \u0026lt;username\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  IdentityFile ~/.ssh/\u0026lt;filename\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  ForwardAgent yes\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis is all you need. If you\u0026rsquo;ve got SSH Agent running, you\u0026rsquo;re sorted.\u003c/p\u003e\n\u003cp\u003eIncidentally, you can add these sections for as many hosts as required. It saves you\nhaving to type out lots of command line switches all the time. Just leave out the\n\u003ccode\u003eForwardAgent yes\u003c/code\u003e line.\u003c/p\u003e\n\u003ch2 id=\"references\"\u003eReferences\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"http://github.com/guides/providing-your-ssh-key#linux\"\u003eGitHub - Providing your SSH key\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://scie.nti.st/2007/9/25/vlad-the-deployer-and-git\"\u003eVlad the Deployer and Git\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://en.wikipedia.org/wiki/Ssh-agent#Setting_Up_Ssh_Agent\"\u003eSSH Agent on Wikipedia\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://www.unixwiz.net/techtips/ssh-agent-forwarding.html\"\u003eAn Illustrated Guide to SSH Agent Forwarding\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://dysinger.net/2008/04/30/deploying-with-capistrano-git-and-ssh-agent/\"\u003eDeploying with Capistrano, Git and SSH-Agent\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n",
      "summary": "\u003cp\u003eI\u0026rsquo;m switching my code repositories over to Git from Subversion. For the most\npart it\u0026rsquo;s going well. I\u0026rsquo;m having some issues with line endings, but that\u0026rsquo;s for\nanother post. I\u0026rsquo;m still getting to grips with day to day usage, but can already\nsee that it\u0026rsquo;s going to be a great improvement on Subversion.\u003c/p\u003e\n\u003cp\u003eThis is how I\u0026rsquo;ve setup Vlad, Git, and SSH to work together.\u003c/p\u003e\n\u003ch2 id=\"vlad-configuration\"\u003eVlad Configuration\u003c/h2\u003e\n\u003cp\u003eI use \u003ca href=\"http://rubyhitsquad.com/Vlad_the_Deployer.html\"\u003eVlad\u003c/a\u003e to deploy my\nprojects to the staging/live server, so I needed to re-configure Vlad to\nsupport Git. This was very simple to do. In my \u003ccode\u003eRakefile\u003c/code\u003e, I included the Git\nclass instead of the Subversion class. This removes the Subversion specific\ncommands and includes the Git specific commands instead.\u003c/p\u003e",
      "date_published": "2008-07-10T00:00:00+00:00",
      "date_modified": "2008-07-10T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/rake-dependencies/",
      "url": "https://elver.me/blog/rake-dependencies/","title": "Rake dependencies",
      "content_html": "\u003cp\u003eRake tasks are great for all sorts of small tasks. They give you a bit of\nstructure to what would normally be a shell or ruby script.  However, the thing\nI like about rake tasks are dependencies.\u003c/p\u003e\n\u003ch2 id=\"a-simple-example\"\u003eA simple example\u003c/h2\u003e\n\u003cp\u003eYou see, you can make one task rely on another. Take this.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edesc \u003cspan style=\"color:#d14\"\u003e\u0026#34;Make coffee\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etask \u003cspan style=\"color:#990073\"\u003e:coffee\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#990073\"\u003e:get_mug\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#0086b3\"\u003eputs\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;Making a coffee\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etask \u003cspan style=\"color:#990073\"\u003e:get_mug\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#0086b3\"\u003eputs\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;Getting a mug\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erake get_mug\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eGetting\u003c/span\u003e a mug\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erake coffee\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eGetting\u003c/span\u003e a mug\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eMaking\u003c/span\u003e a coffee\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eEasy to understand isn\u0026rsquo;t it? \u003ccode\u003ecoffee\u003c/code\u003e depends on \u003ccode\u003eget_mug\u003c/code\u003e, so \u003ccode\u003eget_mug\u003c/code\u003e will be\ncalled before the main \u003ccode\u003ecoffee\u003c/code\u003e task every time \u003ccode\u003erake coffee\u003c/code\u003e is run.\u003c/p\u003e\n\u003ch2 id=\"a-better-example\"\u003eA better example\u003c/h2\u003e\n\u003cp\u003eYou can also redefine task dependencies without altering the original task.\nFor example, on this blog I have a task which creates index pages for the\ncategory pages. I want this to run everytime the \u003ccode\u003ebuild\u003c/code\u003e task is run. I could\nmodify the original \u003ccode\u003ebuild\u003c/code\u003e task, but a much nicer and cleaner way is to\nredefine the task and add the dependant task like so.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etask \u003cspan style=\"color:#990073\"\u003e:build\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#39;blog:categories:create_indexes\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will keep any existing dependencies that the build task originally had and\nalso add my new one. Ain\u0026rsquo;t it pretty?\u003c/p\u003e\n\u003ch2 id=\"references\"\u003eReferences\u003c/h2\u003e\n\u003cp\u003eThanks to the guys at \u003ca href=\"http://www.railsenvy.com\"\u003eRails Envy\u003c/a\u003e for their tutorial\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"http://www.railsenvy.com/2007/6/11/ruby-on-rails-rake-tutorial\"\u003eRuby on Rails Rake Tutorial [aka. How rake turned me into an\nalcoholic]\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n",
      "summary": "\u003cp\u003eRake tasks are great for all sorts of small tasks. They give you a bit of\nstructure to what would normally be a shell or ruby script.  However, the thing\nI like about rake tasks are dependencies.\u003c/p\u003e\n\u003ch2 id=\"a-simple-example\"\u003eA simple example\u003c/h2\u003e\n\u003cp\u003eYou see, you can make one task rely on another. Take this.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edesc \u003cspan style=\"color:#d14\"\u003e\u0026#34;Make coffee\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etask \u003cspan style=\"color:#990073\"\u003e:coffee\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#990073\"\u003e:get_mug\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#0086b3\"\u003eputs\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;Making a coffee\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etask \u003cspan style=\"color:#990073\"\u003e:get_mug\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#0086b3\"\u003eputs\u003c/span\u003e \u003cspan style=\"color:#d14\"\u003e\u0026#34;Getting a mug\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erake get_mug\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eGetting\u003c/span\u003e a mug\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erake coffee\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eGetting\u003c/span\u003e a mug\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#008080\"\u003eMaking\u003c/span\u003e a coffee\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eEasy to understand isn\u0026rsquo;t it? \u003ccode\u003ecoffee\u003c/code\u003e depends on \u003ccode\u003eget_mug\u003c/code\u003e, so \u003ccode\u003eget_mug\u003c/code\u003e will be\ncalled before the main \u003ccode\u003ecoffee\u003c/code\u003e task every time \u003ccode\u003erake coffee\u003c/code\u003e is run.\u003c/p\u003e",
      "date_published": "2008-06-30T00:00:00+00:00",
      "date_modified": "2008-06-30T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/git-completion-yay/",
      "url": "https://elver.me/blog/git-completion-yay/","title": "Git bash completion = Yay!",
      "content_html": "\u003cp\u003eIf you\u0026rsquo;ve heard of, or are already using bash completion, you\u0026rsquo;ll know how great\nit is.  Here\u0026rsquo;s how to use it with \u003ca\nhref=\"http://en.wikipedia.org/wiki/Git_(software)\"\u003eGit\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"how-to-get-the-sweetness\"\u003eHow to get the sweetness?\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;m using Ubuntu (Hardy), but setup instructions should be similar for others.\nFor Ubuntu, uncomment this section in \u003ccode\u003e/etc/bash.bashrc\u003c/code\u003e and you\u0026rsquo;ve enabled bash\ncompletion. You\u0026rsquo;ll need to open a new terminal for it to take affect.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e -f /etc/bash_completion \u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#000;font-weight:bold\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e . /etc/bash_completion\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"how-to-enable-git-completion\"\u003eHow to enable Git completion\u003c/h2\u003e\n\u003cp\u003eYou\u0026rsquo;ll need the bash completion file for Git. This file details how the Git\ncommands will be completed. The file comes as part of the Git distribution.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eDownload the lastest git release from\n\u003ca href=\"http://www.kernel.org/pub/software/scm/git/\"\u003ehttp://www.kernel.org/pub/software/scm/git/\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eFind the \u003ccode\u003egit-completion.bash\u003c/code\u003e file in the \u003ccode\u003econtrib/\u003c/code\u003e directory and copy to\nthe \u003ccode\u003e/etc/bash_completion.d/\u003c/code\u003e directory\u003c/li\u003e\n\u003cli\u003eStart a new login shell (logout/login or start a new Terminal tab)\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"try-it-out\"\u003eTry it out\u003c/h2\u003e\n\u003cp\u003eNow, typing \u003ccode\u003egit \u0026lt;tab\u0026gt;\u0026lt;tab\u0026gt;\u003c/code\u003e should list all of the various git commands (of\nwhich there are many).  However, the really cool part is that it will also list\nyour branches for you. Typing \u003ccode\u003egit checkout \u0026lt;tab\u0026gt;\u0026lt;tab\u0026gt;\u003c/code\u003e within your Git repo\nwill list all of the branches you have.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejord@jordan /home/jord/webby\u003cspan style=\"color:#000;font-weight:bold\"\u003e(\u003c/span\u003emaster\u003cspan style=\"color:#000;font-weight:bold\"\u003e)\u003c/span\u003e $ git checkout \u0026lt;tab\u0026gt;\u0026lt;tab\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eHEAD    master\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRSI, be gone!\u003c/p\u003e\n\u003ch2 id=\"thanks\"\u003eThanks\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"http://blog.bitfluent.com/post/27983389/git-utilities-you-cant-live-without\"\u003eBitfluent\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n",
      "summary": "\u003cp\u003eIf you\u0026rsquo;ve heard of, or are already using bash completion, you\u0026rsquo;ll know how great\nit is.  Here\u0026rsquo;s how to use it with \u003ca\nhref=\"http://en.wikipedia.org/wiki/Git_(software)\"\u003eGit\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"how-to-get-the-sweetness\"\u003eHow to get the sweetness?\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;m using Ubuntu (Hardy), but setup instructions should be similar for others.\nFor Ubuntu, uncomment this section in \u003ccode\u003e/etc/bash.bashrc\u003c/code\u003e and you\u0026rsquo;ve enabled bash\ncompletion. You\u0026rsquo;ll need to open a new terminal for it to take affect.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#000;font-weight:bold\"\u003e[\u003c/span\u003e -f /etc/bash_completion \u003cspan style=\"color:#000;font-weight:bold\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#000;font-weight:bold\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e . /etc/bash_completion\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#000;font-weight:bold\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"how-to-enable-git-completion\"\u003eHow to enable Git completion\u003c/h2\u003e\n\u003cp\u003eYou\u0026rsquo;ll need the bash completion file for Git. This file details how the Git\ncommands will be completed. The file comes as part of the Git distribution.\u003c/p\u003e",
      "date_published": "2008-06-18T00:00:00+00:00",
      "date_modified": "2008-06-18T00:00:00+00:00"
    }
    , {
      "id": "https://elver.me/blog/hello-and-welcome/",
      "url": "https://elver.me/blog/hello-and-welcome/","title": "w00t! First post evar!",
      "content_html": "\u003cp\u003eHello. The proper site is now live. It\u0026rsquo;s been way too long coming, but now it\u0026rsquo;s\nhere. I built it using \u003ca href=\"http://webby.rubyforge.net/\"\u003eWebby\u003c/a\u003e. Hopefully there\u0026rsquo;ll\nbe some posts explaining how that was done soon.\u003c/p\u003e\n\u003cp\u003eP.S. I know some people won\u0026rsquo;t like the colours, but I do.\u003c/p\u003e\n",
      "summary": "\u003cp\u003eHello. The proper site is now live. It\u0026rsquo;s been way too long coming, but now it\u0026rsquo;s\nhere. I built it using \u003ca href=\"http://webby.rubyforge.net/\"\u003eWebby\u003c/a\u003e. Hopefully there\u0026rsquo;ll\nbe some posts explaining how that was done soon.\u003c/p\u003e\n\u003cp\u003eP.S. I know some people won\u0026rsquo;t like the colours, but I do.\u003c/p\u003e",
      "date_published": "2008-06-17T00:00:00+00:00",
      "date_modified": "2008-06-17T00:00:00+00:00"
    }
    
  ]
}
